Show fund transactions (W/D) on calendar, equity curve, and cash history table

- Persist fund transactions to SQLite (fund_transactions table) so they
  survive beyond Tradovate's 28-day report window
- Calendar: highlight W/D dates in amber with the amount shown below the day
- Equity curve: reduce running equity at withdrawal dates and show a vertical
  dashed amber line labelled W/D
- New Cash History table below calendar listing all trades and W/D events
  sorted newest-first

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-03-21 04:16:24 -05:00
co-authored by Claude Sonnet 4.6
parent 6d960ceb7b
commit 4252adfbea
5 changed files with 144 additions and 18 deletions
+18 -5
View File
@@ -5,7 +5,7 @@ import type { AccountItem, AuthLoginResponse, Contract } from './tradovate-helpe
import { computeSec, randomUUIDV4 } from './tradovate-helpers';
import { POINT_VALUES } from './trading-logic';
import { getCachedContract, resolveContracts } from './contract-resolver';
import { saveDailyPnL, loadDailyPnL, saveAccountMeta, loadAccountMeta } from './db';
import { saveDailyPnL, loadDailyPnL, saveAccountMeta, loadAccountMeta, saveFundTransactions, loadFundTransactions } from './db';
export class TradovateClient {
private name: string;
@@ -33,6 +33,8 @@ export class TradovateClient {
public dailyPnL: { [accountId: number]: { date: string; pnl: number }[] } = {};
/** Date of the last fund transaction per account — days traded are counted from this date onwards */
public lastFundDates: { [accountId: number]: string | null } = {};
/** All fund transactions (deposits/withdrawals) per account */
public fundTransactions: { [accountId: number]: { date: string; amount: number }[] } = {};
/** Balance floor set by the prop firm — account is blown when amount <= this value (0 = not set) */
public autoLiqThresholds: { [accountId: number]: number } = {};
@@ -408,9 +410,10 @@ export class TradovateClient {
};
for (const account of this.accountList) {
// Load last known fund date from DB — used to filter days traded to the current challenge period
// Load last known fund date and fund transactions from DB
const storedFundDate = loadAccountMeta(account.id, 'last_fund_date');
this.lastFundDates[account.id] = storedFundDate;
this.fundTransactions[account.id] = loadFundTransactions(account.id);
// Load cache first — serves as both the startup baseline and the fallback if API fails
const cached = loadDailyPnL(account.id);
@@ -427,11 +430,12 @@ export class TradovateClient {
const rows: { Date: string; Delta: string; 'Cash Change Type': string; [k: string]: any }[] = JSON.parse(fixed);
this.lastFetchRaw[account.name] = rows.length > 0 ? JSON.stringify(rows[0]) : '(empty)';
const fundDates: string[] = [];
const fundMap: { [date: string]: number } = {};
const dailyMap: { [date: string]: number } = {};
for (const row of rows) {
if ((row['Cash Change Type'] ?? '').trim() === 'Fund Transaction') {
fundDates.push(row['Date']);
const delta = parseFloat((row['Delta'] ?? '0').replace(/,/g, ''));
if (!isNaN(delta)) fundMap[row['Date']] = (fundMap[row['Date']] ?? 0) + delta;
continue;
}
const delta = parseFloat((row['Delta'] ?? '0').replace(/,/g, ''));
@@ -439,8 +443,17 @@ export class TradovateClient {
dailyMap[row['Date']] = (dailyMap[row['Date']] ?? 0) + delta;
}
// Persist fund transactions so they survive beyond the 28-day window
const freshFundTxns = Object.entries(fundMap)
.map(([date, amount]) => ({ date, amount: Math.round(amount * 100) / 100 }))
.sort((a, b) => a.date.localeCompare(b.date));
if (freshFundTxns.length > 0) {
saveFundTransactions(account.id, account.name, freshFundTxns);
this.fundTransactions[account.id] = loadFundTransactions(account.id); // reload merged full history
}
// Use the most recent fund transaction as the reset point — persist it so it survives beyond the 28-day window
const lastFundDate = fundDates.sort().pop() ?? null;
const lastFundDate = [...Object.keys(fundMap)].sort().pop() ?? null;
if (lastFundDate) {
saveAccountMeta(account.id, 'last_fund_date', lastFundDate);
this.lastFundDates[account.id] = lastFundDate;