diff --git a/lib/trading-logic.ts b/lib/trading-logic.ts index fd81bfc..9b29189 100644 --- a/lib/trading-logic.ts +++ b/lib/trading-logic.ts @@ -3,7 +3,9 @@ export interface WithdrawalStage { consistency: number; minTradingDays: number; /** Spam-withdraw stage: no profit target — book minTradingDays qualifying - * days of ~minDayPnL (tiny MNQ trades), then wait for the withdrawal. */ + * days of ~minDayPnL (tiny MNQ trades), then wait for the withdrawal. + * The stage's consistency still applies: days keep booking until the + * cycle's biggest day fits inside that share of the cycle total. */ drain?: boolean; /** Drain stages only: the per-day booked amount (default $5; randomized * the fixed $4–$15 half-dollar grid by drainDayAmount; this field is @@ -61,7 +63,10 @@ export function resolveEffectiveConfig( if (stage.drain === true) { return { profitTarget: 0, - consistency: 0, + // The firm's consistency rule doesn't pause for the drain + // (Topstep still holds its ratio at payout), so carry the + // stage's value instead of zeroing it. + consistency: stage.consistency, minTradingDays: stage.minTradingDays || minTradingDays, // Drain amounts come from the fixed $4–$15 grid, so the // qualifying bar sits at the grid floor — every draw counts. @@ -796,7 +801,17 @@ export function computeDayPlan( const qualifying = minDayPnL > 0 ? priorDays.filter((d) => d.pnl >= minDayPnL) : priorDays.filter((d) => d.pnl !== 0); - const dayTarget = qualifying.length >= minTradingDays + // The stage's consistency rule outlives the profit target: the cycle's + // biggest day must fit inside `consistency` of the cycle total, so the + // drain keeps booking days past the min-day count until the total + // covers maxDay / consistency. + let consistencyShort = false; + if (consistency > 0 && consistency < 1 && priorDays.length > 0) { + const maxDay = Math.max(0, ...priorDays.map((d) => d.pnl)); + const total = priorDays.reduce((s, d) => s + d.pnl, 0); + consistencyShort = maxDay > 0 && total < round2(maxDay / consistency) - 0.005; + } + const dayTarget = qualifying.length >= minTradingDays && !consistencyShort ? 0 : drainDayAmount(accountId, tradeDate); return {