DXtrade: date transactions by the firm's day, not the CT trade date
The firm rolls its day at 22:00 UTC; our trade date at 5 PM CT. In summer they coincide, in winter the firm's rolls an hour earlier. New entries are already kept out of that hour, but a take-profit or stop that fills in it was booked to the day before. firmDayOf labels the firm's day the way currentTradeDateCT labels ours, and the sync's transfer window now starts at the firm day behind today's trade date. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
934620e8e9
commit
7f55e49755
+12
-5
@@ -25,7 +25,8 @@
|
||||
* - Cash transactions (SETTLEMENT, COMMISSION, FINANCING, DEPOSIT,
|
||||
* WITHDRAWAL…) are the books: daily P&L, fund transactions, today's
|
||||
* realized P&L and the balance at the firm's 22:00 UTC reset all come from
|
||||
* them.
|
||||
* them. Each is dated by the firm's day (firmDayOf), which in winter rolls
|
||||
* an hour before our 5 PM CT trade date.
|
||||
* - No auto-liquidation profile: autoLiqThresholds stays 0; a breached
|
||||
* account shows as accountStatus ≠ FULL_TRADING → active=false.
|
||||
*/
|
||||
@@ -44,7 +45,7 @@ import type { PlatformClient, PlatformAccount, PlatformContract, ClientHealth, P
|
||||
import { OrderNotPlacedError, isOrderNotPlaced, errorMessage } from './platform-client';
|
||||
import {
|
||||
dxAccountId, accountDisplayName, accountCodeOf, encodeContractName, decodeContractName, roundToStep,
|
||||
lastUtcReset, ctTradeDayStart, timeOf, parseDurationMs, ciNum, isFundType, signedFundValue, newOrderCode,
|
||||
lastUtcReset, ctTradeDayStart, firmDayOf, timeOf, parseDurationMs, ciNum, isFundType, signedFundValue, newOrderCode,
|
||||
inTradingHours, realizedVolPerHour, scoreCandidate, candidateRejection, parseTransfers, marginRateOf,
|
||||
} from './dxtrade-helpers';
|
||||
import type { DxTxn } from './dxtrade-helpers';
|
||||
@@ -614,7 +615,13 @@ export class DXTradeClient implements PlatformClient {
|
||||
const orders = new Map<string, DxOrder[]>();
|
||||
let txns: DxTxn[] = [];
|
||||
const now = new Date();
|
||||
const windowStart = new Date(Math.min(ctTradeDayStart(now).getTime(), lastUtcReset(now, FIRM_RESET_UTC_HOUR).getTime()) - 5 * 60_000);
|
||||
// From the firm's day that today's trade date names (in winter it
|
||||
// began an hour before 5 PM CT) and the current reset, whichever
|
||||
// is earlier.
|
||||
const windowStart = new Date(Math.min(
|
||||
lastUtcReset(ctTradeDayStart(now), FIRM_RESET_UTC_HOUR).getTime(),
|
||||
lastUtcReset(now, FIRM_RESET_UTC_HOUR).getTime(),
|
||||
) - 5 * 60_000);
|
||||
if (codes.length > 0) {
|
||||
try {
|
||||
for (const m of await this.perAccounts<Record<string, unknown>>('/accounts/metrics', codes, (d) => (d as { metrics?: unknown[] })?.metrics ?? [])) {
|
||||
@@ -659,7 +666,7 @@ export class DXTradeClient implements PlatformClient {
|
||||
this.recentTxns.set(acc.id, map);
|
||||
let realized = 0;
|
||||
for (const t of map.values()) {
|
||||
if (!isFundType(t.type) && currentTradeDateCT(new Date(t.time)) === today) realized += t.value;
|
||||
if (!isFundType(t.type) && firmDayOf(new Date(t.time), FIRM_RESET_UTC_HOUR) === today) realized += t.value;
|
||||
}
|
||||
this.accountCashBalances[acc.id] = {
|
||||
amount: balance, realizedPnL: round2(realized), equity: ciNum(m, 'equity'), availableFunds: ciNum(m, 'availableFunds'),
|
||||
@@ -913,7 +920,7 @@ export class DXTradeClient implements PlatformClient {
|
||||
const daily = new Map<string, { pnl: number; fees: number }>();
|
||||
const funds = new Map<string, number>();
|
||||
for (const t of mine) {
|
||||
const date = currentTradeDateCT(new Date(t.time));
|
||||
const date = firmDayOf(new Date(t.time), FIRM_RESET_UTC_HOUR);
|
||||
if (isFundType(t.type)) {
|
||||
funds.set(date, (funds.get(date) ?? 0) + signedFundValue(t.type, t.value));
|
||||
continue;
|
||||
|
||||
@@ -91,6 +91,15 @@ export function lastUtcReset(now: Date, hourUtc = 22): Date {
|
||||
return reset;
|
||||
}
|
||||
|
||||
/** The trading day the firm books a moment to: the day that began at the
|
||||
* reset before it, labelled by the calendar date that follows — the label
|
||||
* currentTradeDateCT gives the same day. In summer the two roll together
|
||||
* (22:00 UTC = 5 PM CDT); in winter the firm's rolls an hour earlier, and a
|
||||
* close in that hour belongs to the new day, as the firm counts it. */
|
||||
export function firmDayOf(at: Date, hourUtc = 22): string {
|
||||
return new Date(at.getTime() + (24 - hourUtc) * 3_600_000).toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
/** When the current Central trade date began (5 PM CT) — the window today's
|
||||
* realized P&L covers, matching currentTradeDateCT. */
|
||||
export function ctTradeDayStart(now: Date): Date {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
botStopsActive, sizeNotional, isNoTradeTime, isPastSessionClose, nextTradingDate, effectiveCombine, DEFAULT_TARGET_HOLD_HOURS,
|
||||
projectPerfectPath, cycleCompletion, DLL_RESERVE_FRACTION, DLL_MIN_STOP, notionalCap, expectedSwapToWin, MARGIN_HEADROOM,
|
||||
dllReserve, DLL_SLIPPAGE_FRACTION, maxLossRoom, lastStand, breachDistance, LAST_STAND_FRACTION, planLegSize, rankForTarget,
|
||||
currentTradeDateCT,
|
||||
} from '../lib/trading-logic.ts';
|
||||
|
||||
// Deterministic PRNG so a failure reproduces.
|
||||
@@ -502,7 +503,7 @@ test('an every-day firm can finish its cycle on a Saturday', () => {
|
||||
|
||||
import {
|
||||
dxAccountId, accountDisplayName, accountCodeOf, encodeContractName, decodeContractName, roundToStep,
|
||||
lastUtcReset, ctTradeDayStart, parseDurationMs, ciNum, signedFundValue, inTradingHours,
|
||||
lastUtcReset, ctTradeDayStart, firmDayOf, parseDurationMs, ciNum, signedFundValue, inTradingHours,
|
||||
realizedVolPerHour, scoreCandidate, candidateRejection, MIN_VOL_CANDLES, parseTransfers, marginRateOf,
|
||||
} from '../lib/dxtrade-helpers.ts';
|
||||
|
||||
@@ -573,6 +574,19 @@ test('the firm reset and the CT trade-date start', () => {
|
||||
assert.equal(ctTradeDayStart(cst('2027-01-12T10:00:00')).toISOString(), '2027-01-11T23:00:00.000Z');
|
||||
});
|
||||
|
||||
test('the firm\'s day: the same label as the CT trade date, rolling at its reset', () => {
|
||||
// Summer: the reset is 5 PM CDT, so the two agree on both sides of it.
|
||||
assert.equal(firmDayOf(new Date('2026-09-26T21:59:00Z')), '2026-09-26');
|
||||
assert.equal(firmDayOf(new Date('2026-09-26T22:00:00Z')), '2026-09-27');
|
||||
assert.equal(firmDayOf(new Date('2026-09-26T22:00:00Z')), currentTradeDateCT(new Date('2026-09-26T22:00:00Z')));
|
||||
// Winter: a close at 4:30 PM CST is the firm's next day, still ours.
|
||||
const close = cst('2027-01-12T16:30:00');
|
||||
assert.equal(firmDayOf(close), '2027-01-13');
|
||||
assert.equal(currentTradeDateCT(close), '2027-01-12');
|
||||
assert.equal(firmDayOf(cst('2027-01-12T15:59:00')), '2027-01-12');
|
||||
assert.equal(firmDayOf(cst('2027-01-12T17:30:00')), currentTradeDateCT(cst('2027-01-12T17:30:00')));
|
||||
});
|
||||
|
||||
test('loose parsing', () => {
|
||||
assert.equal(parseDurationMs('PT30M'), 30 * 60_000);
|
||||
assert.equal(parseDurationMs('01:05:08'), (3600 + 300 + 8) * 1000);
|
||||
|
||||
Reference in New Issue
Block a user