Vest: a split day's next leg gives back the day's gain plus the limit
On a split day the next Vest leg's daily-loss room now adds the day's gain still in the account to the bot's limit (floorRoom's `gain`), never past Vest's own floor, and the leg sizes up for the same target hold (Dingo's next eval leg: $455k -> $842k at 1.5 h, win 28% -> 56%). Other platforms are unchanged: dllRoom still never widens on a green day. The gain only widens a leg all but sure to close before Vest's 8 PM ET reset (resetClearStop, held to the shortest leg the day may carve), and none of a hand claim's profit counts. The last-stand margin is measured against the limit, so a green day can't drop a leg's stop. Around the reset: no new Vest position opens in the last 7 minutes, and the guard re-fits every 15 s on fresh positions instead of one pass. An account's positions share the next-day room (stops first, least at risk first; locked-in profit counts), a hand-moved stop is fitted again, and a close or profit-side stop that doesn't take is retried or closed. The dashboard's next stop now reserves for the leg's notional, as fireLeg does, so DXtrade labels read smaller (display only). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1e844178de
commit
f0c1569d96
+6
-5
@@ -18,7 +18,7 @@ import {
|
||||
BIG_DAY_THRESHOLD, LEG_COMPLETION_EPSILON, MAX_LEGS_PER_DAY,
|
||||
FALLBACK_COMMISSION_PER_CONTRACT, MAX_COMMISSION_PER_CONTRACT, DLL_MIN_STOP,
|
||||
CME_EVENING_OPEN_MIN, foldReason, withinFirmCaps,
|
||||
fundRowsKey, markFinishedCycle, unbookedPayoutDrop, machineSide, stageInputsFor, floorRoom,
|
||||
fundRowsKey, markFinishedCycle, unbookedPayoutDrop, machineSide, stageInputsFor, floorRoom, shortestNextLeg,
|
||||
type DayPlan, type FinishedCycleMark,
|
||||
} from './trading-logic';
|
||||
import { getSetting, setSetting, loadAccountMeta, saveAccountMeta } from './db';
|
||||
@@ -621,7 +621,7 @@ function dailyLossGate(
|
||||
cfg: { maxPositionSize: number; firmId: string },
|
||||
cash: { amount: number; realizedPnL: number },
|
||||
combinedRealized: number,
|
||||
remainingToday: number,
|
||||
plan: { remainingToday: number; splitAllowed: boolean },
|
||||
singleContract: boolean,
|
||||
): { room: number } | { reason: string } {
|
||||
if (!(dll > 0)) return { room: Infinity };
|
||||
@@ -634,6 +634,7 @@ function dailyLossGate(
|
||||
room = floorRoom({
|
||||
dailyLossLimit: dll, closedBalance: cash.amount, floor,
|
||||
realizedToday: Math.min(cash.realizedPnL, combinedRealized),
|
||||
gain: client.dayGainForRoom?.(accountId, { leg: shortestNextLeg(plan), limit: dll }) ?? 0,
|
||||
});
|
||||
} else {
|
||||
const dayStart: number | null | undefined = client.dayStartBalance?.(accountId);
|
||||
@@ -651,7 +652,7 @@ function dailyLossGate(
|
||||
// could use. Percent-fee firms size by notional, which isn't known
|
||||
// here — fireLeg re-checks with the real size before any entry.
|
||||
const contractsBound = singleContract ? 1
|
||||
: Math.min(Math.max(1, Math.ceil(remainingToday / 1000)), cfg.maxPositionSize > 0 ? cfg.maxPositionSize : Infinity);
|
||||
: Math.min(Math.max(1, Math.ceil(plan.remainingToday / 1000)), cfg.maxPositionSize > 0 ? cfg.maxPositionSize : Infinity);
|
||||
const percentFees = (getCachedFirm(cfg.firmId)?.commissionPct ?? 0) > 0;
|
||||
const feeEstimate = percentFees ? 0 : 2 * FALLBACK_COMMISSION_PER_CONTRACT * contractsBound;
|
||||
if (!dllViable(room, dll, feeEstimate)) {
|
||||
@@ -837,7 +838,7 @@ function evaluateAccountForTrade(client: any, acc: { id: number; name: string; a
|
||||
// (day done, challenge complete, leg cap) win. A daily-loss stop-out
|
||||
// leaves about the reserve plus fees, which is below what a trade needs —
|
||||
// so a stop-out benches the account for the rest of the day by itself.
|
||||
const gate = dailyLossGate(client, acc.id, effective.dailyLossLimit, cfg, cash, combined.realizedPnL, plan.remainingToday, isExtraDay || effective.drain);
|
||||
const gate = dailyLossGate(client, acc.id, effective.dailyLossLimit, cfg, cash, combined.realizedPnL, plan, isExtraDay || effective.drain);
|
||||
if ('reason' in gate) return { ...evaluated, eligible: false, reason: gate.reason };
|
||||
if (Number.isFinite(gate.room)) return { ...evaluated, eligible: true, reason: '', dllRoom: gate.room };
|
||||
|
||||
@@ -1868,7 +1869,7 @@ export async function fireManualLeg(accountId: number, action: 'Buy' | 'Sell' |
|
||||
// The once-a-day return comes before the daily-loss check: run it here.
|
||||
let room = ev.dllRoom;
|
||||
if (!ev.eligible) {
|
||||
const gate = dailyLossGate(client, acc.id, ev.dll, cfg, ev.cash, ev.cash.realizedPnL, ev.plan.remainingToday, ev.drain);
|
||||
const gate = dailyLossGate(client, acc.id, ev.dll, cfg, ev.cash, ev.cash.realizedPnL, ev.plan, ev.drain);
|
||||
if ('reason' in gate) throw new Error(gate.reason);
|
||||
room = gate.room;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user