Files
crypto_trading/frontend/src/aggregate.test.js
T
matitos 57dcfe7adb Aggregate the top status cards across strategies
With several strategies the header cards showed only the first one. Expose
day_pnl and drawdown_pct per strategy and add aggregateStatus() (frontend) so the
cards show the whole account: summed cash/equity/P&L/trades/positions, worst
drawdown, and any halt. Single-strategy runs are unchanged. Adds tests.
2026-09-22 23:11:18 +00:00

88 lines
2.6 KiB
JavaScript

import { describe, expect, it } from 'vitest'
import { aggregateStatus } from './aggregate'
const SINGLE = { equity: 100, total_pnl: 5, strategies: [] }
function strategy(overrides = {}) {
return {
strategy_key: 'sma_cross',
symbol: 'BTC/USDT',
timeframe: '5m',
allocation: 0.5,
cash: 1000,
equity: 1000,
realized_pnl: 0,
unrealized_pnl: 0,
total_pnl: 0,
open_positions: 0,
halted: false,
halt_reason: null,
trades_today: 0,
day_pnl: 0,
drawdown_pct: 0,
...overrides,
}
}
describe('aggregateStatus', () => {
it('returns the plain status when there is at most one strategy', () => {
expect(aggregateStatus(SINGLE, { strategies: [] })).toBe(SINGLE)
expect(aggregateStatus(SINGLE, { strategies: [strategy()] })).toBe(SINGLE)
})
it('sums the per-strategy numbers', () => {
const multi = {
running: true,
equity: 2100,
starting_balance: 2000,
total_pnl: 100,
price: 123,
strategies: [
strategy({ cash: 1000, equity: 1100, realized_pnl: 100, unrealized_pnl: 0, total_pnl: 100, trades_today: 2, open_positions: 1, day_pnl: 40 }),
strategy({ cash: 1000, equity: 1000, realized_pnl: 0, unrealized_pnl: 0, total_pnl: 0, trades_today: 0, open_positions: 0, day_pnl: -10 }),
],
}
const result = aggregateStatus(SINGLE, multi)
expect(result.cash).toBe(2000)
expect(result.equity).toBe(2100)
expect(result.realized_pnl).toBe(100)
expect(result.total_pnl).toBe(100)
expect(result.day_pnl).toBe(30)
expect(result.trades_today).toBe(2)
expect(result.open_positions).toBe(1)
expect(result.price).toBe(123)
expect(result.running).toBe(true)
})
it('reports the worst drawdown and any halt', () => {
const multi = {
running: true,
equity: 2000,
starting_balance: 2000,
total_pnl: 0,
price: null,
strategies: [
strategy({ drawdown_pct: 3, halted: false }),
strategy({ drawdown_pct: 7.5, halted: true, halt_reason: 'daily loss limit hit' }),
],
}
const result = aggregateStatus(SINGLE, multi)
expect(result.drawdown_pct).toBe(7.5)
expect(result.halted).toBe(true)
expect(result.halt_reason).toBe('daily loss limit hit')
})
it('falls back to the plain price when multi has none', () => {
const multi = {
running: false,
equity: 2000,
starting_balance: 2000,
total_pnl: 0,
price: null,
strategies: [strategy(), strategy()],
}
const result = aggregateStatus({ ...SINGLE, price: 99 }, multi)
expect(result.price).toBe(99)
})
})