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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
70eab33d1b
commit
a6a79bba7e
+10
-1
@@ -380,6 +380,10 @@ export function projectPerfectPath(opts: {
|
|||||||
profitTarget: number; consistency: number; minDayPnL: number;
|
profitTarget: number; consistency: number; minDayPnL: number;
|
||||||
minTradingDays: number; accountSize: number; targetSameEquity: boolean;
|
minTradingDays: number; accountSize: number; targetSameEquity: boolean;
|
||||||
withdrawalStages: WithdrawalStage[]; splitEnabled: 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). */
|
/** Current-cycle daily P&L (account.dailyPnL). */
|
||||||
cycleDailyPnL: { date: string; pnl: number }[];
|
cycleDailyPnL: { date: string; pnl: number }[];
|
||||||
@@ -491,12 +495,17 @@ export function projectPerfectPath(opts: {
|
|||||||
// balance), capped. Prop payouts come out of profit, never the funded
|
// balance), capped. Prop payouts come out of profit, never the funded
|
||||||
// principal. Returns true when the projection should stop (no further
|
// principal. Returns true when the projection should stop (no further
|
||||||
// stages).
|
// 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 finishCycle = (onDate: string, dayPnl: number, stage: number, drain: boolean): boolean => {
|
||||||
const { pct, cap } = stagePayout();
|
const { pct, cap } = stagePayout();
|
||||||
const rawGross = round2(Math.max(0, balance - cfg.accountSize) * pct);
|
const rawGross = round2(Math.max(0, balance - cfg.accountSize) * pct);
|
||||||
const gross = cap > 0 ? Math.min(rawGross, round2(cap)) : rawGross;
|
const gross = cap > 0 ? Math.min(rawGross, round2(cap)) : rawGross;
|
||||||
const net = round2(gross * profitSplit);
|
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 });
|
funds.push({ date: onDate, amount: -gross });
|
||||||
balance = round2(balance - gross);
|
balance = round2(balance - gross);
|
||||||
sim = []; // withdrawal excludes its day — fresh cycle
|
sim = []; // withdrawal excludes its day — fresh cycle
|
||||||
|
|||||||
Reference in New Issue
Block a user