1- import { getDb , accounts , orders } from '~/server/utils/db'
2- import { eq , desc , sql } from 'drizzle-orm'
1+ import { getDb , accounts , accountSnapshots } from '~/server/utils/db'
2+ import { eq , desc , asc } from 'drizzle-orm'
33
44export default defineEventHandler ( async ( event ) => {
55 try {
@@ -12,90 +12,82 @@ export default defineEventHandler(async (event) => {
1212 return [ ]
1313 }
1414
15- // For each account, try to reconstruct some history from orders
16- // If we don't have enough history, generate some points based on current state
15+ // Use account snapshots for historical data, fallback to current account_value
1716 const accountValues : Array < {
1817 timestamp : Date
1918 models : Record < string , number >
2019 } > = [ ]
2120
22- // Get the earliest and latest order timestamps across all accounts
23- const earliestOrder = await db
24- . select ( {
25- created_at : orders . created_at ,
26- } )
27- . from ( orders )
28- . orderBy ( orders . created_at )
29- . limit ( 1 )
21+ // Get all snapshots ordered by timestamp
22+ const allSnapshots = await db
23+ . select ( )
24+ . from ( accountSnapshots )
25+ . orderBy ( asc ( accountSnapshots . snapshot_at ) )
3026
31- const latestOrder = await db
32- . select ( {
33- created_at : orders . created_at ,
34- } )
35- . from ( orders )
36- . orderBy ( desc ( orders . created_at ) )
37- . limit ( 1 )
38-
39- const startTime = earliestOrder . length > 0
40- ? new Date ( earliestOrder [ 0 ] . created_at )
41- : new Date ( Date . now ( ) - 30 * 24 * 60 * 60 * 1000 ) // 30 days ago
42- const endTime = latestOrder . length > 0
43- ? new Date ( latestOrder [ 0 ] . created_at )
44- : new Date ( )
45-
46- // Generate timestamps (daily points over the time range, max 200 points)
47- const timeRange = endTime . getTime ( ) - startTime . getTime ( )
48- const numPoints = Math . min ( 200 , Math . max ( 30 , Math . floor ( timeRange / ( 24 * 60 * 60 * 1000 ) ) ) )
49- const interval = timeRange / numPoints
27+ // Group snapshots by account_id
28+ const snapshotsByAccount = new Map < string , typeof allSnapshots > ( )
29+ for ( const snapshot of allSnapshots ) {
30+ if ( ! snapshotsByAccount . has ( snapshot . account_id ) ) {
31+ snapshotsByAccount . set ( snapshot . account_id , [ ] )
32+ }
33+ snapshotsByAccount . get ( snapshot . account_id ) ! . push ( snapshot )
34+ }
5035
51- // For each account, calculate values over time
52- // This is a simplified approach - ideally we'd have account balance snapshots
53- for ( const account of allAccounts ) {
54- const initialBalance = parseFloat ( account . initial_balance )
55- const currentBalance = parseFloat ( account . current_balance )
56- const totalPnl = parseFloat ( account . total_pnl )
57-
58- // Get orders for this account to reconstruct some history
59- const accountOrders = await db
60- . select ( )
61- . from ( orders )
62- . where ( eq ( orders . account_id , account . id ) )
63- . orderBy ( orders . created_at )
36+ // If we have snapshots, use them
37+ if ( allSnapshots . length > 0 ) {
38+ // Get unique timestamps from all snapshots
39+ const uniqueTimestamps = Array . from ( new Set ( allSnapshots . map ( s => s . snapshot_at . getTime ( ) ) ) )
40+ . sort ( )
41+ . map ( ts => new Date ( ts ) )
6442
65- // Calculate balance progression
66- // This is simplified - we're estimating based on order timestamps and PnL
67- let runningBalance = initialBalance
68- let orderIndex = 0
69-
70- for ( let i = 0 ; i < numPoints ; i ++ ) {
71- const timestamp = new Date ( startTime . getTime ( ) + i * interval )
72-
73- // Check if we have a value for this timestamp
74- if ( ! accountValues [ i ] ) {
75- accountValues [ i ] = {
76- timestamp,
77- models : { } ,
78- }
43+ // Build account values array
44+ for ( const timestamp of uniqueTimestamps ) {
45+ const entry : { timestamp : Date ; models : Record < string , number > } = {
46+ timestamp,
47+ models : { } ,
7948 }
8049
81- // Process orders up to this timestamp
82- while (
83- orderIndex < accountOrders . length &&
84- new Date ( accountOrders [ orderIndex ] . created_at ) <= timestamp
85- ) {
86- const order = accountOrders [ orderIndex ]
87- if ( order . realized_pnl ) {
88- runningBalance += parseFloat ( order . realized_pnl )
50+ // For each account, find the closest snapshot at or before this timestamp
51+ for ( const account of allAccounts ) {
52+ const accountSnapshots = snapshotsByAccount . get ( account . id ) || [ ]
53+ // Find the latest snapshot at or before this timestamp
54+ let closestSnapshot = accountSnapshots
55+ . filter ( s => new Date ( s . snapshot_at ) . getTime ( ) <= timestamp . getTime ( ) )
56+ . sort ( ( a , b ) => new Date ( b . snapshot_at ) . getTime ( ) - new Date ( a . snapshot_at ) . getTime ( ) ) [ 0 ]
57+
58+ if ( closestSnapshot && closestSnapshot . account_value ) {
59+ entry . models [ account . id ] = parseFloat ( closestSnapshot . account_value )
60+ } else if ( account . account_value ) {
61+ // Fallback to current account_value if no snapshot
62+ entry . models [ account . id ] = parseFloat ( account . account_value )
8963 }
90- orderIndex ++
9164 }
9265
93- // Estimate balance at this point
94- // Interpolate between initial and current balance based on progress
95- const progress = i / ( numPoints - 1 )
96- const estimatedBalance = initialBalance + ( totalPnl * progress )
97-
98- accountValues [ i ] . models [ account . id ] = estimatedBalance
66+ if ( Object . keys ( entry . models ) . length > 0 ) {
67+ accountValues . push ( entry )
68+ }
69+ }
70+ }
71+
72+ // If no snapshots, add current account values as a single point
73+ if ( accountValues . length === 0 ) {
74+ const currentEntry : { timestamp : Date ; models : Record < string , number > } = {
75+ timestamp : new Date ( ) ,
76+ models : { } ,
77+ }
78+
79+ for ( const account of allAccounts ) {
80+ // Use stored account_value, or calculate from current_balance if not stored
81+ if ( account . account_value ) {
82+ currentEntry . models [ account . id ] = parseFloat ( account . account_value )
83+ } else {
84+ // Fallback: use current_balance (shouldn't happen if metrics are updated)
85+ currentEntry . models [ account . id ] = parseFloat ( account . current_balance )
86+ }
87+ }
88+
89+ if ( Object . keys ( currentEntry . models ) . length > 0 ) {
90+ accountValues . push ( currentEntry )
9991 }
10092 }
10193
0 commit comments