Report each live account's payout state to the master
payoutStates: one entry per live account (negative included, dead not) with its current-cycle progress — qualifying days, full equity profit, max qualifying day, P&L sum, and the cycle's end-of-day high-water mark — plus the withdrawal count and the cycle's effective target/consistency/ min-days from resolveEffectiveConfig. client.dailyPnL is already the active-cycle slice, so a withdrawal resets the reported path on its own. Feeds the master's payout calendar; an older master ignores the field. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
abfb703f0e
commit
79ee5773cb
@@ -28,6 +28,19 @@ export interface AccountTypeStats {
|
||||
* calendar only projects accounts still on the winning path. Optional so
|
||||
* an older master simply ignores it. */
|
||||
daysHist?: Record<number, number>;
|
||||
/** One entry per live account (negative ones included, dead ones not):
|
||||
* its actual progress toward the current target, so the master can
|
||||
* simulate the real required profit instead of assuming the winning
|
||||
* path. A withdrawal resets the path, so days/maxDay/sum cover only the
|
||||
* current cycle (client.dailyPnL is already the active-cycle slice),
|
||||
* while profit stays the full equity profit. withdrawals > 0 tells the
|
||||
* master the trail locked long ago; target/consistency/minDays carry
|
||||
* this cycle's effective values from resolveEffectiveConfig. Optional so
|
||||
* an older master simply ignores it. */
|
||||
payoutStates?: {
|
||||
days: number; profit: number; maxDay: number; sum: number; high: number;
|
||||
withdrawals?: number; target?: number; consistency?: number; minDays?: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface FirmStats {
|
||||
|
||||
+40
-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 } from './trading-logic';
|
||||
import { combineCmeSessions, computeDayPlan, currentTradeDateCT, LEG_COMPLETION_EPSILON, resolveEffectiveConfig } from './trading-logic';
|
||||
import type { AccountTypeStats } from './master-protocol';
|
||||
|
||||
function collectFirmStats() {
|
||||
@@ -95,6 +95,45 @@ function collectFirmStats() {
|
||||
hist[combined.daysTraded] = (hist[combined.daysTraded] ?? 0) + 1;
|
||||
}
|
||||
|
||||
// Exact payout state for every live account — negative ones
|
||||
// included, since the master sims their real required profit.
|
||||
// client.dailyPnL is already the active-cycle slice, so a
|
||||
// withdrawal resets days/maxDay/sum here automatically.
|
||||
if (cfg && combined) {
|
||||
const r2 = (n: number) => Math.round(n * 100) / 100;
|
||||
const equityProfit = r2(cash.amount - cfg.accountSize);
|
||||
const qualifying = cfg.minDayPnL === 0
|
||||
? combined.dailyPnL
|
||||
: combined.dailyPnL.filter((d) => d.pnl >= cfg.minDayPnL);
|
||||
const sum = r2(combined.dailyPnL.reduce((s, d) => s + d.pnl, 0));
|
||||
// End-of-day equity-profit high-water mark for this cycle,
|
||||
// floored at the $0 start: walk the P&L from the cycle base.
|
||||
let run = r2(equityProfit - sum);
|
||||
let highWater = Math.max(0, run);
|
||||
for (const d of combined.dailyPnL) {
|
||||
run = r2(run + d.pnl);
|
||||
if (run > highWater) highWater = run;
|
||||
}
|
||||
// Effective target for this cycle: withdrawal stages applied.
|
||||
const fundTxns = client.fundTransactions?.[acc.id] ?? [];
|
||||
const effective = resolveEffectiveConfig(
|
||||
cfg.profitTarget, cfg.consistency, cfg.minTradingDays,
|
||||
cfg.targetSameEquity, cfg.withdrawalStages,
|
||||
client.priorProfit?.[acc.id] ?? 0, fundTxns,
|
||||
);
|
||||
(slice.payoutStates ??= []).push({
|
||||
days: qualifying.length,
|
||||
profit: equityProfit,
|
||||
maxDay: qualifying.length > 0 ? Math.max(...qualifying.map((d) => d.pnl)) : 0,
|
||||
sum,
|
||||
high: highWater,
|
||||
withdrawals: fundTxns.filter((f) => f.amount < 0).length,
|
||||
target: effective.profitTarget,
|
||||
consistency: effective.consistency,
|
||||
minDays: effective.minTradingDays,
|
||||
});
|
||||
}
|
||||
|
||||
if (client.positions[acc.id]) {
|
||||
inTrade++;
|
||||
accountsTraded++;
|
||||
|
||||
Reference in New Issue
Block a user