Report each account's future payout chain with closed-form reach probabilities
projectPerfectPath prices every projected withdrawal as min(withdrawalPct × equity profit, cap) × profit split, and tracks the cumulative probability of reaching it under the fair-walk ruin model — room/(room + dayTarget) per day (the per-leg product telescopes), trailing threshold locked at breakeven after the first withdrawal, no obfuscation branches. The reporter attaches the first 6 payouts per account so the master's payout calendar renders real amounts and probabilities instead of one static expectedPayout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
50a5eb4713
commit
52d51afebb
+45
-1
@@ -17,7 +17,7 @@ import { getClients } from './clients';
|
||||
import { getTradableFirms } from './firm-catalog';
|
||||
import { getAccountConfig } from './account-type-resolver';
|
||||
import { getSchedulerStatus } from './auto-trade';
|
||||
import { combineCmeSessions, computeDayPlan, currentTradeDateCT, LEG_COMPLETION_EPSILON, resolveEffectiveConfig } from './trading-logic';
|
||||
import { combineCmeSessions, computeDayPlan, currentTradeDateCT, LEG_COMPLETION_EPSILON, projectPerfectPath, resolveEffectiveConfig } from './trading-logic';
|
||||
import type { AccountTypeStats } from './master-protocol';
|
||||
|
||||
function collectFirmStats() {
|
||||
@@ -126,6 +126,49 @@ function collectFirmStats() {
|
||||
if (run > highWater) highWater = run;
|
||||
}
|
||||
const traded = combined.dailyPnL.filter((d) => d.pnl !== 0);
|
||||
|
||||
// The account's future payout chain on the perfect path —
|
||||
// dates, dynamic amounts (withdrawal % of balance, capped,
|
||||
// × profit split) and cumulative fair-walk reach probability.
|
||||
// The master renders these directly instead of running its
|
||||
// own Monte-Carlo for this account.
|
||||
let futurePayouts: NonNullable<AccountTypeStats['payoutStates']>[number]['futurePayouts'];
|
||||
try {
|
||||
futurePayouts = projectPerfectPath({
|
||||
accountId: acc.id,
|
||||
cfg,
|
||||
cycleDailyPnL: client.dailyPnL[acc.id] ?? [],
|
||||
fundTransactions: fundTxns,
|
||||
balance: cash.amount,
|
||||
realizedPnL: cash.realizedPnL,
|
||||
payout: {
|
||||
withdrawalPct: cfg.withdrawalPct,
|
||||
payoutCap: cfg.payoutCap,
|
||||
profitSplit: cfg.profitSplit,
|
||||
// Same default chain the master's calendar always
|
||||
// used: configured trailing drawdown, else the
|
||||
// max-loss size, else $2,000.
|
||||
drawdown: (cfg.trailingDrawdown ?? 0) > 0
|
||||
? cfg.trailingDrawdown!
|
||||
: Math.abs(cfg.maxLoss) > 0 ? Math.abs(cfg.maxLoss) : 2000,
|
||||
startHigh: highWater,
|
||||
},
|
||||
})
|
||||
.filter((p) => p.withdrawal !== undefined || p.payoutReady)
|
||||
.slice(0, 6)
|
||||
.map((p) => ({
|
||||
date: p.date,
|
||||
gross: p.gross ?? 0,
|
||||
net: p.net ?? 0,
|
||||
prob: Math.round(p.prob * 10000) / 10000,
|
||||
stage: p.stage,
|
||||
...(p.drain ? { drain: true } : {}),
|
||||
}));
|
||||
if (futurePayouts.length === 0) futurePayouts = undefined;
|
||||
} catch (err) {
|
||||
console.error(`[reporter] payout projection failed for account ${acc.id}`, err);
|
||||
}
|
||||
|
||||
(slice.payoutStates ??= []).push({
|
||||
days: qualifying.length,
|
||||
profit: equityProfit,
|
||||
@@ -137,6 +180,7 @@ function collectFirmStats() {
|
||||
consistency: effective.consistency,
|
||||
minDays: effective.minTradingDays,
|
||||
lastDay: traded.length > 0 ? traded.reduce((a, b) => (a.date > b.date ? a : b)).date : undefined,
|
||||
...(futurePayouts ? { futurePayouts } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user