Commit Graph
10 Commits
Author SHA1 Message Date
momentum devandClaude Opus 4.8 638843ca3a Account-aware sizing: size off REAL equity + buying-power & liquidity caps
For going live on a SMALL real account ($300). The sizer assumed a $100k
default -> at $300 it would size $1,000 positions and get rejected. Now:
- _current_equity() reads actual broker equity (cached get_account) and sizes
  off it, so a $300 account sizes for $300.
- max_position_value caps each position to ~1/3 of equity (TRADING_MAX_POSITION_PCT)
  so N positions fit buying power.
- max_shares liquidity cap (TRADING_LIQUIDITY_MAX_VOLUME_PCT * day volume) wired
  through the trigger book -> submit_breakout_now; off at small size, for scaling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:52:22 -04:00
momentum devandClaude Opus 4.8 6dfc074c6d Profit-lock checkpoints + fix uPnL display
- strategy/exits.py: percentage profit-lock tiers (TRADING_EXIT_PROFIT_TIERS,
  e.g. 8:3,15:9,25:18,40:30). Once a trade has been up gain%, the stop is pinned
  to entry*(1+lock%) so a winner can only give back to a locked-in minimum gain
  and a fade sells you out IN PROFIT. Shared by backtest + live, active-trades
  only (ratchets the broker stop up via replace, never cancel).
- momentum_cli.py: open-positions uPnL was reading 'unrealized_pl' but the sync
  stores 'unrealized_pnl' -> always blank. Read the right key (+ green/red).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 15:57:27 -04:00
momentum devandClaude Opus 4.8 ab5a92447f Managed exits (swept + OOS-verified) + bigger armed set
Replace the static -1R/+2R bracket with active, parameterised exit management
shared by the backtest and live (strategy/exits.py): move to breakeven, trail
under prior-bar lows or by %, scale out, or first-red exit.

Swept all variants over 187 sessions with a held-out train/test split
(sweep_exits.py), then an adversarial 3-judge workflow picked the robust winner
OUT-OF-SAMPLE: trailLow_a1 (trail under the prior bar's low after +1R) — test
avgR 0.128 vs the old static_2R 0.067 (~1.9x), win 49% vs 38%. The naive
in-sample winner (trailPct10) was correctly rejected as overfit
(train 0.684 -> test 0.085).

- strategy/exits.py: ExitConfig + simulate_exit (backtest) + manage_live (live)
- strategy/backtest/engine.py: simulate the managed exit on entry; Trade.entry_index
- runtime/exit_manager.py: live manager — ratchets the broker stop UP via
  REPLACE/PATCH (never cancel = never naked), scales out, first-red exit
- alpaca_paper/client.py: replace_order (PATCH) + partial close_position
- run_live_paper.py: 'exits' step (12s); TRIGGER_MAX_ARMED 6 -> 10
- tests: exits sim/live + manager ratchet-never-cancel

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 13:16:47 -04:00
momentum devandClaude Opus 4.8 fb377ac321 Fast armed-trigger path + live, self-explanatory board
The "always late / miss the move" problem was structural: entries waited for a
1-min bar to CLOSE above the breakout then walked the ingest->watch->execute
cadence (~2 min), and rested a limit AT the trigger that a runner never fills.

Add a top-6 armed-trigger book (runtime/triggers.py): keep the most-promising
gappers ready with a pre-computed opening-range trigger/stop and FIRE the
instant LIVE price crosses, via TradingExecutionService.submit_breakout_now —
a marketable order (capped above the trigger) that actually fills — reusing the
same risk gates (daily-loss breaker, max-concurrent, 1%-risk sizing, dedup).

- structure.py: opening_range() helper (shared with the ORB detector)
- run_live_paper.py: step_arm (rank + arm, 20s) + step_trigger (price-cross
  fire + snapshot, 4s)
- momentum_cli.py: rewrite the watch board — market clock + countdown, loop
  heartbeat, the 6 armed triggers with live price / gap / rvol / trigger /
  distance / stop, and a plain-English "what it's hunting" line
- tests: opening range, trigger book state machine, submit_breakout_now gates

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 07:28:53 -04:00
momentum devandClaude Opus 4.8 92985938e1 Strategy: ORB-first + STRATEGY_SETUPS config; harness gapper filter + per-setup attribution
Sweep over 172 real gapper sessions (16 days) with a per-setup breakdown found
the opening-range breakout is the ONLY profitable setup (+0.25R); every slow
pattern loses (bull_flag -$78k @ ~42min is the worst). Running ORB-ONLY flips
the book from -$95k to +$7.4k AND enters ~11 min earlier (23 vs 34 min avg).

- classify_setup prefers the ORB when valid and honors STRATEGY_SETUPS env
  (comma-sep setup values) to restrict active setups; .env defaults to
  STRATEGY_SETUPS=opening_range_break (ORB-only).
- BacktestEngine Trade.setup_type captured for per-setup attribution.
- learn_params load_sessions filters to gapper sessions (mimics live scanner) so
  the backtest trades the same universe the system would.

75/75 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 18:44:03 -04:00
momentum devandClaude Opus 4.8 14cccc5d30 Add opening-range-breakout setup (the EARLY trigger) + history backfill
- structure.detect_opening_range_breakout: break of the first ~5 RTH bars' high
  on >= OR-average volume; entry=OR high, stop=OR low. Placed first in
  classify_setup so it fires minutes after the open instead of waiting for a
  pullback to form. Verified: RGNT triggers at bar 6 (~9:36 ET) q=1.0.
- evaluate_setup counts OPENING_RANGE_BREAK toward the structure criterion.
- backfill_history.py: pull ~3 weeks of minute + 60d daily bars for the sub-$20
  universe so learn_params optimizes over real sessions.

75/75 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 17:22:43 -04:00
momentum devandClaude Opus 4.8 9677c79db4 Learning harness + tunable timing knobs (diagnose 'always late')
- learn_params.py: grid-search the strategy timing/threshold knobs over stored
  sessions, ranking by total P&L AND 'entry_min' (minutes after the open the
  entry fires) — a learning method to find params that enter earlier while
  staying green.
- evaluate_setup(min_bars=...) + BacktestEngine(ready_score_pct/criteria/min_bars)
  now tunable; watcher passes WATCHER_MIN_BARS through (was a no-op: evaluator
  hard-coded 10).

Sweep finding: even the earliest params enter ~28-30 min after the open
(baseline ~33) because the first-pullback setup waits for a pullback to FORM.
Cadence/min_bars are not the bottleneck — the setup design is. Genuine earliness
needs a fast opening-breakout trigger + more historical sessions to learn from.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 17:07:08 -04:00
momentum devandClaude Opus 4.8 a16a8cd29d WS3: surface evaluation internals (gap/rvol/per-criterion reasons)
- SetupEvaluationResult.criteria_detail: per-criterion {name,passed,reason}.
- watcher CriteriaEvaluatedEvent now carries gap_pct, relative_volume, status,
  reason, and the per-criterion detail (was only passed/failed names).
- CLI: 'inspect criteria SYMBOL' shows status + gap/rvol + each criterion reason;
  new 'inspect signals' = ready board + per-symbol gap/rvol/status.

Verified on real 2026-06-15 bars: RGNT shows 9/9 with reasons (rvol 26.2x),
'late' explained by the 10:30 ET cutoff. 67/67 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 07:22:55 -04:00
momentum devandClaude Opus 4.8 b1ef0930ca WS0 cleanup: DuckDB-free + wire DATABASE_URL
- Drop duckdb dependency (pyproject, requirements) -> psycopg2-binary.
- run_live_paper: boot log shows the real Postgres target (masked); add
  _db_target_desc() helper.
- .env.example: document DATABASE_URL / PG* vars; mark WATCHER_EVENT_DB_PATH legacy.
- evaluation/storage.py: INSERT OR REPLACE -> INSERT (setup_id is a fresh uuid).

Verified: 67/67 tests green on Postgres with duckdb UNINSTALLED; app boots
against the live endpoint (127.0.0.1:5432 proxy) and wrote 205 daily bars.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 00:15:19 -04:00
momentum devandClaude Opus 4.8 007cd95c79 Baseline before Postgres migration (WS0)
Snapshot before the DuckDB->PostgreSQL migration. data/, archive/, the 232MB
rebuild zip, the Windows .venv, and .env are excluded via .gitignore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 23:39:29 -04:00