Attribute evening-session Cash History rows to the next trade date

Cash History rows are stamped with the Central calendar date, but trades
in the evening session (>= 5:00 PM CT) belong to the next trade date -
matching currentTradeDateCT and when Tradovate resets realizedPnL. The
mismatch made computeDayPlan count the same session twice (as a phantom
prior day and as today's realized PnL), inflating the day target into a
moving one: 40% consistency accounts showed dayTarget = base + 0.4 x
today's PnL instead of the anchored day-start target.

Roll those rows forward at ingestion, and make a successful Cash History
report authoritative for its 28-day window so rows cached under the old
calendar date are dropped rather than double-counted. An empty report
never deletes anything (it can't distinguish "no trades" from "no
data"), and a report still pending after polling now throws instead of
masquerading as an empty one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-08 20:45:19 -05:00
co-authored by Claude Fable 5
parent 77ae4971f8
commit 57846d2b5f
2 changed files with 57 additions and 7 deletions
+17
View File
@@ -234,6 +234,23 @@ export function saveDailyPnL(accountId: number, accountName: string, entries: {
txn();
}
/**
* Replace all rows on/after fromDate with the given entries, keeping older rows.
* Used when a full Cash History report succeeded: it is authoritative for its window,
* so stale rows there (e.g. saved under a pre-roll calendar date) must not survive.
*/
export function replaceDailyPnLWindow(accountId: number, accountName: string, fromDate: string, entries: { date: string; pnl: number }[]): void {
const del = db.prepare('DELETE FROM daily_pnl WHERE account_id = ? AND date >= ?');
const ins = db.prepare('INSERT OR REPLACE INTO daily_pnl (account_id, account_name, date, pnl) VALUES (?, ?, ?, ?)');
const txn = db.transaction(() => {
del.run(accountId, fromDate);
for (const e of entries) {
ins.run(accountId, accountName, e.date, e.pnl);
}
});
txn();
}
export function loadDailyPnL(accountId: number): { date: string; pnl: number }[] {
return (db.prepare('SELECT date, pnl FROM daily_pnl WHERE account_id = ? ORDER BY date').all(accountId) as { date: string; pnl: number }[]);
}