Documents the six fixes (Phases 1-4), the new-only cold-start, the migration handling + pipeline migration-gap, and go-live verification. This commit (no ci skip) triggers the CI build + rollout of the Kevin trading fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.1 KiB
Meet Kevin — trade-execution fix (go-live)
Date: 2026-06-04 Status: shipped (Phases 1–4 merged) → deploying
Problem
Phase 2 "paper trading" was enabled (TRADING_KEVIN_ENABLE_TRADING=true) but
was a silent no-op: 89 Kevin signals reached the executor and every one was
dropped at sizing. The Alpaca paper account had zero Kevin orders (all 50
historical fills were SIMPLE orders from the normal signal-generator path).
Six distinct breaks sat between "infer a call from Kevin's video" and "place +
record + manage the trade":
- Sizing read price only from
sentiment_context["current_price"](None for Kevin) →qty=0→ skip. - The bridge never put a price on the signal.
- Orders were always built
SIMPLE—stop_loss_pct/take_profit_pctwere ignored, so no brackets. - Persisted trades hardcoded
strategy_id=None→ unattributed, invisible to the dashboard (which filters by Kevin's strategy id). - The exit scanner was orphaned — the cron was wired nowhere, so exits never ran; it also lacked an "already-closed" guard.
- No realized P&L was ever written, and bracket stop/take-profit legs that fill at Alpaca never passed through our executor (close + P&L invisible).
Fixes (TDD throughout)
| Phase | Change | Commit |
|---|---|---|
| 1 | TradeSignal.current_price; bridge sets it; sizing honors target_dollars and resolves price; BRACKET orders for LONG entries (EXIT stays SIMPLE) |
14407d3 |
| 2 | Persist strategy_id on the trade row + execution (record + dashboard) |
a8b0d33 |
| 3 | EXIT closes the full held position; realized P&L on close; wire exit scanner on its cron + offsetting-SELL guard | 52b3c76 |
| 4 | broker.get_order(nested) + list_orders; Trade.broker_order_id (migration f6a7b8c9d0e1); reconcile loop books bracket auto-closes + P&L idempotently, syncs status, logs drift |
82dc622 |
Design decision: sizing honors the bridge's target_dollars rather than
re-deriving in the executor — the bridge already applied conviction-weighting
and per-ticker headroom the executor can't see. Brackets trigger on entry
direction + presence of both pct legs (the bridge stamps pcts even on exits, so
the LONG guard is what keeps exits simple).
Cold start — "new signals only"
The live state was already new-only at go-live: bridge cursor
kevin:bridge:last_mention_id = 316 (= MAX mention id, all prior mentions
behind the cursor) and kevin:deferred_signals empty. No flush required. The
stale "deferred" backlog seen in kevin_signal_bridge_state was audit-row
classification, not queued orders.
Migration handling + a latent gap
The Woodpecker pipeline (.woodpecker.yml) has no migration step — it runs
tests → builds images → patches the deployment. Migrations only run via the
TF-managed trading-bot-migrations Job (alembic upgrade head), which the
deploy does not trigger. Migration f6a7b8c9d0e1 (nullable
trades.broker_order_id + index) is backward-compatible, so it was applied to
the live DB ahead of the rollout (atomic DDL + version stamp); the TF Job will
no-op on its next run.
Recommendation (separate, not done here): add a run-migrations step to the
pipeline (a K8s Job on the freshly-built image, before update-deployment) so
future migrations apply automatically on deploy. Requires create jobs RBAC for
the Woodpecker service account (infra repo).
Guardrails (live)
Per-strategy caps already enforced in the risk manager: 10 trades/day, $20k/day
allocation, 20% equity-drawdown halt (permanent pause), 5% daily-loss circuit
(24h pause). Paper account only (TRADING_PAPER_TRADING=true).
Go-live verification
- CI green;
trading-bot-workers6/6 on the new image. - Next Kevin video → mention (id > 316) → bridge emits → executor places a
BRACKET paper order →
tradesrow withstrategy_id = kevin→ dashboard/meet-kevin/strategyshows it;#trading-botSlack posts the entry. (First real trade occurs on Kevin's next upload — the watcher polls every ~3h.) - On a stop/take-profit fill, the reconcile loop books the closing SELL + realized P&L.