# APEX V16 — Backlog

## Bugs from V15 diagnosis (must NOT recur)
- [V15-BUG-1] 4 trade opening points duplicated → V16: single `trade_opener.py`
- [V15-BUG-2] `entry_struct` vs `market_structure_at_entry` mismatch → V16: dataclasses enforce names
- [V15-BUG-3] `init_data` generic dict with hidden fallbacks → V16: typed `BrainContext`
- [V15-BUG-4] `manage_exit` signature differs TF/MR → V16: shared `BrainBase`
- [V15-BUG-5] `rsi_start` saved at entry but never propagated → V16: `TradeEntry` carries all snapshot fields
- [V15-BUG-6] Stale ticket bug in breakeven after partials → V16: validate ticket before each modify
- [V15-BUG-7] MNQ progress_pct >150% phantom (closed broker-side) → V16: broker poll on suspect progress
- [V15-BUG-8] JPY pip_mult bug (pre-fix V10.7) → V16: ensure pip_mult comes from config_futures, not assumed

## Open questions for later cycles
- [ ] Re-enable correlation when going to Funded
- [ ] Asset-specific structural thresholds (V10.7 had: XAUUSD 0.25, JPY 0.20, forex 0.15)
- [ ] Decision: Brain TF and Brain MR running in parallel or sequential evaluation?
- [ ] Decision: Bias-First (hourly AI bias gate) keep AS-IS or rethink?
- [ ] BrokerBase.fetch_bars: declare abstract method on broker_base.py +
      wire topstepx_v16.py (currently BrokerMarketDataProvider duck-types
      `await broker.fetch_bars(symbol, timeframe, n)`).
- [ ] AccountInfoProvider Protocol + BrokerAccountInfoProvider:
      extract when the orchestrator wires position_sizer.size_for_entry
      and needs a testable abstraction over `account_balance`. For now
      the orchestrator passes the float directly and size_for_entry stays
      a pure function (preferred — keeps sizing math unit-testable
      without mocking a Protocol). Re-evaluate once orchestrator lives.
- [ ] convert_v15_state utility: V16 schema v2 has _migrate_v1_to_v2 only
      for V16 v1 -> v2. V15's v15_state.json has a different shape (no
      schema_version field, V15-specific keys). If we ever need to
      resume from a V15 state file, write a one-shot CLI that maps V15
      keys -> V16 v2 dict and feeds it to SessionState.from_dict. Out
      of scope while we plan to start V16 from a fresh state.
- [ ] BiasResolver: bias_ai_failures_count on BrainCounters. Currently
      bias_calls_count counts attempts; fallback rate is greppable from
      system.log only. Add a dedicated counter when AI cost / reliability
      analytics need finer granularity.
- [ ] BiasResolver: wire orchestrator to call resolver.invalidate(symbol)
      on every new H4 candle close, so a fresh struct flip doesn't wait
      up to 1h to be reflected. For now TTL alone bounds staleness.
- [ ] BiasResolver: AI temperature=0.7 inherited from V15 calibration.
      Re-validate during V16 calibration whether 0.2 (V16 default) or
      0.0 (full determinism) reduces noise without hurting bias quality.
- [ ] RiskManager: SESSION_FILTER hook. V15 had no risk-level session
      filter; if V16 needs to block specific assets in specific UTC
      windows (e.g. crude oil only during NY session), add as 14th
      RiskRule code and wire to a session table in config_futures.
- [ ] RiskManager: profit_target_reduced_risk_multiplier policy. V15-parity
      currently halts entries on profit_target. Future option: allow new
      entries with risk_per_trade * multiplier (e.g. 0.5) so the bot
      keeps trading after target with reduced exposure. The
      state.daily.profit_target_hit flag is already in v2 to support this.
- [ ] RiskManager: re-validate daily_loss_soft_stop ratio. V15 used
      soft/hard = 0.50; V16 default is 0.67 (-1000 / -1500). Calibrate
      on first paper days to find an empirically defensible ratio.
- [ ] RiskManager: consecutive_sl_count is currently stored in
      state.metadata["consecutive_sl_count"][symbol] to avoid bumping
      schema_version a third time. Promote to a first-class field on
      CooldownState if usage stabilizes (would require schema v2 -> v3
      migration with default 0 for all symbols).
- [ ] Orchestrator: re-validate in calibration whether throttling AI
      calls separately for scan vs manage helps cost. V16 Phase A
      collapsed Day-1 scan_interval_seconds + track_interval_seconds into
      a single loop_sleep_seconds (V15 parity). If AI cost analytics
      show frequent redundant manage-side AI calls, split the throttles.
- [ ] Orchestrator: candle-gating optimization. Currently fetch + compute
      indicators every tick (V15 parity). Optimization: gate on M5 close
      to skip recomputation when no new candle arrived. Likely 50-80%
      provider-call reduction. Requires candle_time tracking per symbol.
- [ ] Orchestrator Phase B: wire TradeOpener / TradeCloser / sizing /
      RiskManager.check_entry; mutate state.active_trades; respect
      V15-BUG-9 discipline (save after every mutation); call
      risk_manager.register_sl_hit / register_tp_hit / update_daily_pnl
      on close.
- [ ] Orchestrator Phase C: SIGTERM + asyncio.Event signal handler in
      main.py; broker.connect() + BrokerMarketDataProvider replacing
      Phase A FakeMarketDataProvider({}); broker error handling and
      orphan recovery (V15 _watchdog_naked_positions); FORCE_FLAT
      close-all logic; DRY mode connects broker but never writes orders.
- [ ] Orchestrator Phase C: BrokerBase.get_account_balance() abstract
      method + topstepx_v16 implementation. Today
      orchestrator._resolve_account_balance() always returns
      config.dry_run_balance. LIVE branch: read from broker, fallback to
      dry_run_balance + warning if broker call fails. AccountInfoProvider
      Protocol (already on BACKLOG) becomes natural once 2+ callsites emerge.
- [ ] consecutive_sl_count promotion to first-class CooldownState field
      (currently in state.metadata for v2 schema stability). Schema v3
      migration once usage stabilizes — TradeRuntime now also has
      current_sl_price + partial_pnl_usd as default-zero fields, those
      were backward-compatible without schema bump.
- [ ] PAPER-on-real-data: today PAPER uses FakeMarketDataProvider({})
      (empty bars). Production paper testing benefits from real Topstep
      bars + simulated fills. Wireup: broker.connect in PAPER too,
      provider = BrokerMarketDataProvider(broker), opener/closer keep
      is_paper=True. Could land before / alongside C2.
- [ ] AccountInfo dataclass (balance + equity + margin + buying_power)
      replacing the bare float on get_account_balance. Today only balance
      is consumed by sizing; margin checks and buying-power gating are
      candidates for V16 calibration.
- [ ] Configurable connect backoff: today _connect_with_retry has
      max_attempts=3 and delays_seconds=(5,10,20) hardcoded. If Topstep
      maintenance windows are >60s, expose these via RuntimeConfig.
- [ ] Optional FORCE_FLAT close-all hook (V15 behaviour was passive,
      Topstep auto-flatten 22:00 UTC). V16 calibration may decide to
      close actively at 21:08 to avoid Topstep slippage on liquidation.
      Hook signature would be: orchestrator._handle_force_flat_close()
      iterating active_trades and calling closer.close_trade with a
      "FORCE_FLAT" reason. Currently NOT implemented per V15 parity.
- [ ] Topstep 429 explicit backoff: V15 and V16 today rely on ProjectX
      SDK internal rate limiter (and bypass it for /Position/searchOpen
      / /Order/searchOpen / /Trade/search via direct REST). No 429
      issues observed in V15 production. If calibration shows 429 on
      direct-REST helpers, wrap `_rest_post` in topstepx_v16.py with
      tenacity-style retry that honors the `Retry-After` HTTP header.
      Phase C2b (resilience in-flight) shipped 2026-04-28 — closes the
      WS-drop / mid-loop transport failure class (V15-BUG-9). The 429
      gap is the only known broker-resilience hole left.
- [ ] Reconciler case (v) escalation: today log-only (V15 parity).
      Calibration may decide to emergency-close naked positions after
      N consecutive watchdog hits. Hook would extend
      Reconciler.watchdog_naked_positions to call
      broker.close_position when count exceeds threshold.
- [ ] Reconciler case (iii) auto-adopt opt-in: today V16 conservatively
      log-only on broker-position-without-state. Operator may want a
      `--adopt-orphan-positions` CLI flag that calls
      trade_opener.recover_orphan_position to materialize a TradeEntry
      with is_orphan_recovered=True. Risk: zero indicators-at-entry,
      so Brain decisions on adopted trades are degraded. Decide in
      calibration.
- [ ] Reconciler case (ii) match heuristic refinement: current match
      is "most recent recent_trade for that symbol with pnl != 0
      since entry.opened_at". Could tighten by also matching contracts
      count and side (BUY entry → SELL close leg or partial BUY close
      legs that net out). Useful if multiple trades on same symbol on
      same day produce ambiguous history.

## Pre-Funded checklist (when V16 ready for Express account)
- [ ] Rotate ProjectX API keys
- [ ] Switch .env to Express account (EXPRESS-V2-566414-12462346)
- [ ] Set ENABLE_CORRELATION=True
- [ ] RISK_PER_TRADE: 0.003 → 0.0015
- [ ] MAX_CONTRACTS: conservative values
- [ ] daily_loss_hard_stop: -$1500
- [ ] MAX_RISK_VS_DAILY_BUDGET: 0.33
- [ ] Shutdown AWS VPS 16.16.121.27

## Test plan before live
- [ ] 5+ market days in `--mode paper` on MES, MNQ, 6E, 6B
- [ ] At least 30 paper trades each Brain
- [ ] No crashes, no orphans, no state corruption for 5 consecutive days
- [ ] Then 3-5 days in `--mode live --account ineligible` (burned)
- [ ] Only after that, plan Express deployment

## Validation strategy decision (2026-04-28)

User decision: skip paper validation, go directly to live on burned account
when V16 is feature-complete. Diagnostics via end-of-day JSONL review.

Rationale: burned account has no real capital risk. JSONL logs contain
all events needed for post-mortem. Faster iteration than paper+burned.

Tradeoff accepted:
  - One iteration cycle per day (vs two with paper-then-burned)
  - Bugs surface during live operation, not in controlled simulation
  - Burned-broker behavior (latency, SDK quirks) is mixed with V16 logic
    bugs in the same logs - debugging is harder

Mitigations to apply:
  - Start with --max-trades-session 3 first day (cap exposure to surprises)
  - Limit asset scope: --asset MES,6E first day, expand only after clean run
  - End-of-day discipline: review trade_log + brain_log + error_log
    EVERY evening before next session
  - Any error_log event = stop, diagnose, fix before next session
  - Any state-broker desync = stop immediately

Reconsider this decision if:
  - Two consecutive days with crashes or state corruption
  - Any V16-introduced regression vs V15 burnt-account behavior
