Vest: payout projections and the completion catch-up restart same-day stages each day

projectPerfectPath and endedCycleCompletion counted every withdrawal ever
made, so a Vest funded account that had claimed on any earlier day was
projected at stage 2's $3,000 forever (labelled stage 3, 4, ...), and a
day's stage-1 cycle that the auto-claim ended before the reporter saw it
was judged against $3,000 and never reported. The projection now takes
the stage inputs: a type whose stages reset each day trades today on
today's payouts and every projected day from the base stage. The catch-up
judges a same-day type's ended cycle on its day's base stage; the stage it
reports stays the payout's number among the account's withdrawals, which
the master clears the entry by. Other types project exactly as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-10-27 14:15:07 -05:00
co-authored by Claude Opus 5.5
parent 4add379c68
commit e33e890a83
5 changed files with 99 additions and 15 deletions
+55 -1
View File
@@ -11,7 +11,7 @@ import assert from 'node:assert/strict';
import {
percentFee, takeProfitPlan, onTick, obfuscationStopDollars, dllRoom, dllStopDollars, dllViable, stopTicks,
botStopsActive, sizeNotional, isNoTradeTime, isPastSessionClose, nextTradingDate, effectiveCombine, DEFAULT_TARGET_HOLD_HOURS,
projectPerfectPath, cycleCompletion, DLL_RESERVE_FRACTION, DLL_MIN_STOP, notionalCap, expectedSwapToWin, MARGIN_HEADROOM,
projectPerfectPath, cycleCompletion, endedCycleCompletion, DLL_RESERVE_FRACTION, DLL_MIN_STOP, notionalCap, expectedSwapToWin, MARGIN_HEADROOM,
dllReserve, DLL_SLIPPAGE_FRACTION, maxLossRoom, lastStand, breachDistance, LAST_STAND_FRACTION, planLegSize, rankForTarget,
currentTradeDateCT, foldReason, withinFirmCaps, personSizeFactor, personHoldHours, PERSON_SIZE_SPREAD, machineSide,
resolveEffectiveConfig, stageInputsFor, floorRoom, resetClearStop, shortestNextLeg, computeDayPlan,
@@ -1147,3 +1147,57 @@ test('the day after a claim: a fresh cycle that aims at the next stage\'s target
assert.ok(plan.dayTarget >= 3000 && plan.dayTarget <= 3025, `dayTarget ${plan.dayTarget}`);
assert.equal(plan.remainingToday, plan.dayTarget);
});
// The Vest 50k Funded type: $1,500 and claim it all, then $3,000 on the room
// the claim freed — and every new day back at $1,500.
const vestFunded = {
profitTarget: 1500, consistency: 0, minDayPnL: 0, minTradingDays: 0, accountSize: 50000,
targetSameEquity: false, splitEnabled: true, payoutable: true, tradesEveryDay: true, combineCmeHolidays: false,
withdrawalStages: [{ profit: 3000, consistency: 0, minTradingDays: 0, dailyLossLimit: 3000 }],
};
const claimedBefore = [{ date: '2026-10-20', amount: 50000 }, { date: '2026-10-25', amount: -1500 }];
test('projectPerfectPath: a same-day type trades each projected day from the base stage', () => {
const opts = {
accountId: 7, cfg: vestFunded, cycleDailyPnL: [], fundTransactions: claimedBefore,
balance: 50000, realizedPnL: 0, payout: { withdrawalPct: 1 }, now: cdt('2026-10-27T12:00:00'),
};
const firstOf = (path, n) => path.slice(0, n).map((d) => [d.date, d.stage, d.withdrawal]);
// An earlier day's claim doesn't count: today and every day after it
// make $1,500 on stage 1 and claim it.
const fresh = projectPerfectPath({ ...opts, stageInputs: { dailyLossLimit: 1229, stagesSameDay: true, sameDayWithdrawals: 0 } });
assert.deepEqual(firstOf(fresh, 3), [['2026-10-27', 1, -1500], ['2026-10-28', 1, -1500], ['2026-10-29', 1, -1500]]);
assert.ok(fresh.every((d) => d.stage === 1));
// Claimed once already today: today's cycle is stage 2's $3,000, tomorrow $1,500 again.
const claimedToday = projectPerfectPath({ ...opts, stageInputs: { dailyLossLimit: 1229, stagesSameDay: true, sameDayWithdrawals: 1 } });
assert.deepEqual(firstOf(claimedToday, 2), [['2026-10-27', 2, -3000], ['2026-10-28', 1, -1500]]);
// Any other type counts every withdrawal, as it always has.
const lifetime = projectPerfectPath(opts);
assert.deepEqual(firstOf(lifetime, 3), [['2026-10-27', 2, -3000], ['2026-10-28', 3, -3000], ['2026-10-29', 4, -3000]]);
assert.deepEqual(projectPerfectPath({ ...opts, stageInputs: { dailyLossLimit: 1229, stagesSameDay: false, sameDayWithdrawals: 0 } }), lifetime);
});
test('endedCycleCompletion: a same-day type\'s ended cycle is judged on its day\'s base stage', () => {
// 2026-10-26 made $1,500 and the auto-claim took it before the reporter
// saw the final day; the account had claimed the day before too. The
// claim ended that day's stage 1, not a $3,000 stage 2.
const opts = {
accountId: 7, rawTradeDate: '2026-10-26',
fullDailyPnL: [{ date: '2026-10-25', pnl: 1500 }, { date: '2026-10-26', pnl: 1500 }],
cycleDailyPnL: [],
fundTransactions: [...claimedBefore, { date: '2026-10-26', amount: -1500 }],
lastFundDate: '2026-10-26',
endedCycleStart: { fundDate: '2026-10-25', includesDay: false },
rawRealizedPnL: 0, equityProfit: 0,
cfg: { ...vestFunded, stagesSameDay: true },
now: cdt('2026-10-26T12:00:00'),
};
// Its stage stays the payout's number among the account's withdrawals,
// which the master clears the entry by.
assert.deepEqual(endedCycleCompletion(opts), { date: '2026-10-26', stage: 2, drain: false, cycle: '2026-10-25' });
// Any other type: judged on stage 2's $3,000, as before.
assert.equal(endedCycleCompletion({ ...opts, cfg: vestFunded }), null);
});