export interface AccountConfig { /** Master's account type id — what a manual assignment pins to. */ id: string; label: string; prefix: string; profitTarget: number; consistency: number; minDayPnL: number; minTradingDays: number; accountSize: number; maxLoss: number; // 0 = no limit; positive = account is "Dead" when loss exceeds this maxPositionSize: number; // 0 = no limit; positive = max contracts per trade targetSameEquity: boolean; // if true, reduce profitTarget by priorProfit after withdrawal splitEnabled: boolean; // false = big days trade as one unsplit leg instead of random ≥$500 pieces minLegs: number; // big days (> $1,700) must carve at least this many legs (0 = however the carve falls) firstDayStop: boolean; // first cycle day carries a $1,000 account stop - books red and ends minDayStop: boolean; // min-day trades carry a 2x-target stop; next day after a loss rides stopless combineCmeHolidays: boolean; // true = CME holiday sessions count with the next full trading day (Apex-style) withdrawalStages: { profit: number; consistency: number; minTradingDays: number }[]; // per-stage fresh profit + consistency (empty = use base values) } export interface FirmConfig { /** Master's firm id (uuid). */ id: string; firm: string; /** Blank when this machine has no credentials for the firm. */ username: string; /** True once a Tradovate username and password are stored locally. Firms * without them are shown greyed out with an inline credential form. */ hasCredentials: boolean; bannedSymbols: string[]; /** The firm's account types, straight from the catalog. Doubles as the * option list for a manual account-type override. */ accounts: AccountConfig[]; catalogUpdatedAt: string; } export interface AccountState { id: number; name: string; active: boolean; amount: number; realizedPnL: number; daysTraded: number; positionDirection: 'long' | 'short' | null; /** Balance floor from Tradovate's auto-liquidation profile (0 = not set) */ autoLiqThreshold: number; /** Sum of all historical daily P&L entries */ totalProfit: number; /** True when today's realizedPnL has met or exceeded the computed daily target */ targetHit: boolean; /** True when a stop-enabled account booked its day red via its own stop — * the personal DLL. It stands down until the next session's make-back. */ personalDll: boolean; /** Current stage = 1 + number of withdrawals (negative fund transactions). Stage 1 = fresh account. */ stage: number; /** Next trading day's target, computed server-side. null when account is dead or config is missing. */ dailyTarget: { amount: number; path: 'first_day' | 'normal_day' | 'reduced_day' } | null; /** Actual profit target to hit — max(stage profit target, consistency realTarget = maxDay/consistency). */ effectiveProfitTarget: number | null; /** Current cycle daily P&L — used for target calculations. */ dailyPnL: { date: string; pnl: number }[]; /** Full P&L history across all cycles — used for calendar and equity curve display. */ fullDailyPnL: { date: string; pnl: number }[]; /** Fund transactions (deposits/withdrawals) — used by the calendar and equity curve. */ fundTransactions: { date: string; amount: number }[]; /** Master account type this account resolved to. null = Unassigned. */ accountTypeId: string | null; accountTypeLabel: string | null; assignmentSource: 'manual' | 'auto' | 'unassigned'; /** The resolved config inlined, so client components need no second lookup. */ accountType: AccountConfig | null; /** Size from the initial funding transaction; null until reports return. */ inferredAccountSize: number | null; /** True when more than one account type matched and updatedAt decided it. */ accountTypeAmbiguous: boolean; } export interface FirmState { /** Master's firm id — join state to config on this, not on the name, which * changes the moment someone renames a firm on the master. */ firmId: string; firm: string; connected: boolean; accounts: AccountState[]; }