|
1 | | -import { getDb, accounts, accountSnapshots } from '~/server/utils/db' |
| 1 | +import { getDb, accounts, accountSnapshots, orders, positions } from '~/server/utils/db' |
2 | 2 | import { eq, desc, asc } from 'drizzle-orm' |
3 | 3 |
|
4 | 4 | export default defineEventHandler(async (event) => { |
@@ -33,61 +33,80 @@ export default defineEventHandler(async (event) => { |
33 | 33 | snapshotsByAccount.get(snapshot.account_id)!.push(snapshot) |
34 | 34 | } |
35 | 35 |
|
36 | | - // If we have snapshots, use them |
| 36 | + // If we have snapshots, use them for accurate historical data |
37 | 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)) |
| 38 | + // Group snapshots by timestamp to ensure we get all accounts at each point |
| 39 | + const snapshotsByTimestamp = new Map<number, typeof allSnapshots>() |
42 | 40 |
|
43 | | - // Build account values array |
44 | | - for (const timestamp of uniqueTimestamps) { |
| 41 | + for (const snapshot of allSnapshots) { |
| 42 | + const timestamp = new Date(snapshot.snapshot_at).getTime() |
| 43 | + if (!snapshotsByTimestamp.has(timestamp)) { |
| 44 | + snapshotsByTimestamp.set(timestamp, []) |
| 45 | + } |
| 46 | + snapshotsByTimestamp.get(timestamp)!.push(snapshot) |
| 47 | + } |
| 48 | + |
| 49 | + // Get unique timestamps and sort them |
| 50 | + const uniqueTimestamps = Array.from(snapshotsByTimestamp.keys()).sort() |
| 51 | + |
| 52 | + // Build account values array from snapshots |
| 53 | + for (const timestampMs of uniqueTimestamps) { |
| 54 | + const timestamp = new Date(timestampMs) |
| 55 | + const snapshotsAtTime = snapshotsByTimestamp.get(timestampMs) || [] |
| 56 | + |
45 | 57 | const entry: { timestamp: Date; models: Record<string, number> } = { |
46 | 58 | timestamp, |
47 | 59 | models: {}, |
48 | 60 | } |
49 | 61 |
|
50 | | - // For each account, find the closest snapshot at or before this timestamp |
| 62 | + // For each account, find its snapshot at this exact timestamp |
51 | 63 | 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] |
| 64 | + const snapshot = snapshotsAtTime.find(s => s.account_id === account.id) |
57 | 65 |
|
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) |
| 66 | + if (snapshot && snapshot.account_value) { |
| 67 | + entry.models[account.id] = parseFloat(snapshot.account_value) |
| 68 | + } else { |
| 69 | + // Find the latest snapshot before this timestamp |
| 70 | + const accountSnapshots = snapshotsByAccount.get(account.id) || [] |
| 71 | + const closestSnapshot = accountSnapshots |
| 72 | + .filter(s => new Date(s.snapshot_at).getTime() <= timestampMs) |
| 73 | + .sort((a, b) => new Date(b.snapshot_at).getTime() - new Date(a.snapshot_at).getTime())[0] |
| 74 | + |
| 75 | + if (closestSnapshot && closestSnapshot.account_value) { |
| 76 | + entry.models[account.id] = parseFloat(closestSnapshot.account_value) |
| 77 | + } else if (account.account_value) { |
| 78 | + // Fallback to current account_value if no snapshot found |
| 79 | + entry.models[account.id] = parseFloat(account.account_value) |
| 80 | + } |
63 | 81 | } |
64 | 82 | } |
65 | 83 |
|
66 | 84 | if (Object.keys(entry.models).length > 0) { |
67 | 85 | accountValues.push(entry) |
68 | 86 | } |
69 | 87 | } |
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 | 88 |
|
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) |
| 89 | + // Add current value as the latest point if not already included |
| 90 | + const latestTimestamp = uniqueTimestamps.length > 0 ? uniqueTimestamps[uniqueTimestamps.length - 1] : null |
| 91 | + const currentTime = Date.now() |
| 92 | + if (!latestTimestamp || (currentTime - latestTimestamp) > 60000) { // More than 1 minute difference |
| 93 | + const currentEntry: { timestamp: Date; models: Record<string, number> } = { |
| 94 | + timestamp: new Date(), |
| 95 | + models: {}, |
| 96 | + } |
| 97 | + |
| 98 | + for (const account of allAccounts) { |
| 99 | + if (account.account_value) { |
| 100 | + currentEntry.models[account.id] = parseFloat(account.account_value) |
| 101 | + } else { |
| 102 | + // Fallback: use current_balance |
| 103 | + currentEntry.models[account.id] = parseFloat(account.current_balance) |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + if (Object.keys(currentEntry.models).length > 0) { |
| 108 | + accountValues.push(currentEntry) |
86 | 109 | } |
87 | | - } |
88 | | - |
89 | | - if (Object.keys(currentEntry.models).length > 0) { |
90 | | - accountValues.push(currentEntry) |
91 | 110 | } |
92 | 111 | } |
93 | 112 |
|
|
0 commit comments