// Checks for the pure trading math in lib/trading-logic.ts. // // npm run check // // .mjs on purpose: Next's tsconfig includes every .ts file, and a .ts test // importing another .ts by extension would trip the build's type check. Node // strips the types from lib/trading-logic.ts at import time (it has no // imports of its own). import { test } from 'node:test'; import assert from 'node:assert/strict'; import { percentFee, takeProfitPlan, 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, dllReserve, DLL_SLIPPAGE_FRACTION, maxLossRoom, lastStand, breachDistance, LAST_STAND_FRACTION, planLegSize, rankForTarget, currentTradeDateCT, } from '../lib/trading-logic.ts'; // Deterministic PRNG so a failure reproduces. function rng(seed) { let s = seed >>> 0; return () => { s = (s + 0x6d2b79f5) >>> 0; let t = s; t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; } // ── parity with the pre-refactor inline code ──────────────────────────────── /** The take-profit exactly as auto-trade.ts computed it before takeProfitPlan. */ function oldTakeProfit(side, entry, leg, pv, contracts, tickSize, entryCommission) { const totalCommission = entryCommission * 2; const grossTarget = leg + totalCommission; const targetPoints = grossTarget / (pv * contracts); const ticks = Math.ceil(targetPoints / tickSize); const exitPrice = side === 'Buy' ? entry + ticks * tickSize : entry - ticks * tickSize; return { exitPrice, ticks, totalCommission, grossTarget }; } test('takeProfitPlan reproduces the old formula when fees are measured', () => { const r = rng(1); for (let i = 0; i < 2000; i++) { const side = r() < 0.5 ? 'Buy' : 'Sell'; const [pv, tick] = [[20, 0.25], [2, 0.25], [100, 0.1], [1000, 0.01], [50, 0.25]][Math.floor(r() * 5)]; const entry = Math.round((1000 + r() * 20000) / tick) * tick; const leg = Math.round(5 + r() * 2500); const contracts = 1 + Math.floor(r() * 4); const fee = Math.round(r() * 1000) / 100; const got = takeProfitPlan({ side, entry, leg, pointValue: pv, contracts, tickSize: tick, commissionPct: 0, entryCommission: fee }); const want = oldTakeProfit(side, entry, leg, pv, contracts, tick, fee); assert.equal(got.exitPrice, want.exitPrice); assert.equal(got.ticks, want.ticks); assert.equal(got.totalCommission, want.totalCommission); assert.equal(got.grossTarget, want.grossTarget); } }); /** obfuscationStopDollars exactly as it lived in auto-trade.ts. */ function oldObfuscation(cfg, dailyPnL, plan, tradeDate, legAmount) { if (!cfg.firstDayStop && !cfg.minDayStop) return Infinity; const prior = dailyPnL.filter((d) => d.date < tradeDate && d.pnl !== 0).sort((a, b) => a.date.localeCompare(b.date)); if (prior.length > 0 && prior[prior.length - 1].pnl < 0) return Infinity; let stop = Infinity; const realizedToday = Math.round((plan.dayTarget - plan.remainingToday) * 100) / 100; if (cfg.firstDayStop && prior.length === 0) stop = Math.max(0, 1000 + realizedToday); if (cfg.minDayStop && plan.dayTarget <= 1700) stop = Math.min(stop, 2 * legAmount); return stop; } test('obfuscationStopDollars is unchanged by the move', () => { const r = rng(2); for (let i = 0; i < 1000; i++) { const cfg = { firstDayStop: r() < 0.5, minDayStop: r() < 0.5 }; const days = Math.floor(r() * 4); const dailyPnL = Array.from({ length: days }, (_, d) => ({ date: `2026-09-0${d + 1}`, pnl: Math.round((r() - 0.4) * 2000) })); const dayTarget = Math.round(r() * 3000); const plan = { dayTarget, remainingToday: Math.round(dayTarget * r()), path: 'normal_day', splitAllowed: true }; const leg = Math.round(100 + r() * 1500); assert.equal(obfuscationStopDollars(cfg, dailyPnL, plan, '2026-09-08', leg), oldObfuscation(cfg, dailyPnL, plan, '2026-09-08', leg)); } }); /** isInNoTradeWindow as it was, with the clock and settings passed in. */ function oldNoTrade(now, mode, jitter) { const centralParts = new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: 'numeric', hour12: false, timeZone: 'America/Chicago' }).formatToParts(now); const hour = parseInt(centralParts.find((p) => p.type === 'hour').value, 10); const minute = parseInt(centralParts.find((p) => p.type === 'minute').value, 10); const day = new Intl.DateTimeFormat('en-US', { weekday: 'short', timeZone: 'America/Chicago' }).format(now); const timeMinutes = hour * 60 + minute; if (mode === 'equity_hours') { if (day === 'Sat' || day === 'Sun') return true; const open = 8 * 60 + 35 + jitter; const close = 14 * 60 + 55; return timeMinutes < open || timeMinutes >= close; } if (day === 'Sat') return true; const cmeOpen = 17 * 60 + 5 + jitter; if (day === 'Sun') return timeMinutes < cmeOpen; const cmeClose = 14 * 60 + 55; if (day === 'Fri' && timeMinutes >= cmeClose) return true; if (timeMinutes >= cmeClose && timeMinutes < cmeOpen) return true; return false; } function oldAfterClose(now) { const parts = new Intl.DateTimeFormat('en-US', { weekday: 'short', hour: 'numeric', minute: 'numeric', hour12: false, timeZone: 'America/Chicago' }).formatToParts(now); const get = (type) => parts.find((p) => p.type === type)?.value ?? ''; if (get('weekday') === 'Sat' || get('weekday') === 'Sun') return false; const minutes = (parseInt(get('hour'), 10) % 24) * 60 + parseInt(get('minute'), 10); return minutes >= 14 * 60 + 55 && minutes < 17 * 60; } test('CME trading hours are unchanged, across both DST regimes', () => { for (const start of [Date.UTC(2026, 8, 20), Date.UTC(2026, 0, 11)]) { // a September and a January week for (let m = 0; m < 7 * 24 * 60; m += 7) { const now = new Date(start + m * 60_000); for (const mode of ['full_cme', 'equity_hours']) { for (const jitter of [1, 13, 20]) { assert.equal(isNoTradeTime(now, { everyDay: false, mode, jitterMin: jitter }), oldNoTrade(now, mode, jitter), `${now.toISOString()} ${mode} ${jitter}`); } } assert.equal(isPastSessionClose(now, false), oldAfterClose(now), now.toISOString()); } } }); // ── every-day hours ────────────────────────────────────────────────────────── const ct = (iso, offsetHours) => new Date(Date.parse(`${iso}Z`) + offsetHours * 3600_000); const cdt = (iso) => ct(iso, 5); // Central daylight: UTC−5 const cst = (iso) => ct(iso, 6); // Central standard: UTC−6 test('every-day firms close only around the roll (CDT)', () => { const o = { everyDay: true, mode: 'full_cme', jitterMin: 7 }; assert.equal(isNoTradeTime(cdt('2026-09-26T12:00:00'), o), false); // Saturday noon assert.equal(isNoTradeTime(cdt('2026-09-27T09:00:00'), o), false); // Sunday morning assert.equal(isNoTradeTime(cdt('2026-09-28T16:54:00'), o), false); assert.equal(isNoTradeTime(cdt('2026-09-28T16:55:00'), o), true); assert.equal(isNoTradeTime(cdt('2026-09-28T17:11:00'), o), true); assert.equal(isNoTradeTime(cdt('2026-09-28T17:12:00'), o), false); assert.equal(isPastSessionClose(cdt('2026-09-26T16:56:00'), true), true); assert.equal(isPastSessionClose(cdt('2026-09-26T17:01:00'), true), false); }); test('every-day firms close from 3:55 PM in CST, covering the 4 PM firm reset', () => { const o = { everyDay: true, mode: 'equity_hours', jitterMin: 1 }; assert.equal(isNoTradeTime(cst('2027-01-12T15:54:00'), o), false); assert.equal(isNoTradeTime(cst('2027-01-12T15:55:00'), o), true); assert.equal(isNoTradeTime(cst('2027-01-12T16:30:00'), o), true); assert.equal(isNoTradeTime(cst('2027-01-12T17:05:00'), o), true); assert.equal(isNoTradeTime(cst('2027-01-12T17:06:00'), o), false); assert.equal(isNoTradeTime(cst('2027-01-16T12:00:00'), o), false); // Saturday assert.equal(isPastSessionClose(cst('2027-01-12T16:10:00'), true), true); }); // ── fees ───────────────────────────────────────────────────────────────────── test('percentFee rounds each side up to the cent', () => { assert.equal(percentFee(0.04, 60000, 1, 1), 24); assert.equal(percentFee(0.04, 100000, 0.2, 1), 8); assert.equal(percentFee(0.04, 123.45, 0.001, 7), 0.01); // 0.00035 → a cent assert.equal(percentFee(0, 60000, 1, 1), 0); }); test('percent-fee take-profits net at least the leg, and one tick less does not', () => { const r = rng(3); for (let i = 0; i < 3000; i++) { const side = r() < 0.5 ? 'Buy' : 'Sell'; const tick = [0.01, 0.1, 0.5, 1][Math.floor(r() * 4)]; const entry = Math.round((50 + r() * 100000) / tick) * tick; const step = [0.001, 0.01, 1][Math.floor(r() * 3)]; const contracts = 1 + Math.floor(r() * 400); const pct = [0.04, 0.02, 0.1][Math.floor(r() * 3)]; const leg = Math.round(5 + r() * 2000); const entryFee = percentFee(pct, entry, step, contracts); const tp = takeProfitPlan({ side, entry, leg, pointValue: step, contracts, tickSize: tick, commissionPct: pct, entryCommission: entryFee }); const k = step * contracts; const dir = side === 'Buy' ? 1 : -1; const net = (ticks) => { const x = entry + dir * ticks * tick; return ticks * tick * k - entryFee - percentFee(pct, x, step, contracts); }; assert.ok(net(tp.ticks) >= leg - 1e-6, `short of the leg: ${JSON.stringify({ side, entry, tick, step, contracts, pct, leg, tp })}`); if (tp.ticks > 1) assert.ok(net(tp.ticks - 1) < leg + 0.02, `a tick further than needed: ${JSON.stringify(tp)}`); assert.equal(tp.totalCommission, Math.round((entryFee + tp.exitFee) * 100) / 100); } }); // ── daily loss limit ───────────────────────────────────────────────────────── test('dllRoom: snapshot, fallback, green-day cap, no limit', () => { assert.equal(dllRoom({ dailyLossLimit: 0, closedBalance: 50000, realizedToday: -900 }), Infinity); assert.equal(dllRoom({ dailyLossLimit: 1500, closedBalance: 49600, dayStartBalance: 50000, realizedToday: 0 }), 1100); assert.equal(dllRoom({ dailyLossLimit: 1500, closedBalance: 50600, dayStartBalance: 50000, realizedToday: 0 }), 1500); assert.equal(dllRoom({ dailyLossLimit: 1000, closedBalance: 50000, dayStartBalance: null, realizedToday: -200 }), 800); assert.equal(dllRoom({ dailyLossLimit: 1000, closedBalance: 50000, realizedToday: 350 }), 1000); }); test('the worked futures example: $1,000 limit, down $200, NQ at $2.14/side', () => { const room = dllRoom({ dailyLossLimit: 1000, closedBalance: 49800, realizedToday: -200 }); const d = dllStopDollars({ room, dailyLossLimit: 1000, entryFee: 2.14, commissionPct: 0 }); const ticks = stopTicks({ obfuscationDollars: Infinity, dllDollars: d, pointValue: 20, contracts: 1, tickSize: 0.25 }); assert.equal(ticks, 139); const loss = ticks * 0.25 * 20 + 2 * 2.14; assert.ok(-200 - loss > -1000 + 100 - 1e-9); }); test('stopTicks: obfuscation rounds out, the daily-loss stop rounds in, the tighter wins', () => { const base = { pointValue: 20, contracts: 1, tickSize: 0.25 }; assert.equal(stopTicks({ ...base, obfuscationDollars: Infinity, dllDollars: Infinity }), null); assert.equal(stopTicks({ ...base, obfuscationDollars: 0, dllDollars: Infinity }), null); assert.equal(stopTicks({ ...base, obfuscationDollars: 1001, dllDollars: Infinity }), 201); assert.equal(stopTicks({ ...base, obfuscationDollars: Infinity, dllDollars: 1000 }), 200); assert.equal(stopTicks({ ...base, obfuscationDollars: Infinity, dllDollars: 999.99 }), 199); assert.equal(stopTicks({ ...base, obfuscationDollars: 600, dllDollars: 1000 }), 120); assert.equal(stopTicks({ ...base, obfuscationDollars: 1600, dllDollars: 1000 }), 200); assert.equal(stopTicks({ ...base, obfuscationDollars: Infinity, dllDollars: 3 }), 0); assert.equal(stopTicks({ ...base, obfuscationDollars: Infinity, dllDollars: -40 }), 0); }); test('worst case at the stop, both fees paid, never spends past room − reserve', () => { const r = rng(4); for (let i = 0; i < 20000; i++) { const side = r() < 0.5 ? 'Buy' : 'Sell'; const pctFirm = r() < 0.6; const pct = pctFirm ? [0.04, 0.1][Math.floor(r() * 2)] : 0; const tick = pctFirm ? [0.01, 0.1, 1][Math.floor(r() * 3)] : 0.25; const pv = pctFirm ? [0.001, 0.01, 1][Math.floor(r() * 3)] : [2, 20, 50][Math.floor(r() * 3)]; const contracts = pctFirm ? 1 + Math.floor(r() * 500) : 1 + Math.floor(r() * 3); const entry = Math.round((100 + r() * 90000) / tick) * tick; const dll = [300, 750, 1500, 3000][Math.floor(r() * 4)]; const room = dllRoom({ dailyLossLimit: dll, closedBalance: 50000, dayStartBalance: 50000 + Math.round((r() - 0.3) * dll), realizedToday: 0 }); const entryFee = pctFirm ? percentFee(pct, entry, pv, contracts) : Math.round(r() * 500) / 100 * contracts; const d = dllStopDollars({ room, dailyLossLimit: dll, entryFee, commissionPct: pct }); const ticks = stopTicks({ obfuscationDollars: Infinity, dllDollars: d, pointValue: pv, contracts, tickSize: tick }); if (ticks === 0) continue; // flattened instead const stopPrice = side === 'Buy' ? entry - ticks * tick : entry + ticks * tick; const exitFee = pctFirm ? percentFee(pct, stopPrice, pv, contracts) : entryFee; const loss = ticks * tick * pv * contracts + entryFee + exitFee; assert.ok(loss <= room - DLL_RESERVE_FRACTION * dll + 1e-6, JSON.stringify({ side, pct, tick, pv, contracts, entry, dll, room, ticks, loss })); } }); test('dllViable and botStopsActive', () => { assert.equal(dllViable(Infinity, 0, 0), true); assert.equal(dllViable(1500, 1500, 20), true); assert.equal(dllViable(150 + DLL_MIN_STOP + 20, 1500, 20), true); assert.equal(dllViable(150 + DLL_MIN_STOP + 19.99, 1500, 20), false); assert.equal(botStopsActive({ firstDayStop: false, minDayStop: false }), false); assert.equal(botStopsActive({ firstDayStop: false, minDayStop: false, dailyLossLimit: 1500 }), true); assert.equal(botStopsActive({ firstDayStop: true, minDayStop: false, dailyLossLimit: 0 }), true); }); // ── sizing ─────────────────────────────────────────────────────────────────── const btcSigma = 0.03 / Math.sqrt(24); // 3%/day test('volatility sizing hits the target hold, and the example table', () => { const stop = () => 1350; const at = (hours) => sizeNotional({ leg: 600, stopDollarsAt: stop, volatilityPerHour: btcSigma, targetHoldHours: hours, commissionPct: 0, maxNotional: 0 }); for (const [hours, notional] of [[54, 20000], [24, 30000], [6, 60000]]) { const s = at(hours); assert.ok(Math.abs(s.notional - notional) / notional < 0.001, `${hours}h → ${s.notional}`); assert.ok(Math.abs(s.predictedHoldHours - hours) / hours < 0.001); assert.ok(Math.abs(s.winProbability - 1350 / 1950) < 1e-9); } }); test('sizing: fees feed back, the cap binds, financing only raises N, defaults', () => { const base = { leg: 600, volatilityPerHour: btcSigma, targetHoldHours: 6, commissionPct: 0.04, spreadFraction: 0.0002, maxNotional: 0 }; // Daily-loss stop shrinks as fees grow with N. const stopDollarsAt = (n) => dllStopDollars({ room: 1500, dailyLossLimit: 1500, entryFee: 0.0004 * n, commissionPct: 0.04 }); const s = sizeNotional({ ...base, stopDollarsAt }); const a = 600 + 2 * 0.0004 * s.notional; const b = stopDollarsAt(s.notional); assert.ok(Math.abs(s.predictedHoldHours - 6) < 0.05, `hold ${s.predictedHoldHours}`); assert.ok(Math.abs(s.winProbability - b / (a + b)) < 1e-9); const capped = sizeNotional({ ...base, stopDollarsAt, maxNotional: 25000 }); assert.equal(capped.notional, 25000); assert.ok(capped.predictedHoldHours > 6); const financed = sizeNotional({ ...base, stopDollarsAt, targetHoldHours: 200, financingPctPerDay: 0.033 }); const unfinanced = sizeNotional({ ...base, stopDollarsAt, targetHoldHours: 200 }); assert.ok(financed.notional > unfinanced.notional); // No target hold = 12 h. const dflt = sizeNotional({ ...base, targetHoldHours: 0, stopDollarsAt }); assert.ok(Math.abs(dflt.predictedHoldHours - DEFAULT_TARGET_HOLD_HOURS) < 0.1, `hold ${dflt.predictedHoldHours}`); // A stopless leg is sized as if the stop sat as far as the target. const stopless = sizeNotional({ ...base, stopDollarsAt: () => Infinity }); assert.ok(Math.abs(stopless.predictedHoldHours - 6) < 0.05); assert.equal(stopless.winProbability, null); // No volatility, no size. assert.equal(sizeNotional({ ...base, volatilityPerHour: undefined, stopDollarsAt }), null); }); test('random-walk simulation agrees with the predicted hold and win rate', () => { const r = rng(5); const notional = 60000; const A = 600; const B = 1350; const sigma = btcSigma; const dtHours = 1 / 60; // one-minute steps const stepDollars = notional * sigma * Math.sqrt(dtHours); const predicted = (A / notional) * (B / notional) / (sigma * sigma); let wins = 0; let totalHours = 0; let winHours = 0; const paths = 3000; for (let p = 0; p < paths; p++) { let x = 0; let steps = 0; while (x < A && x > -B) { x += r() < 0.5 ? stepDollars : -stepDollars; steps++; } if (x >= A) { wins++; winHours += steps * dtHours; } totalHours += steps * dtHours; } const meanHold = totalHours / paths; assert.ok(Math.abs(meanHold - predicted) / predicted < 0.1, `mean hold ${meanHold} vs predicted ${predicted}`); assert.ok(Math.abs(wins / paths - B / (A + B)) < 0.03, `win rate ${wins / paths}`); // Winners take longer than the average leg: (A² + 2AB) / 3σ$² — what // expectedSwapToWin charges swap over. const predictedWin = (A * A + 2 * A * B) / (3 * (notional * sigma) ** 2); assert.ok(Math.abs(winHours / wins - predictedWin) / predictedWin < 0.1, `winners ${winHours / wins}h vs ${predictedWin}h`); }); test('expected swap to win: a fixed point over the winners\' hold, zero without swap, capped', () => { const sigma = 0.022 / Math.sqrt(24); // ETH over a week, ~2.2%/day const o = { distance: 12400, stopDollars: 2300, notional: 200000, volatilityPerHour: sigma, financingPctPerDay: 0.033 }; const swap = expectedSwapToWin(o); const a = o.distance + swap; const b = o.stopDollars; const winHours = (a * a + 2 * a * b) / (3 * (o.notional * sigma) ** 2); assert.ok(Math.abs(swap - 0.00033 * o.notional * (winHours / 24)) < 0.02, `swap ${swap} over ${winHours}h`); assert.ok(swap > 200 && swap < 400, `swap ${swap}`); // Quieter means a longer hold and more swap; none without a rate or σ. assert.ok(expectedSwapToWin({ ...o, volatilityPerHour: sigma * 0.6 }) > swap); assert.equal(expectedSwapToWin({ ...o, financingPctPerDay: 0 }), 0); assert.equal(expectedSwapToWin({ ...o, volatilityPerHour: undefined }), 0); // Stopless reads as B = A: a longer walk than with the stop. assert.ok(expectedSwapToWin({ ...o, stopDollars: Infinity }) > swap); // A leg that can barely move is capped at a quarter of its distance. assert.equal(expectedSwapToWin({ ...o, volatilityPerHour: sigma / 50 }), 3100); }); test('the notional cap: what the available funds can margin at the symbol\'s rate', () => { assert.equal(notionalCap({ marginRate: 0.2, availableFunds: 100000 }), MARGIN_HEADROOM * 500000); assert.equal(notionalCap({ marginRate: 0.5, availableFunds: 99757.39 }), MARGIN_HEADROOM * 99757.39 * 2); assert.equal(notionalCap({ marginRate: 0.2, availableFunds: undefined }), 0); assert.equal(notionalCap({ availableFunds: 100000 }), 0); }); test('the daily-loss reserve grows with the position past 10% of the limit', () => { assert.equal(dllReserve(3000), 300); assert.equal(dllReserve(3000, 200000), 300); assert.equal(dllReserve(3000, 485000), 485000 * DLL_SLIPPAGE_FRACTION); const at = (notional) => dllStopDollars({ room: 3000, dailyLossLimit: 3000, entryFee: 0.0004 * notional, commissionPct: 0.04, notional }); // Below $300k the flat 10% binds; above, 0.1% of notional does. assert.ok(Math.abs(at(200000) - (3000 - 300 - 160 - 0.01) / 1.0004) < 1e-9); assert.ok(Math.abs(at(485000) - (3000 - 485 - 388 - 0.01) / 1.0004) < 1e-9); // No notional given: the old flat reserve. assert.equal(dllStopDollars({ room: 3000, dailyLossLimit: 3000, entryFee: 0, commissionPct: 0 }), 2699.99); }); test('max-drawdown room: the static floor, the broker floor, the higher', () => { assert.equal(maxLossRoom({ balance: 99757.39, accountSize: 100000, maxLoss: 6000 }), 5757.39); assert.equal(maxLossRoom({ balance: 99757.39, accountSize: 100000, maxLoss: -6000 }), 5757.39); assert.equal(maxLossRoom({ balance: 51000, accountSize: 50000, maxLoss: 0, autoLiqThreshold: 48500 }), 2500); assert.equal(maxLossRoom({ balance: 51000, accountSize: 50000, maxLoss: 2000, autoLiqThreshold: 49000 }), 2000); assert.equal(maxLossRoom({ balance: 99000, accountSize: 100000, maxLoss: 0 }), null); }); test('last stand: no daily-loss stop once hitting the daily limit would leave 15% of it or less', () => { // A full day's $3,000 of room. const at = (maxRoom) => lastStand({ dllRoom: 3000, dailyLossLimit: 3000, maxLossRoom: maxRoom }); assert.equal(LAST_STAND_FRACTION, 0.15); assert.equal(at(5757.39), false, 'a fresh 1-Step keeps its stop'); assert.equal(at(3000 * 1.15 + 1), false, 'just over 15% left: keep the stop'); assert.equal(at(3000 * 1.15), true, '15% left: last stand'); assert.equal(at(3294.70), true, 'one stopped day in: $294.70 left, 9.8% — last stand'); assert.equal(at(800), true, 'floor nearer than the limit: last stand'); assert.equal(at(null), false, 'no floor known'); // Measured against what's left of the day: $1,000 of room, $1,200 to the floor. assert.equal(lastStand({ dllRoom: 1000, dailyLossLimit: 3000, maxLossRoom: 1200 }), false); assert.equal(lastStand({ dllRoom: 1000, dailyLossLimit: 3000, maxLossRoom: 1150 }), true); assert.equal(lastStand({ dllRoom: Infinity, dailyLossLimit: 0, maxLossRoom: 500 }), false, 'no daily limit, no daily stop to drop'); // The far side of the walk is the nearer floor, net of the entry fee. assert.equal(breachDistance({ dllRoom: 3000, maxLossRoom: 2900, entryFee: 120 }), 2780); assert.equal(breachDistance({ dllRoom: 2757.39, maxLossRoom: null, entryFee: 0 }), 2757.39); }); test('planLegSize: the stop, a last stand, the margin cap and the cost, as fireLeg trades', () => { const eth = { stepNotional: 26.86, volatilityPerHour: 0.022 / Math.sqrt(24), spreadFraction: 0.00004, marginRate: 0.2 }; const base = { leg: 12239, contract: eth, commissionPct: 0.04, targetHoldHours: 12, financingPctPerDay: 0.033, dailyLossLimit: 3000, dllRoom: 2757.39, obfuscationDollars: Infinity, availableFunds: 99757.39, }; const fresh = planLegSize({ ...base, maxLossRoom: 5757.39 }); assert.equal(fresh.lastStand, false); assert.ok(Math.abs(fresh.sizing.predictedHoldHours - 12) < 0.1, `hold ${fresh.sizing.predictedHoldHours}`); const n = fresh.sizing.notional; assert.ok(Math.abs(fresh.walkAt(n) - dllStopDollars({ room: 2757.39, dailyLossLimit: 3000, entryFee: 0.0004 * n, commissionPct: 0.04, notional: n })) < 1e-9); const expectCost = n * (0.00004 + 0.0008) + 0.00033 * n * (fresh.sizing.predictedHoldHours / 24); assert.ok(Math.abs(fresh.cost - expectCost) < 1e-6); // Two days stopped out: a stop-out would leave ~$300 above the floor. const stand = planLegSize({ ...base, dllRoom: 3000, maxLossRoom: 3000 }); assert.equal(stand.lastStand, true); assert.equal(stand.walkAt(300000), 3000 - 120); assert.ok(stand.sizing.winProbability > fresh.sizing.winProbability, 'the whole room, no reserve: a better chance'); // An obfuscation stop still counts on a last stand. assert.equal(planLegSize({ ...base, dllRoom: 3000, maxLossRoom: 3000, obfuscationDollars: 1000 }).walkAt(300000), 1000); // A 2:1 stock can't reach 12 h in a quiet market: the cap binds. const stock = planLegSize({ ...base, maxLossRoom: 5757.39, contract: { ...eth, marginRate: 0.5, volatilityPerHour: 0.0076 / Math.sqrt(24) } }); assert.ok(Math.abs(stock.sizing.notional - stock.cap) < 1e-6); assert.ok(stock.sizing.predictedHoldHours > 12); // Futures (no stepNotional) aren't sized by notional. assert.equal(planLegSize({ ...base, maxLossRoom: null, contract: {} }).sizing, null); }); test('rankForTarget: reach the target hold first, cheapest; else quickest', () => { const order = rankForTarget([ { holdHours: 12, cost: 330 }, // ETH { holdHours: 12.1, cost: 300 }, // BTC — within the 2% tolerance { holdHours: 40, cost: 250 }, // XYZ100, capped at 2:1 { holdHours: 20, cost: 400 }, // SP500, capped ], 12); assert.deepEqual(order, [1, 0, 3, 2]); assert.deepEqual(rankForTarget([{ holdHours: 30, cost: 1 }, { holdHours: 25, cost: 9 }], 12), [1, 0]); assert.deepEqual(rankForTarget([], 12), []); }); test('sizing carries the swap reserve into the take-profit distance', () => { const sigma = 0.0124 / Math.sqrt(24); const stopDollarsAt = (n) => dllStopDollars({ room: 2760.15, dailyLossLimit: 3000, entryFee: 0.0004 * n, commissionPct: 0.04 }); const base = { leg: 12239, stopDollarsAt, volatilityPerHour: sigma, targetHoldHours: 12, commissionPct: 0.04, spreadFraction: 0.0001 }; const withSwap = sizeNotional({ ...base, financingPctPerDay: 0.033, maxNotional: 200000 }); const noSwap = sizeNotional({ ...base, financingPctPerDay: 0, maxNotional: 200000 }); assert.equal(noSwap.swapReserve, 0); assert.ok(withSwap.swapReserve > 500, `swap ${withSwap.swapReserve}`); const a = 12239 + 2 * 0.0004 * 200000 + withSwap.swapReserve; const b = stopDollarsAt(200000); assert.ok(Math.abs(withSwap.predictedHoldHours - (a / 200000) * (b / 200000) / (sigma * sigma)) < 1e-6); assert.ok(withSwap.predictedHoldHours > noSwap.predictedHoldHours); assert.ok(withSwap.winProbability < noSwap.winProbability); }); // ── every-day calendar ─────────────────────────────────────────────────────── test('nextTradingDate and effectiveCombine', () => { assert.equal(nextTradingDate('2026-09-25'), '2026-09-28'); assert.equal(nextTradingDate('2026-09-25', true), '2026-09-26'); assert.equal(nextTradingDate('2026-12-24', true), '2026-12-25'); assert.equal(effectiveCombine({ combineCmeHolidays: true }), true); assert.equal(effectiveCombine({ combineCmeHolidays: true, tradesEveryDay: true }), false); }); const projCfg = { profitTarget: 3000, consistency: 0, minDayPnL: 0, minTradingDays: 5, accountSize: 50000, targetSameEquity: false, withdrawalStages: [], splitEnabled: true, }; test('every-day projections use consecutive dates; CME ones still skip weekends', () => { const now = cdt('2026-09-26T12:00:00'); // Saturday noon → trade date Saturday const common = { accountId: 7, cycleDailyPnL: [], fundTransactions: [], balance: 50000, realizedPnL: 0, now }; const every = projectPerfectPath({ ...common, cfg: { ...projCfg, tradesEveryDay: true } }); const cme = projectPerfectPath({ ...common, cfg: projCfg }); assert.ok(every.length > 1); for (let i = 1; i < every.length; i++) assert.equal(every[i].date, nextTradingDate(every[i - 1].date, true)); for (const d of cme) { const dow = new Date(`${d.date}T00:00:00Z`).getUTCDay(); assert.ok(dow !== 0 && dow !== 6, `CME projection landed on ${d.date}`); } // Saturday itself is booked (silently) for the every-day firm: its first // projected day is Sunday, the CME one's is Monday. assert.equal(every[0].date, '2026-09-27'); assert.equal(cme[0].date, '2026-09-28'); }); test('an every-day firm can finish its cycle on a Saturday', () => { const now = cdt('2026-09-26T12:00:00'); const cycleDailyPnL = [ { date: '2026-09-21', pnl: 700 }, { date: '2026-09-22', pnl: 700 }, { date: '2026-09-23', pnl: 700 }, { date: '2026-09-24', pnl: 700 }, ]; const opts = { accountId: 7, rawTradeDate: '2026-09-26', cycleDailyPnL, rawRealizedPnL: 400, combineCmeHolidays: false, effective: { profitTarget: 3000, consistency: 0, minTradingDays: 5, minDayPnL: 0, drain: false }, equityProfit: 3200, splitEnabled: true, now, }; assert.equal(cycleCompletion({ ...opts, tradesEveryDay: true }), 'today'); assert.equal(cycleCompletion(opts), 'none'); }); // ── DXTrade helpers (lib/dxtrade-helpers.ts) ───────────────────────────────── import { dxAccountId, accountDisplayName, accountCodeOf, encodeContractName, decodeContractName, roundToStep, lastUtcReset, ctTradeDayStart, firmDayOf, parseDurationMs, ciNum, signedFundValue, inTradingHours, realizedVolPerHour, scoreCandidate, candidateRejection, MIN_VOL_CANDLES, parseTransfers, marginRateOf, } from '../lib/dxtrade-helpers.ts'; test('transfers: a close\'s SETTLEMENT and COMMISSION share a code and both count', () => { // As Tradeify247 answered for a flatten. const body = { cashTransfers: [ { account: 'default:1S100K2166398', transferCode: '2354051:1417097', transactionTime: '2026-09-26T22:33:39.615Z', cashTransactions: [ { account: 'default:1S100K2166398', transactionCode: '2354051:1417097', type: 'COMMISSION', value: -79.95, transactionTime: '2026-09-26T22:33:39.615Z', orderCode: 'x:af-1-e' }, ] }, { account: 'default:1S100K2166398', transferCode: '2354051:1417102', transactionTime: '2026-09-26T22:33:42.197Z', cashTransactions: [ { account: 'default:1S100K2166398', transactionCode: '2354051:1417102', type: 'SETTLEMENT', value: -5.43, transactionTime: '2026-09-26T22:33:42.197Z', orderCode: 'x:af-2-x' }, { account: 'default:1S100K2166398', transactionCode: '2354051:1417102', type: 'COMMISSION', value: -79.95, transactionTime: '2026-09-26T22:33:42.197Z', orderCode: 'x:af-2-x' }, ] }, ] }; const txns = parseTransfers(body); assert.equal(txns.length, 3); const byKey = new Map(txns.map((t) => [t.key, t])); assert.equal(byKey.size, 3, 'every transaction keeps its own key'); assert.equal(Math.round([...byKey.values()].reduce((s, t) => s + t.value, 0) * 100) / 100, -165.33); // The same answer again (the next sync, another page) keys identically. assert.deepEqual(parseTransfers(body).map((t) => t.key), txns.map((t) => t.key)); // Two of one type in one transfer stay apart too. const twice = { cashTransfers: [{ transferCode: 't', cashTransactions: [ { account: 'default:A', transactionCode: 'c', type: 'COMMISSION', value: -1, transactionTime: '2026-09-26T00:00:00Z' }, { account: 'default:A', transactionCode: 'c', type: 'COMMISSION', value: -2, transactionTime: '2026-09-26T00:00:00Z' }, ] }] }; assert.equal(new Set(parseTransfers(twice).map((t) => t.key)).size, 2); }); test('margin rates: flat and tiered, the steepest tier, junk ignored', () => { assert.equal(marginRateOf({ marginRate: { rateType: 'FLAT', tiers: [{ endVolume: 'Infinity', value: 0.2 }] } }), 0.2); assert.equal(marginRateOf({ marginRate: { rateType: 'FLAT', tiers: [{ endVolume: 'Infinity', value: 0.5 }] } }), 0.5); assert.equal(marginRateOf({ marginRate: { tiers: [{ endVolume: 1e6, value: 0.1 }, { endVolume: 'Infinity', value: 0.25 }] } }), 0.25); assert.equal(marginRateOf({ marginRate: { value: 0.05 } }), 0.05); assert.equal(marginRateOf({}), undefined); assert.equal(marginRateOf(undefined), undefined); assert.equal(marginRateOf({ marginRate: { tiers: [{ value: 'x' }, { value: 0 }, { value: 3 }] } }), undefined); }); test('dxAccountId is stable, safe, far above broker ids, and host-scoped', () => { const a = dxAccountId('dx.tradeify247.co', 'default:TFY-1'); assert.equal(a, dxAccountId('DX.Tradeify247.co', 'default:TFY-1')); assert.ok(Number.isSafeInteger(a) && a >= 2 ** 50 && a < 2 ** 53); assert.notEqual(a, dxAccountId('dx.other.co', 'default:TFY-1')); const seen = new Set(); for (let i = 0; i < 20000; i++) seen.add(dxAccountId('dx.tradeify247.co', `default:${i}`)); assert.equal(seen.size, 20000); }); test('account codes and contract names', () => { assert.equal(accountDisplayName('default:TFY-50K-12'), 'TFY-50K-12'); assert.equal(accountDisplayName('plain'), 'plain'); assert.equal(accountCodeOf({ clearing: 'default', account: 'x1' }), 'default:x1'); assert.equal(accountCodeOf('default:x1'), 'default:x1'); assert.deepEqual(decodeContractName(encodeContractName('BTC/USD', 0.001)), { symbol: 'BTC/USD', step: 0.001 }); assert.equal(decodeContractName('NQZ5'), null); assert.equal(roundToStep(0.1 + 0.2, 0.1), 0.3); assert.equal(roundToStep(12 * 0.001, 0.001), 0.012); assert.equal(roundToStep(101.234, 0.5, 'up'), 101.5); assert.equal(roundToStep(101.234, 0.5, 'down'), 101); }); test('the firm reset and the CT trade-date start', () => { assert.equal(lastUtcReset(new Date('2026-09-26T21:59:00Z')).toISOString(), '2026-09-25T22:00:00.000Z'); assert.equal(lastUtcReset(new Date('2026-09-26T22:00:00Z')).toISOString(), '2026-09-26T22:00:00.000Z'); assert.equal(ctTradeDayStart(cdt('2026-09-26T18:30:00')).toISOString(), '2026-09-26T22:00:00.000Z'); assert.equal(ctTradeDayStart(cdt('2026-09-27T09:00:00')).toISOString(), '2026-09-26T22:00:00.000Z'); assert.equal(ctTradeDayStart(cst('2027-01-12T10:00:00')).toISOString(), '2027-01-11T23:00:00.000Z'); }); test('the firm\'s day: the same label as the CT trade date, rolling at its reset', () => { // Summer: the reset is 5 PM CDT, so the two agree on both sides of it. assert.equal(firmDayOf(new Date('2026-09-26T21:59:00Z')), '2026-09-26'); assert.equal(firmDayOf(new Date('2026-09-26T22:00:00Z')), '2026-09-27'); assert.equal(firmDayOf(new Date('2026-09-26T22:00:00Z')), currentTradeDateCT(new Date('2026-09-26T22:00:00Z'))); // Winter: a close at 4:30 PM CST is the firm's next day, still ours. const close = cst('2027-01-12T16:30:00'); assert.equal(firmDayOf(close), '2027-01-13'); assert.equal(currentTradeDateCT(close), '2027-01-12'); assert.equal(firmDayOf(cst('2027-01-12T15:59:00')), '2027-01-12'); assert.equal(firmDayOf(cst('2027-01-12T17:30:00')), currentTradeDateCT(cst('2027-01-12T17:30:00'))); }); test('loose parsing', () => { assert.equal(parseDurationMs('PT30M'), 30 * 60_000); assert.equal(parseDurationMs('01:05:08'), (3600 + 300 + 8) * 1000); assert.equal(parseDurationMs({ seconds: 900 }), 900_000); assert.equal(parseDurationMs(undefined), 30 * 60_000); assert.equal(ciNum({ openPL: 12.5 }, 'openPl'), 12.5); assert.equal(ciNum({ openPL: '12' }, 'openPl'), undefined); assert.equal(signedFundValue('WITHDRAWAL', 500), -500); assert.equal(signedFundValue('WITHDRAWAL', -500), -500); assert.equal(signedFundValue('DEPOSIT', 50000), 50000); }); test('trading hours from UTC week sessions', () => { const at = (dayOfWeek, hour, eventType) => ({ eventType, weekDay: { dayOfWeek, time: { hour, minute: 30 } } }); const weekdays = [at('MONDAY', 13, 'SESSION_OPEN'), at('MONDAY', 20, 'SESSION_CLOSE'), at('TUESDAY', 13, 'SESSION_OPEN'), at('TUESDAY', 20, 'SESSION_CLOSE')]; assert.equal(inTradingHours(weekdays, new Date('2026-09-28T15:00:00Z')), true); // Monday assert.equal(inTradingHours(weekdays, new Date('2026-09-28T21:00:00Z')), false); assert.equal(inTradingHours(weekdays, new Date('2026-09-27T15:00:00Z')), false); // Sunday wraps to Tuesday's close assert.equal(inTradingHours([], new Date()), true); assert.equal(inTradingHours([{ garbage: true }], new Date()), true); }); test('realized volatility of hourly closes', () => { assert.equal(realizedVolPerHour([100, 101, 102]), null); const r = rng(6); const sigma = 0.004; const closes = [30000]; for (let i = 0; i < 2000; i++) { // Box–Muller for a normal step const z = Math.sqrt(-2 * Math.log(1 - r())) * Math.cos(2 * Math.PI * r()); closes.push(closes[closes.length - 1] * Math.exp(sigma * z)); } const v = realizedVolPerHour(closes); assert.ok(Math.abs(v - sigma) / sigma < 0.08, `σ ${v}`); const flat = Array.from({ length: MIN_VOL_CANDLES + 1 }, () => 200); assert.equal(realizedVolPerHour(flat), 0); }); test('the selector ranks the plan example: ETH, BTC, XYZ100, AAPL', () => { const perHour = (daily) => daily / Math.sqrt(24); const score = (spread, daily) => scoreCandidate({ spreadFraction: spread, commissionPct: 0.04, financingPctPerDay: 0, targetHoldHours: 6, volatilityPerHour: perHour(daily), }); const board = [ ['BTC/USD', score(0.0002, 0.03)], ['XYZ100', score(0.0001, 0.012)], ['AAPL/USD (Sat)', score(0.0005, 0.004)], ['ETH/USD', score(0.0003, 0.04)], ['AAPL/USD (Tue)', score(0.0005, 0.015)], ].sort((a, b) => a[1] - b[1]).map((b) => b[0]); assert.deepEqual(board, ['ETH/USD', 'BTC/USD', 'XYZ100', 'AAPL/USD (Tue)', 'AAPL/USD (Sat)']); // Cost per leg = score × √(A·B/T): the example's $50 and $60. const k = Math.sqrt((600 * 1350) / 6); assert.ok(Math.abs(score(0.0003, 0.04) * k - 49.6) < 1); assert.ok(Math.abs(score(0.0002, 0.03) * k - 60) < 1); // No volatility, no score; financing over the hold adds to the cost. assert.equal(scoreCandidate({ spreadFraction: 0.0002, commissionPct: 0.04, financingPctPerDay: 0, targetHoldHours: 6, volatilityPerHour: null }), null); const withFinancing = scoreCandidate({ spreadFraction: 0.0002, commissionPct: 0.04, financingPctPerDay: 0.033, targetHoldHours: 12, volatilityPerHour: perHour(0.03) }); assert.ok(withFinancing > score(0.0002, 0.03)); }); test('the selector drops what cannot trade right now', () => { const ok = { tradingStatus: 'FULL', bid: 100, ask: 100.02, quoteAgeMs: 1000, inHours: true, candles: 49, minContractsOk: true, currency: 'USD', accountCurrency: 'USD', }; assert.equal(candidateRejection(ok), null); assert.match(candidateRejection({ ...ok, tradingStatus: 'CLOSE_ONLY' }), /CLOSE_ONLY/); assert.match(candidateRejection({ ...ok, inHours: false }), /hours/); assert.match(candidateRejection({ ...ok, bid: undefined }), /quote/); assert.match(candidateRejection({ ...ok, quoteAgeMs: 10 * 60_000 }), /old/); assert.match(candidateRejection({ ...ok, ask: 101 }), /spread/); assert.match(candidateRejection({ ...ok, candles: 10 }), /candles/); assert.match(candidateRejection({ ...ok, currency: 'EUR' }), /EUR/); }); // ── Hyperliquid price fallback (lib/hyperliquid-feed.ts) ───────────────────── import { hyperliquidCoinFor, parseL2Book } from '../lib/hyperliquid-feed.ts'; test('DXtrade symbols map to Hyperliquid coins', () => { const coins = new Set(['BTC', 'ETH', 'xyz:XYZ100', 'xyz:SP500', 'xyz:AAPL']); assert.equal(hyperliquidCoinFor('BTC/USD', coins), 'BTC'); assert.equal(hyperliquidCoinFor('ETH/USD', coins), 'ETH'); assert.equal(hyperliquidCoinFor('XYZ100', coins), 'xyz:XYZ100'); assert.equal(hyperliquidCoinFor('SP500', coins), 'xyz:SP500'); assert.equal(hyperliquidCoinFor('AAPL/USD', coins), 'xyz:AAPL'); assert.equal(hyperliquidCoinFor('DOGE/USD', coins), null); }); test('l2Book best bid/ask', () => { const q = parseL2Book({ levels: [[{ px: '84310.0' }, { px: '84309' }], [{ px: '84311.0' }]], time: 1790461870878 }); assert.deepEqual(q, { bid: 84310, ask: 84311, time: 1790461870878 }); const empty = parseL2Book({ levels: [[], []] }); assert.equal(empty.bid, undefined); assert.equal(empty.ask, undefined); });