Vest: no leg opens into a market close, and accounts plan on Vest's day

Market close: a Vest entry now needs its symbol's market to stay open
for RESET_CLEAR_HOLDS holds of the leg (clearsClose), the clearance the
8 PM ET reset already gets: at the target hold, and at the leg's sized
hold when a leverage cap stretches it. That covers the weekly close and
the daily maintenance hour as Vest's market-hours periods list them.
Before a closure that runs past an 8 PM ET reset (the weekend, a
holiday: longClosure), the bot's own legs still open in the last 5
minutes are closed at market, loudly; a position the bot didn't open is
named in the log and left alone.

Vest's day: Vest accounts are planned, and their legs and entries
counted, on Vest's day (20:00 ET) instead of the Central date
(tradeDateFor, PlatformClient.tradeDate). Between 5 PM CT and the reset
the Central date counted the open Vest day as a prior day and as today's
realized P&L both ($3,000 to go, $800 made: $2,050 left, not $1,900) and
started the leg count over. Other platforms keep the Central date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-10-27 14:12:27 -05:00
co-authored by Claude Opus 5.5
parent beddbdcf6c
commit ec3e6ea388
8 changed files with 290 additions and 32 deletions
+21
View File
@@ -165,6 +165,18 @@ export function currentTradeDateCT(now: Date = new Date()): string {
return new Date(date).toISOString().slice(0, 10);
}
/**
* The trade date a client's accounts are planned on and count their legs by:
* the platform's own day where its daily rows and realized P&L follow one
* that turns at another hour (Vest: 20:00 ET, two hours after the 5 PM CT
* roll), else the Central trade date. Between the two rolls the Central date
* would count the platform's still-open day both as a prior day and as
* today's realized P&L, and start the day's leg count over mid-day.
*/
export function tradeDateFor(client: { tradeDate?: (now: number) => string } | null | undefined, now: Date = new Date()): string {
return typeof client?.tradeDate === 'function' ? client.tradeDate(now.getTime()) : currentTradeDateCT(now);
}
/** Drain-day target grid: $4–$15 in $0.50 steps (23 values, MNQ's
* per-contract tick), always traded on MNQ. Drain days qualify at ≥
* DRAIN_DAY_MIN, so every draw counts. Seeded per (account, trade date) like
@@ -1387,6 +1399,15 @@ export function resetClearStop(o: { leg: number; resetInMs: number; holdHours: n
return (o.leg * (c + Math.sqrt(c * c - 4))) / 2;
}
/** Whether a leg of `holdHours` starting now is all but sure to be done
* before its market closes `closeInMs` from now: the RESET_CLEAR_HOLDS
* holds a daily reset is given (resetClearStop). A leg still open at the
* close can't be exited until the market reopens, and meets the reopen's
* gap with its stop. */
export function clearsClose(o: { closeInMs: number; holdHours: number }): boolean {
return o.closeInMs >= RESET_CLEAR_HOLDS * o.holdHours * 3_600_000;
}
/**
* dllRoom where the platform states its daily-loss floor itself (Vest: the
* equity the account may not fall below today, already moved by the day's