Commit graph

126 commits

Author SHA1 Message Date
Viktor Barzin
f7ca671bf3 feat(phase2): BRACKET orders + Kevin risk caps (Tasks 18, 19)
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
Task 18 — OrderRequest + AlpacaBroker BRACKET support:
- OrderRequest gains order_class ("simple" | "bracket"),
  take_profit_price, stop_loss_price + model_validator that requires
  both legs when order_class == "bracket".
- AlpacaBroker._build_order_request branches to a MarketOrderRequest
  with OrderClass.BRACKET + TakeProfitRequest + StopLossRequest legs,
  TimeInForce.GTC so the bracket survives day boundaries.

Task 19 — RiskManager Kevin caps + circuit-breaker:
- TradeExecutorConfig gains 4 fields: kevin_daily_trade_cap,
  kevin_daily_alloc_cap_usd, kevin_equity_drawdown_halt_pct,
  kevin_daily_loss_circuit_pct.
- check_risk() applies the caps only when
  signal.strategy_id == KEVIN_STRATEGY_UUID; non-Kevin signals pass
  through the existing path unchanged.
- 4 new checks in order: drawdown halt (sets permanent
  trading:paused), daily-loss circuit (setex 24h), daily trade-count
  cap, daily allocation cap (rolling today's $ + this trade's
  notional).
- Counter keys: kevin:daily_trades:YYYY-MM-DD,
  kevin:daily_alloc_usd:YYYY-MM-DD, kevin:daily_pnl_usd:YYYY-MM-DD,
  kevin:starting_equity_usd. All read-only here; bridge + executor
  write them.

Tests: 5 bracket + 9 kevin-caps + 28 regression-safe. Total 67 + 14
new = 81 passing (excluding -m integration). No DB needed.
2026-05-26 21:03:59 +00:00
Viktor Barzin
fdc2a60257 ci: fix worker container list — match current infra (5 containers)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Pipeline #47 was the first green build since the infra was simplified
on 2026-05-22 (commit 66ca8b9e in infra), which removed news-fetcher,
sentiment-analyzer, trade-executor and added meet-kevin-watcher; a
later commit added kevin-signal-bridge for Phase 1. The .woodpecker.yml
patch list was never updated, so update-deployment added 3 stale
containers to the running pod (news-fetcher, sentiment-analyzer,
trade-executor) — sentiment + trade-executor crash on port 8000 since
news-fetcher binds it first.

Update the strategic-merge patch to match infra exactly:
  - signal-generator
  - learning-engine
  - market-data
  - meet-kevin-watcher    (was unmanaged → image stuck on :latest)
  - kevin-signal-bridge   (was unmanaged → image stuck on :latest)

Strategic merge doesn't remove containers not in the patch, so the 3
stale containers in the live deployment will be cleaned up by a
follow-up terragrunt apply on infra/stacks/trading-bot.
2026-05-26 20:13:55 +00:00
Viktor Barzin
bc479802db test(kevin): fix enum assertion + mark Postgres-dependent tests as integration
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Pipeline #46 surfaced two pre-existing CI bugs once fakeredis was
installed and tests could collect:

1. test_models.py:389 asserted "DISCOVERED" in status_col.type.enums,
   but the model defines KevinVideoStatus with values_callable so
   .enums returns the lowercase string values, not member names.
   Asserting "discovered" instead.

2. Four test files use the db_session fixture which requires a real
   Postgres on localhost:5432. CI has no Postgres, so 10 tests failed
   with Connect call failed (errno 111). These genuinely need a DB —
   mirroring tests/integration/* which already use
   @pytest.mark.integration. Adding module-level
   pytestmark = pytest.mark.integration to:
   - tests/shared/models/test_meet_kevin_trading.py
   - tests/services/kevin_signal_bridge/test_aggregator.py
   - tests/services/kevin_signal_bridge/test_audit.py
   - tests/services/kevin_signal_bridge/test_exit_scanner.py

CI runs with -m "not integration" so they're now deselected.
Local pytest still picks them up by default (no marker filter).
2026-05-26 20:01:37 +00:00
Viktor Barzin
de6f27ddbb ci: retrigger build after Woodpecker server reschedule killed #45
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-05-26 19:50:30 +00:00
Viktor Barzin
e035904e1c fix(ci): add fakeredis to [dev] extras
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is running
CI test step in pipelines #41/#43/#44 (commits db103df, 06ede26,
552f5a1) failed during collection with ModuleNotFoundError:
fakeredis on test_blocklist.py, test_cursor.py, test_risk_counters.py.
The bridge tests use fakeredis.aioredis to mock Redis but the dep
wasn't pinned in pyproject. Locally it was installed manually, so
67 kevin tests pass via .venv but CI never installed it.

Unblocks the trading-bot-service rebuild that should ship the
PositionInfo.ticker fix the bridge pod is crash-looping on, and
also unblocks the dashboard rebuild (last pushed 2026-02-25, so
none of the Meet Kevin UI is live yet).
2026-05-26 19:47:15 +00:00
552f5a18f7 fix(kevin_bridge): use PositionInfo.ticker + qty*avg_entry for cost basis
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
PositionInfo schema in shared/schemas/trading.py has ticker (not symbol)
and no cost_basis field. Compute cost basis as qty * avg_entry.
Production logs showed AttributeError on every mention process.
2026-05-24 01:22:40 +00:00
06ede26e78 feat(api): /api/meet-kevin/strategy/* routes
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-05-24 01:12:16 +00:00
886dbaec86 feat(api): /api/meet-kevin/backtest/* routes
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
2026-05-24 01:10:34 +00:00
db103df9b1 feat(kevin_bridge): service entrypoint with concrete wiring
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Wires the dependency-injected KevinBridge to concrete Redis cursor +
DB session factory + AlpacaBroker (or stub when creds missing). Includes
TradeSignalPublisher (Pydantic -> dict for the redis stream) and
SIGINT/SIGTERM graceful shutdown. Adds is_asset_tradable + get_latest_price
to AlpacaBroker so the bridge can query asset metadata.
2026-05-24 01:06:18 +00:00
cff2564428 feat(kevin_bridge): exit-scan daily job + cursor + audit writer
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
2026-05-24 01:03:53 +00:00
a417cae77b feat(kevin_bridge): blocklist + daily risk counters
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
2026-05-24 01:01:54 +00:00
3347847e38 feat(kevin_bridge): multi-mention aggregator with capped conviction boost
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
2026-05-24 01:01:02 +00:00
adbd7f3c65 feat(kevin_bridge): main orchestrator with dependency injection
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
Composable: cursor/aggregator/strategy/publisher/audit_writer/broker
all injected. Master kill-switch (kevin_enable_trading=false) routes to
audit-only path. Cursor advances ONLY after XADD succeeds (race fix).
Concrete collaborators wired in subsequent tasks.

Also extends TradeSignal + SignalDirection.EXIT with the optional
fields Kevin paths need (strategy_id, target_dollars, stop_loss_pct,
take_profit_pct).
2026-05-24 00:59:56 +00:00
cd75c4ab7e feat(backtester): extend compute_metrics with alpha/beta/winners/best
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
In-place extension (no fork). Existing tests still pass; new fields are
optional and None when no benchmark is supplied.
2026-05-24 00:57:42 +00:00
23ce45a4f2 feat(kevin): mention-driven backtest mini-engine
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
Walks mentions chronologically, T+1 entry, time-based exit per
KevinStrategy. Reuses backtester/metrics::compute_metrics for headline
numbers. KevinPriceLoader fronts market_data + Alpaca.
2026-05-24 00:56:57 +00:00
7dcce5ea0e feat(kevin): KevinStrategy standalone decision logic
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Stateless: mention + account_state -> KevinDecision. Conviction-weighted
sizing, time_horizon-derived hold periods, hard per-ticker cap. The
bridge and the backtest mini-engine both call evaluate_mention so
behaviour cannot drift.
2026-05-24 00:51:31 +00:00
c4e92b580e feat(kevin): alembic migration for v2 trading tables
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
3 new tables + seeds the 'kevin' row in strategies with a pinned UUID
constant so Trade.strategy_id can be joined back to the strategy across
live + backtest paths.
2026-05-24 00:50:00 +00:00
4d40536da7 feat(kevin): SA models for bridge audit + backtest persistence
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
3 tables (kevin_signal_bridge_state, kevin_backtest_runs,
kevin_backtest_trades) all UUID-keyed for consistency with Trade/Position.
KEVIN_STRATEGY_UUID constant pinned for FK joins from Trade.strategy_id.
2026-05-24 00:49:52 +00:00
6636054742 feat(dashboard): /meet-kevin/strategy page wired
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
Strategy.tsx composes 6-up metrics header, StrategyVsBenchmarkCurve
equity chart, TickerScorecardTable and BacktestRunHistory. Selecting a
backtest run replaces the chart with that run's equity curve. Run
Backtest button fires POST and polls for completion.
App.tsx: +route /meet-kevin/strategy.
Layout.tsx: +sidebar entry 'MK Strategy' under Meet Kevin group.
2026-05-24 00:48:27 +00:00
5f5529ef09 feat(dashboard): 3 components for the strategy page
TickerScorecardTable: bridge-status badges (WOULD-TRADE/HOLDING/etc),
conviction bar, unrealised P&L, manual-close button.
BacktestRunHistory: sortable run list with return/sharpe/alpha columns,
row click selects a run for detail view.
StrategyVsBenchmarkCurve: dual lightweight-charts line (strategy blue,
SPY dashed grey) with legend.
2026-05-24 00:48:22 +00:00
90cf21521f feat(dashboard): TS types + API client for strategy + backtest
BacktestRun, BacktestRunDetail, StrategyTicker, StrategyEquityCurve,
StrategyPerformance types added to meetKevin.ts. New meetKevinStrategy.ts
with 8 axios methods covering the backtest run/list/get/latest and
strategy tickers/equity-curve/performance/close endpoints.
2026-05-24 00:48:16 +00:00
9d752aa0a2 feat(kevin): KevinDecision + KevinAccountState schemas
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
Standalone schemas (no BaseStrategy coupling) used by both the live
signal bridge and the backtest mini-engine.
2026-05-24 00:44:57 +00:00
c83f13625b add Meet Kevin v2 implementation plan (3 phases, 22 tasks)
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Maps the design doc (commit 280f807) to bite-sized tasks. Phase 1 ships
strategy + backtest + bridge in audit-only mode; Phase 2 extends
OrderRequest/AlpacaBroker for BRACKET orders, extends RiskManager, and
flips the kill-switch; Phase 3 ships the paper-account UI page.

Each task has Test → Run-and-fail → Implement → Run-and-pass → Commit
steps with concrete code in every step. Implementer can pick up any
task without prior session context.
2026-05-24 00:40:20 +00:00
280f807236 add Meet Kevin v2 paper-trading + backtest + UI design
Synthesizes work of two parallel architect agents (strategy +
paper-trading rules / backtest + UI surface) and the subsequent
challenger review. Resolves 11 issues the challenger raised:

- KevinStrategy is standalone, not BaseStrategy subclass (signature
  mismatch — BaseStrategy.evaluate is bar-driven, Kevin is event-driven)
- backtester/kevin_backtest.py as parallel mention-driven mini-engine,
  not a fake adapter onto BacktestEngine
- AlpacaBroker BRACKET support specified (OrderRequest schema + broker
  _build_order_request extensions)
- Filtering paper-account trades via strategy_id FK (the actual field;
  Trade.strategy_name doesn't exist) — migration seeds a 'kevin' row
- Cursor advance race fixed (XADD success → cursor advance)
- Daily counter mechanics specified (Redis INCR + audit dedupe)
- kevin_signal_bridge_state table added to data model (3 new tables now)
- All PKs UUID for consistency with Trade/Position
- StrategyVsBenchmarkCurve.tsx promoted from contingent to definitely-new
- 'avoid' policy split into AVOID_CLOSES_LONGS + AVOID_BLOCKS_DAYS knobs
- Phasing collapsed A+B into Phase 1 (ticker scorecard needs bridge
  audit rows to render WOULD-TRADE badges)
2026-05-23 10:04:04 +00:00
ed2195d879 feat(meet-kevin): throttle inter-video LLM calls (30s) to stay under Anthropic RPM
First production run hit Anthropic's per-account rate_limit_error (429) trying
to burn through 16 backfill videos in seconds. The SDK's built-in retry can't
recover because the rate limit window resets slower than the 3 retry attempts.

Added meet_kevin_inter_video_sleep_seconds (default 30s) to PipelineDeps and
main's _process_pending_videos loop. 16 backfill videos now take ~8 min (16 * 30s
sleeps + ~30s per LLM call) instead of bursting into the rate limit.
2026-05-22 20:25:19 +00:00
3402ba0e7f fix(docker): drop sentiment extras from runtime image
sentiment-analyzer service is disabled in the K8s revival (removed from
the workers Pod spec). Its torch+transformers dependency adds ~2GB to
the image with no runtime benefit. Tests still install it via the
.[dev] extras for the test step.

Image size: ~3GB -> ~1GB.
2026-05-22 20:15:06 +00:00
31047e6fd2 ci: include meet_kevin extras in test step (yt-dlp, feedparser, anthropic, httpx) 2026-05-22 20:00:59 +00:00
8a1d03a967 refactor(meet-kevin): switch LLM back to native Anthropic SDK with OAuth bearer
Previous refactor (89f01ad) moved to OpenRouter because no sk-ant-api-* key
was found in Vault. Turns out claude-agent-service-spare-{1,2} hold
sk-ant-oat01-* OAuth tokens (108 chars, scope user:inference, 1-year TTL,
minted via 'claude setup-token' — see memory id=832).

These tokens work with the Anthropic SDK via the auth_token= constructor
argument (routes to Authorization: Bearer ... instead of x-api-key: ...).
They consume the Enterprise Claude subscription quota rather than
per-call billing, so the OpenRouter zero-credit problem goes away.

- llm_analyzer.py: revert OpenAI client to AsyncAnthropic; tool-use API
  + cache_control restored
- config.py: openrouter_api_key -> anthropic_oauth_token; model slug
  reverted from anthropic/claude-sonnet-4.5 -> claude-sonnet-4-5
- main.py: AsyncOpenAI -> AsyncAnthropic(auth_token=...), drop OpenRouter
  attribution headers
- pyproject: openai>=1.50 -> anthropic>=0.40 in meet_kevin extras
- tests: mocks ported back to messages.create + tool_use blocks
2026-05-22 19:24:40 +00:00
4f4d365652 fix(docker): add ffmpeg + nodejs for yt-dlp YouTube extraction
yt-dlp requires:
- ffmpeg for sub-conversion (--convert-subs srt) and any format mux
- A JS runtime for YouTube player decryption (deno or node)

Without these, every caption extraction attempt in the meet-kevin-watcher
container fails with 'ffmpeg not found' + 'No supported JavaScript runtime
could be found'. Adding both to the runtime stage of Dockerfile.service.

Size impact: ~50 MB.
2026-05-22 14:03:22 +00:00
89f01ad9c0 refactor(meet-kevin): switch LLM analyzer to OpenRouter via OpenAI SDK
User's Vault has openrouter_api_key but no direct sk-ant-* Anthropic key.
OpenRouter passes through Claude Sonnet 4.6 (~3% markup over Anthropic
list pricing) and matches the existing gpt_mini_endpoint pattern used
by recruiter-responder.

- Replace anthropic.AsyncAnthropic with openai.AsyncOpenAI + base_url
- Convert Anthropic tool-use API to OpenAI function-calling
- System prompt unchanged (analyst instructions are model-agnostic)
- Drop cache_control (not in OpenAI API); revisit later if cost matters
- Model slug: anthropic/claude-sonnet-4.5 (OpenRouter's current Claude tier)
- Pricing: $3.10/M input, $15.50/M output (OpenRouter pass-through)
- Config field anthropic_api_key -> openrouter_api_key
- pyproject extras: anthropic>=0.40 -> openai>=1.50

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 09:52:55 +00:00
3c20c8c12c feat: add meet_kevin extras (yt-dlp, feedparser, anthropic) 2026-05-21 20:16:09 +00:00
7b81980c66 fix(meet-kevin): API smoke-test bugs from Task 17 QA
Three issues caught during end-to-end manual QA against docker-compose:

1. SAEnum field columns serialized to Python enum NAMES ('DISCOVERED')
   but the DB enum had VALUES ('discovered'). Added `values_callable`
   to all 5 SAEnum() declarations in shared/models/meet_kevin.py so they
   emit values, matching the migration's enum literals.

2. /dashboard's "last 7 days" / "last 14 days" filters used
   `func.cast("7 days", type_=None)` which produced NullType DDL.
   Replaced with `text("now() - interval '7 days'")`.

3. /dashboard's outlook trend query repeated `func.date_trunc("day", col)`
   in SELECT, GROUP BY and ORDER BY — Postgres treats each as a separate
   parameterized expression. Hoisted into a single `day_trunc` variable
   so all three clauses reference the same SQL fragment.

All 11 /api/meet-kevin/* endpoints now return valid JSON against a
docker-compose Postgres seeded with one analyzed video + NVDA mention.
2026-05-21 20:15:08 +00:00
01856bab9f feat(dashboard): wire Meet Kevin routes + sidebar entry 2026-05-21 20:08:08 +00:00
6bcb6637a8 feat(dashboard): Meet Kevin stocks list + per-ticker drill-down 2026-05-21 20:07:01 +00:00
9ce0e44929 feat(dashboard): Meet Kevin video detail page (tabs + iframe + deep-links) 2026-05-21 20:05:14 +00:00
625c22b833 feat(dashboard): Meet Kevin videos feed page 2026-05-21 20:03:23 +00:00
d4a1ca870e feat(dashboard): Meet Kevin home page 2026-05-21 20:01:53 +00:00
a4d75e37c4 feat(dashboard): reusable Meet Kevin components (ActionChip, ConvictionBar, YouTubeEmbed) 2026-05-21 19:58:36 +00:00
83b18b43cf revert: keep original dashboard tsconfig (Task 10 didn't need to change it) 2026-05-21 19:57:30 +00:00
cafcaad502 feat(dashboard): Meet Kevin TypeScript types + API client 2026-05-21 19:56:13 +00:00
bfa7a503da feat(api): /api/meet-kevin/* routes (11 endpoints) 2026-05-21 19:53:16 +00:00
8f5ee8f1c3 feat(meet-kevin): pipeline orchestrator + service main loop
Implements Task 8 of the Meet Kevin revival plan.

- pipeline.py: PipelineDeps dataclass (frozen, DI-friendly), process_one_video
  state machine (discovered→captioned→analyzed with retry/cost-cap logic),
  and daily_cost_used() SQL helper.
- main.py: async run() entry point with RSS poll loop, per-video pipeline
  processing, OTEL counters, SIGTERM/SIGINT shutdown, httpx client lifecycle,
  and clean Anthropic/DB teardown.
- tests: 5 pipeline unit tests (happy path, no captions, cost cap, retry
  increment, failed-after-3-retries) all passing; full watcher suite 56/56.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 19:48:43 +00:00
8309556c00 feat(meet-kevin): Claude Sonnet 4.6 LLM analyzer (tool-use forcing + prompt cache)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 19:44:57 +00:00
145f7dbec5 feat(meet-kevin): caption extractor via yt-dlp
- Implement CaptionResult frozen dataclass for structured caption data
- Add parse_srt() to parse SubRip format with flexible timestamp handling
- Add extract_captions() async function using yt-dlp subprocess wrapper
- Prefer manual captions over auto-generated; clean up SRT files after parsing
- Add 16 comprehensive tests covering edge cases (empty input, malformed SRT,
  timestamp variations, language extraction, manual vs auto selection)
- Type-safe implementation with full mypy --strict compliance
- Add sample.srt fixture with 3 segments mentioning NVDA for test reference
2026-05-21 19:40:52 +00:00
8ce3ede09c feat(meet-kevin): RSS poller for YouTube uploads 2026-05-21 19:36:22 +00:00
8edcb070ed feat: scaffold meet_kevin_watcher service + config 2026-05-21 19:33:04 +00:00
61adf63c7d feat: add Alembic migration for Meet Kevin tables 2026-05-21 19:31:21 +00:00
a49e46f787 fix(models): drop dead __table_args__ + use func.now() for server defaults 2026-05-21 19:26:42 +00:00
8ed2e70e8f feat: add Meet Kevin SQLAlchemy models (5 tables) 2026-05-21 19:19:33 +00:00
8a412e6ae9 fix(schemas): use enum types as field types + enforce symbol length
- Replace all Literal[...] type annotations with corresponding enum classes
  (TickerAction, TimeHorizon, MarketOutlook, VideoStatus, TranscriptSource)
  for MeetKevinTickerMention, MeetKevinAnalysis, and API response models
  (VideoSummary, VideoDetail, StockMention, StockSummary, TimelineBucket)
- Add min_length=1, max_length=10 validation to MeetKevinTickerMention.symbol
- Split test_conviction_edge_cases into two separate boundary tests
- Strengthen test_valid_ticker_mention with assertions for all 6 fields
- Trim no-information docstrings from TranscriptSegment, StockTimeline
- All 60 schema tests pass
2026-05-21 19:15:59 +00:00