From a6a79bba7e682bd1d4d189596fe56e9fe736fd79 Mon Sep 17 00:00:00 2001 From: Brandon Li Date: Sat, 12 Sep 2026 19:04:22 -0500 Subject: [PATCH] Payout projection: payoutable same-equity accounts loop without explicit stages A payoutable account with no withdrawal stages (e.g. MFFU same-equity) now withdraws its share and re-targets the same equity each cycle instead of stopping after the first payout. Non-payoutable evals still stop at one completion. Co-Authored-By: Claude Opus 4.8 --- lib/trading-logic.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/trading-logic.ts b/lib/trading-logic.ts index f58f1d6..7f57630 100644 --- a/lib/trading-logic.ts +++ b/lib/trading-logic.ts @@ -380,6 +380,10 @@ export function projectPerfectPath(opts: { profitTarget: number; consistency: number; minDayPnL: number; minTradingDays: number; accountSize: number; targetSameEquity: boolean; withdrawalStages: WithdrawalStage[]; splitEnabled: boolean; + /** True = completing a cycle pays out and the account keeps going + * (withdraw, reset, re-target). False = it just PASSES once (an + * eval) and the projection stops. */ + payoutable?: boolean; }; /** Current-cycle daily P&L (account.dailyPnL). */ cycleDailyPnL: { date: string; pnl: number }[]; @@ -491,12 +495,17 @@ export function projectPerfectPath(opts: { // balance), capped. Prop payouts come out of profit, never the funded // principal. Returns true when the projection should stop (no further // stages). + // A cycle that completes keeps looping when the account has withdrawal + // stages OR is simply payoutable — a same-equity payout account (no + // explicit stages) withdraws its share and re-targets the same equity, + // over and over. Only a non-payoutable eval stops after one completion. + const loops = cfg.withdrawalStages.length > 0 || cfg.payoutable === true; const finishCycle = (onDate: string, dayPnl: number, stage: number, drain: boolean): boolean => { const { pct, cap } = stagePayout(); const rawGross = round2(Math.max(0, balance - cfg.accountSize) * pct); const gross = cap > 0 ? Math.min(rawGross, round2(cap)) : rawGross; const net = round2(gross * profitSplit); - if (cfg.withdrawalStages.length > 0 && withdrawals < maxWithdrawals && gross > 1) { + if (loops && withdrawals < maxWithdrawals && gross > 1) { funds.push({ date: onDate, amount: -gross }); balance = round2(balance - gross); sim = []; // withdrawal excludes its day — fresh cycle