- A batch sizes its leg on every mapped candidate, each at the leverage
the broker states for it, and trades the cheapest (spread, commissions,
expected swap) that reaches the target hold — else the quickest. The
dashboard ranks the same way from a half-hourly candidate survey.
- Last stand: when a daily-loss stop-out would leave the account within
15% of that loss above its max-drawdown floor (the account type's static
max loss, or the broker's liquidation floor), the leg carries no
daily-loss stop and is sized against the firm's own breach.
- The daily-loss reserve is 10% of the limit or 0.1% of the position,
whichever is more, since a stop's slippage grows with the position.
- The account type's max notional is gone: the broker's margin rates cap
each symbol. planLegSize holds the one sizing rule fireLeg, the pick and
the dashboard share.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- DXtrade client (lib/dxtrade-class.ts): REST session login with lockout-safe
backoff, 60s sync of accounts/metrics/positions/orders/transfers, history and
fund transactions from cash transactions, day-start balance at the 22:00 UTC
reset, CLOSE-by-positionCode exits (native OCO group, else a stop-first
emulated pair), a cross-account hedge guard, stray-order reaper.
- Instrument selector: the firm's symbol map lists candidate symbols per CME
root; the cheapest tradable one per unit of recent hourly volatility wins
(spread + 2x commission [+ financing] / sigma).
- Fees: a firm's commission % of notional prices every fee off the fill; the
take-profit is solved in closed form so the leg nets its target.
- Hard daily loss limit per account type: every leg carries a stop keeping the
firm's day inside the limit less a 10% reserve and both fees (rounded in);
a green day never widens it; no room or a refused bracket flattens.
- Notional sizing where the platform prices positions: target-hold volatility
sizing or the symbol map's fixed notional, under a per-type notional cap.
- Every-day firms trade weekends/holidays; the scheduler runs lanes (futures,
one per DXtrade firm) with their own hours, flat gate, mutex and batches.
- The catalog sync skips platforms this build doesn't know instead of treating
them as Tradovate. One shared fireLeg replaces the duplicated batch/copy code.
- npm run check: fee/stop/sizing/hours/selector checks; scripts/dxtrade-smoke.mjs
confirms the API against a real login before trading.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TopstepXClient (ProjectX Gateway): API-key auth, SignalR user hub,
Trade/search daily P&L, emulated OCO exits, books-invariant
withdrawal detection for $0-start accounts
- Platform-aware client pool, credentials (api_key column), and
credential form; $0-size account types match by prefix alone
- Drain stages: min-day ($5-$15 randomized) MNQ legs for the
configured days, then wait for the 50% withdrawal; stage pills
show 'Stage N – Drain'
- Account calendar projects the perfect path: future days book their
planned targets, simulated 50% withdrawals, payout marker
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When an account type opts in, legs are placed as an OCO pair via
order/placeoco - the usual limit plus a stop: the first cycle day's stop
keeps the day's net floor at -$1,000, min-day trades stop at 2x their
target, and the day after any red day rides stopless to make it back. A
stop-out books the day: eligibility refuses the account once realized
P&L falls short of the legs fired (or is simply negative on an opted-in
account, which survives restarts). If placeoco is rejected the leg falls
back to the classic lone limit with a loud log rather than leave a
position without an exit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Big days (> the $1,700 split threshold) must carve at least the account
type's minLegs from the catalog — pickLegAmount reserves the minimum
piece for each leg still owed, forcing a carve even once the residual
dips below the threshold, clamped to what the pieces allow. Days at or
below the threshold still trade as one leg. The smallest split piece
drops from $800 to $500. An older master without the field means no
minimum.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Account types with the catalog's new combineCmeHolidays flag are read
through a combined-day view: holiday-session P&L entries fold into the
next full trading day (computed from the federal-holiday rules, so any
year works), the day count matches how the firm records it (Apex: Sun/
Mon/Tue around a Monday holiday is ONE day), and on the later sessions
of the stretch the earlier sessions' booked P&L carries into realized —
so day targets, already-traded gates, target-hit status and the panel's
day P&L all treat the stretch as a single day. Off by default; older
masters that don't send the flag behave exactly as before.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When the master marks an account type splitEnabled=false, big days trade
as one unsplit leg (the legacy one-and-done behavior); min-day bumps are
unaffected. Missing field — older master or pre-toggle cached catalog —
defaults to enabled.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This box no longer decides what firms exist. It caches the master's catalog
and stores one thing of its own: the Tradovate login for each firm. Creating
firms locally is gone — app/add, POST /api/firms, the account-config routes
and the banned-symbol route with it.
The cache is the point, not an optimisation. The scheduler needs every
account's target and size on each 60s tick, so fetching them per tick would
stop trading with positions open the first time the master rebooted. It is
one JSON blob in settings, replaced only by a complete valid response, and a
response with zero firms is refused outright: a misconfigured FIRMS_DB serves
{"firms":[]} as a healthy 200, and mirroring that would idle the whole fleet
at once.
Accounts bind to an account type by prefix AND size, the size taken from the
opening funding transaction. That does not arrive with the first sync, so an
account sits Unassigned and the resolver tries again next sync rather than
guessing. A type picked by hand is never overwritten by an auto pass. This
replaces the longest-prefix helper that had been copy-pasted into five files,
which could not tell two types with the same prefix apart at all.
firm_credentials and account_assignments deliberately have no foreign key to
the cached catalog. foreign_keys is ON, so a cascade would erase the Tradovate
passwords the moment a firm was removed on the master — routine, not
exceptional. They survive it, and are re-keyed by name if the master's database
is ever rebuilt with fresh ids.
The legacy firms/account_configs/firm_banned_symbols tables are left on disk,
unreferenced. firms is the only table that ever held plaintext credentials and
dropping it would make a rollback unrecoverable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- lib/db.ts: new firm_banned_symbols table with getBannedSymbols,
isSymbolBanned, and setBannedSymbol helpers
- app/api/firms/[id]/banned-symbols/route.ts: GET lists banned symbols,
PATCH toggles a ban for a given symbol
- app/api/firms/route.ts: include bannedSymbols[] in firm list response
- app/firms/[id]/settings/page.tsx: Instruments section shows all
globally-enabled symbols with a red toggle to ban/unban; banned
symbols display a "SYMBOL BANNED" pill next to their name
- app/page.tsx: FirmRows shows "ES BANNED" (or current symbol) pill next
to the firm name when the selected trade symbol is banned for that firm
- lib/auto-trade.ts: skip firms entirely when the trade symbol is banned
- types.ts: add bannedSymbols field to FirmConfig
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SQLite DB (better-sqlite3) with firms, account_configs, firm_fees, instruments tables
- REST API routes: firms CRUD, account configs CRUD, state, accounts, instruments
- Live Tradovate WebSocket client: login, sync, positions, auto-liq thresholds
- Dashboard (app/page.tsx): per-firm account list with balance, day P&L, days traded,
target progress, and Dead/Inactive/Flat status based on Tradovate auto-liq floors
- Account detail page: objectives progress, daily P&L chart, consistency tracking
- Per-firm settings page: account configs and instrument fee management
- Dead detection uses trailingMaxDrawdownLimit - trailingMaxDrawdown from
userAccountAutoLiqs; filters Tradovate sentinel value (999999999 = no limit)
- FIFO P&L engine with commission accounting for daily P&L history
- Removed manual maxLoss fallback in favour of live Tradovate auto-liq data
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>