Skip to content

Commit 1c58064

Browse files
feat : Updated the Client to sync with Account Snapshot
1 parent e2d200d commit 1c58064

File tree

7 files changed

+102
-83
lines changed

7 files changed

+102
-83
lines changed

client/components/MainChart.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ const chartOptions = computed(() => {
169169
// min: yAxisMin,
170170
// max: yAxisMax,
171171
// tickAmount: Math.ceil((yAxisMax - yAxisMin) / 5000),
172+
min: 7500,
173+
max: 12500,
174+
tickAmount: 6,
172175
labels: {
173176
style: {
174177
colors: '#000000',

client/components/activity/partials/TradeCard.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ const modelIcon = computed(() => getModelIcon(props.trade.model_name))
106106
const coinIcon = computed(() => getCoinIcon(props.trade.coin))
107107
const tradeType = computed(() => props.trade.trade_type.toLowerCase())
108108
const tradeTypeColor = computed(() =>
109-
tradeType.value === 'long' ? 'text-green-600' : 'text-red-600'
109+
tradeType.value === 'long trade' ? 'text-green-600' : 'text-red-600'
110110
)
111111
const pnlColor = computed(() =>
112112
props.trade.net_pnl >= 0 ? 'text-green-600' : 'text-red-600'

client/server/api/account-values.get.ts

Lines changed: 65 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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

44
export 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

client/server/api/accounts.get.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@ export default defineEventHandler(async (event) => {
1616
const allAccounts = await db.select().from(accounts).orderBy(accounts.created_at)
1717

1818
// Convert numeric values to numbers and add model_name
19+
// Use stored metrics from the database
1920
return allAccounts.map(account => ({
2021
id: account.id,
2122
model_name: getModelName(account.id),
2223
initial_balance: parseFloat(account.initial_balance),
2324
current_balance: parseFloat(account.current_balance),
2425
total_pnl: parseFloat(account.total_pnl),
26+
// Use stored calculated metrics
27+
account_value: account.account_value ? parseFloat(account.account_value) : parseFloat(account.current_balance),
28+
crypto_value: account.crypto_value ? parseFloat(account.crypto_value) : 0,
29+
total_return_percent: account.total_return_percent ? parseFloat(account.total_return_percent) : 0,
30+
sharpe_ratio: account.sharpe_ratio ? parseFloat(account.sharpe_ratio) : null,
2531
created_at: account.created_at,
2632
updated_at: account.updated_at,
2733
}))

client/server/api/completed-trades.get.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export default defineEventHandler(async (event) => {
7676
const realizedPnl = parseFloat(order.realized_pnl)
7777

7878
// Determine trade type and holding time
79-
const tradeType = position ? (position.side === 'LONG' ? 'long trade' : 'short trade') : 'trade'
79+
const tradeType = position ? (position.side === 'BUY' ? 'long trade' : 'short trade') : 'trade'
8080
const holdingTime = position
8181
? Math.round((new Date(order.created_at).getTime() - new Date(position.created_at).getTime()) / (1000 * 60)) // minutes
8282
: 0

client/server/api/performance.get.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,30 @@ export default defineEventHandler(async (event) => {
1818
}
1919
}
2020

21-
// Calculate percentage change from initial balance
21+
// Use stored account_value and total_return_percent instead of calculating
2222
const accountsWithChange = allAccounts.map(account => {
2323
const initial = parseFloat(account.initial_balance)
24-
const current = parseFloat(account.current_balance)
25-
const change = initial > 0 ? ((current - initial) / initial) * 100 : 0
24+
// Use stored account_value if available, otherwise use current_balance
25+
const accountValue = account.account_value
26+
? parseFloat(account.account_value)
27+
: parseFloat(account.current_balance)
28+
// Use stored total_return_percent if available, otherwise calculate
29+
const change = account.total_return_percent
30+
? parseFloat(account.total_return_percent)
31+
: (initial > 0 ? ((accountValue - initial) / initial) * 100 : 0)
2632

2733
return {
2834
id: account.id,
29-
current_balance: current,
35+
account_value: accountValue,
36+
current_balance: parseFloat(account.current_balance),
3037
initial_balance: initial,
3138
change,
3239
}
3340
})
3441

42+
// Sort by account_value (total value) instead of just current_balance
43+
accountsWithChange.sort((a, b) => b.account_value - a.account_value)
44+
3545
// Get highest (first in descending order)
3646
const highest = accountsWithChange[0]
3747

@@ -48,13 +58,13 @@ export default defineEventHandler(async (event) => {
4858
return {
4959
highest: {
5060
model: modelNameMap[highest.id] || `Account ${highest.id.slice(0, 8)}`,
51-
value: highest.current_balance,
61+
value: highest.account_value, // Use total account value instead of just balance
5262
change: highest.change,
5363
icon: 'purple', // Default icon
5464
},
5565
lowest: {
5666
model: modelNameMap[lowest.id] || `Account ${lowest.id.slice(0, 8)}`,
57-
value: lowest.current_balance,
67+
value: lowest.account_value, // Use total account value instead of just balance
5868
change: lowest.change,
5969
icon: 'green', // Default icon
6070
},

client/server/api/positions.get.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,23 @@ export default defineEventHandler(async (event) => {
2626

2727
for (const { position, account } of allPositions) {
2828
if (!positionsByAccount.has(account.id)) {
29+
// Use stored crypto_value if available, otherwise calculate from positions
30+
const cryptoValue = account.crypto_value
31+
? parseFloat(account.crypto_value)
32+
: 0
33+
2934
positionsByAccount.set(account.id, {
3035
account_id: account.id,
3136
account: {
3237
id: account.id,
3338
initial_balance: parseFloat(account.initial_balance),
3439
current_balance: parseFloat(account.current_balance),
3540
total_pnl: parseFloat(account.total_pnl),
41+
account_value: account.account_value ? parseFloat(account.account_value) : parseFloat(account.current_balance),
42+
crypto_value: cryptoValue,
3643
},
3744
positions: [],
38-
total_unrealized_pnl: 0,
45+
total_unrealized_pnl: cryptoValue,
3946
available_cash: parseFloat(account.current_balance),
4047
})
4148
}
@@ -56,7 +63,8 @@ export default defineEventHandler(async (event) => {
5663
}
5764

5865
accountData.positions.push(positionData)
59-
accountData.total_unrealized_pnl += positionData.unrealized_pnl
66+
// Use stored total_unrealized_pnl from account, don't recalculate
67+
// The stored value is already the sum of all position unrealized_pnl
6068

6169
// Calculate notional value (quantity * current_price)
6270
const notional = Math.abs(positionData.quantity * positionData.current_price)

0 commit comments

Comments
 (0)