Merge branch 'vest-fix-display' into vest-fixes
# Conflicts: # package.json # scripts/check-trading-logic.mjs
This commit is contained in:
+49
-29
@@ -282,6 +282,7 @@ function ObjRow({ passed, label, value }: { passed: boolean; label: ReactNode; v
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function EquityTooltip({ active, payload, label }: any) {
|
||||
if (!active || !payload?.length) return null;
|
||||
// Always read from the raw data point so fill-only series don't interfere
|
||||
@@ -487,9 +488,11 @@ export default function AccountPage() {
|
||||
}, [accountId]);
|
||||
|
||||
useEffect(() => {
|
||||
void load();
|
||||
// The first load from a timer too, like the polls: not setState
|
||||
// from the effect's own body.
|
||||
const first = setTimeout(() => { void load(); }, 0);
|
||||
const interval = setInterval(() => { void load(); }, 5000);
|
||||
return () => clearInterval(interval);
|
||||
return () => { clearTimeout(first); clearInterval(interval); };
|
||||
}, [load]);
|
||||
|
||||
if (!account) {
|
||||
@@ -513,9 +516,12 @@ export default function AccountPage() {
|
||||
|
||||
// The firm's hard daily loss limit, and the least a day must make to count
|
||||
// as a trading day — two different numbers; neither is set on every type.
|
||||
const dailyLossLimit = (cfg?.dailyLossLimit ?? 0) > 0 ? cfg!.dailyLossLimit! : null;
|
||||
// The limit and the min days are the current stage's, which may set its own.
|
||||
const stageLossLimit = account.effectiveDailyLossLimit ?? cfg?.dailyLossLimit ?? 0;
|
||||
const dailyLossLimit = stageLossLimit > 0 ? stageLossLimit : null;
|
||||
const minDayPnL = (cfg?.minDayPnL ?? 0) > 0 ? cfg!.minDayPnL : null;
|
||||
const daysPassed = cfg != null && account.daysTraded >= cfg.minTradingDays;
|
||||
const minTradingDays = account.effectiveMinTradingDays ?? cfg?.minTradingDays ?? 0;
|
||||
const daysPassed = cfg != null && account.daysTraded >= minTradingDays;
|
||||
// Dead when balance hits the liquidation floor — Tradovate reports its
|
||||
// (positive) floor; TopstepX floors are reconstructed MLLs, negative or
|
||||
// +$0.01 once locked at breakeven. 0 = no floor known.
|
||||
@@ -552,13 +558,19 @@ export default function AccountPage() {
|
||||
}
|
||||
const sortedEvents = [...allDates].sort().map((d) => eventsByDate.get(d)!);
|
||||
|
||||
// Running equity after each event, summed here rather than inside the
|
||||
// map below: a render callback mustn't reassign an outer variable.
|
||||
const runningAfter: number[] = [];
|
||||
let running = 0;
|
||||
for (const ev of sortedEvents) {
|
||||
if (ev.fundAmt !== undefined) running += ev.fundAmt; // withdrawals reduce equity
|
||||
if (ev.pnl !== undefined) running += ev.pnl;
|
||||
runningAfter.push(running);
|
||||
}
|
||||
const equityData = [
|
||||
{ label: '', equity: 0, pnl: 0, origin: true, pos: 0, neg: 0, isWithdrawal: false },
|
||||
...sortedEvents.map((ev) => {
|
||||
if (ev.fundAmt !== undefined) running += ev.fundAmt; // withdrawals reduce equity
|
||||
if (ev.pnl !== undefined) running += ev.pnl;
|
||||
const equity = Math.round(running * 100) / 100;
|
||||
...sortedEvents.map((ev, i) => {
|
||||
const equity = Math.round(runningAfter[i] * 100) / 100;
|
||||
return {
|
||||
label: fmtDate(ev.date),
|
||||
equity,
|
||||
@@ -597,7 +609,7 @@ export default function AccountPage() {
|
||||
// books exactly its plan, cycles complete, and the configured withdrawal
|
||||
// fires the day a cycle finishes. Same planner the trade path runs, so the
|
||||
// numbers (bumps and drain amounts included) are what would actually be
|
||||
// aimed for. Shown up to TWO payouts ahead.
|
||||
// aimed for. Shown up to TWO payout days ahead.
|
||||
const rawProjection = (cfg && !isDead) ? projectPerfectPath({
|
||||
accountId: account.id,
|
||||
cfg,
|
||||
@@ -605,7 +617,10 @@ export default function AccountPage() {
|
||||
fundTransactions,
|
||||
balance: account.amount,
|
||||
realizedPnL: account.realizedPnL,
|
||||
maxWithdrawals: 2,
|
||||
// A type whose stages reset each day: its state stage counts today's payouts.
|
||||
stageInputs: { stagesSameDay: cfg.stagesSameDay === true, sameDayWithdrawals: account.stage - 1 },
|
||||
// Such a type pays out once per stage on each of those days.
|
||||
maxWithdrawals: cfg.stagesSameDay === true ? 2 * (1 + cfg.withdrawalStages.length) : 2,
|
||||
payout: {
|
||||
withdrawalPct: cfg.withdrawalPct,
|
||||
payoutCap: cfg.payoutCap,
|
||||
@@ -621,23 +636,21 @@ export default function AccountPage() {
|
||||
: undefined,
|
||||
},
|
||||
}) : [];
|
||||
// Cut the projection at the second payout — days beyond it are noise here.
|
||||
const secondPayoutIdx = (() => {
|
||||
let seen = 0;
|
||||
for (let i = 0; i < rawProjection.length; i++) {
|
||||
const p = rawProjection[i];
|
||||
if (p.withdrawal !== undefined || p.payoutReady) {
|
||||
seen++;
|
||||
if (seen >= 2 || p.payoutReady) return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
})();
|
||||
const projection = secondPayoutIdx >= 0 ? rawProjection.slice(0, secondPayoutIdx + 1) : rawProjection;
|
||||
// Cut the projection after the second payout day — days beyond it are
|
||||
// noise here. The path's dates never go back, and a payoutReady entry is
|
||||
// always its last.
|
||||
const payoutDays = [...new Set(rawProjection.filter((p) => p.withdrawal !== undefined || p.payoutReady).map((p) => p.date))];
|
||||
const projection = payoutDays.length >= 2 ? rawProjection.filter((p) => p.date <= payoutDays[1]) : rawProjection;
|
||||
// A payout day is the cycle's FINAL trading day — it can carry both the
|
||||
// day's booked P&L and the withdrawal.
|
||||
const projPnlMap = new Map(projection.filter((p) => p.pnl !== 0).map((p) => [p.date, p.pnl]));
|
||||
const projFundMap = new Map(projection.filter((p) => p.withdrawal !== undefined).map((p) => [p.date, p.withdrawal!]));
|
||||
// day's booked P&L and the withdrawal. A same-day type's day can hold
|
||||
// several cycles: their P&L and withdrawals add up, and the day shows
|
||||
// the last stage it traded.
|
||||
const projPnlMap = new Map<string, number>();
|
||||
const projFundMap = new Map<string, number>();
|
||||
for (const p of projection) {
|
||||
if (p.pnl !== 0) projPnlMap.set(p.date, Math.round(((projPnlMap.get(p.date) ?? 0) + p.pnl) * 100) / 100);
|
||||
if (p.withdrawal !== undefined) projFundMap.set(p.date, Math.round(((projFundMap.get(p.date) ?? 0) + p.withdrawal) * 100) / 100);
|
||||
}
|
||||
const projStageMap = new Map(projection.map((p) => [p.date, p.stage]));
|
||||
/** Cumulative probability of the perfect path surviving through each projected day. */
|
||||
const projProbMap = new Map(projection.map((p) => [p.date, p.prob]));
|
||||
@@ -652,14 +665,20 @@ export default function AccountPage() {
|
||||
];
|
||||
const calendarMonths = [...new Set(allCalendarDates.map((d) => d.slice(0, 7)))].sort();
|
||||
|
||||
// Build stage map: stage increments after each withdrawal (negative fund txn)
|
||||
// Build stage map: stage increments after each withdrawal (negative fund txn).
|
||||
// A type whose stages reset each day starts every day on stage 1 and
|
||||
// trades its next stage after a payout that day, as the projected days
|
||||
// show — one fund row a day holds all of that day's payouts, so a day
|
||||
// with one reads stage 2.
|
||||
const sortedWithdrawalDates = fundTransactions
|
||||
.filter((f) => f.amount < 0)
|
||||
.map((f) => f.date)
|
||||
.sort();
|
||||
const stageMap = new Map<string, number>();
|
||||
for (const d of displayPnL) {
|
||||
const stage = sortedWithdrawalDates.filter((wd) => wd < d.date).length + 1;
|
||||
const stage = cfg?.stagesSameDay === true
|
||||
? sortedWithdrawalDates.filter((wd) => wd === d.date).length + 1
|
||||
: sortedWithdrawalDates.filter((wd) => wd < d.date).length + 1;
|
||||
stageMap.set(d.date, stage);
|
||||
}
|
||||
|
||||
@@ -739,7 +758,7 @@ export default function AccountPage() {
|
||||
<ObjRow
|
||||
passed={daysPassed}
|
||||
label="Trading Days"
|
||||
value={cfg ? `${account.daysTraded} of ${cfg.minTradingDays}` : '—'}
|
||||
value={cfg ? `${account.daysTraded} of ${minTradingDays}` : '—'}
|
||||
/>
|
||||
{dailyTarget != null && (
|
||||
<div className="flex items-center justify-between py-3 border-b border-slate-100 last:border-0">
|
||||
@@ -892,6 +911,7 @@ export default function AccountPage() {
|
||||
stroke={`url(#${strokeGradId})`}
|
||||
strokeWidth={2.5}
|
||||
fill="none"
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
dot={(props: any) => {
|
||||
if (props.payload?.origin) return <g key={props.key} />;
|
||||
return (
|
||||
|
||||
@@ -173,7 +173,7 @@ export async function GET() {
|
||||
};
|
||||
}
|
||||
}
|
||||
if (plan && !dayBooked && botStopsActive(cfg)) {
|
||||
if (plan && !dayBooked && botStopsActive(cfg, effective)) {
|
||||
const obf = obfuscationStopDollars(cfg, dailyPnL, plan, combined.tradeDate, plan.remainingToday);
|
||||
// The reserve grows with the position (dllReserve): at
|
||||
// the size the next leg would trade, where it's known.
|
||||
@@ -276,7 +276,7 @@ export async function GET() {
|
||||
// An account the bot puts stops on whose day booked red via
|
||||
// its own stop — the personal DLL. Firm DLL lockouts show
|
||||
// via !active instead.
|
||||
personalDll: !!cfg && botStopsActive(cfg)
|
||||
personalDll: !!cfg && !!effective && botStopsActive(cfg, effective)
|
||||
&& acc.active && !client.positions[acc.id]
|
||||
&& combined.realizedPnL <= -25,
|
||||
positionDirection: client.positions[acc.id]
|
||||
@@ -296,6 +296,8 @@ export async function GET() {
|
||||
lastStand: lastStandNext,
|
||||
remainingToday,
|
||||
effectiveProfitTarget,
|
||||
effectiveDailyLossLimit: effective?.dailyLossLimit ?? null,
|
||||
effectiveMinTradingDays: effective?.minTradingDays ?? null,
|
||||
dailyPnL,
|
||||
fullDailyPnL: combineCmeSessions(
|
||||
cfg?.combineCmeHolidays === true,
|
||||
|
||||
+1
-1
@@ -453,7 +453,7 @@ function AccountRow({ account, firm, hideDead, privacy, onAssigned, onStateChang
|
||||
<AccountTypeCell account={account} firm={firm} onAssigned={onAssigned} />
|
||||
</td>
|
||||
<td className="px-4 py-3 text-slate-600 tabular-nums">
|
||||
{account.daysTraded} / {cfg?.minTradingDays ?? '—'}
|
||||
{account.daysTraded} / {account.effectiveMinTradingDays ?? cfg?.minTradingDays ?? '—'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-slate-600 tabular-nums">
|
||||
<div className="flex flex-col">
|
||||
|
||||
Reference in New Issue
Block a user