From d1ecadb55245fdb4adc45aed42600c883a2f3683 Mon Sep 17 00:00:00 2001 From: k Date: Wed, 27 May 2026 11:04:14 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20add=20CLAUDE.md=20=E2=80=94=20AI-agent?= =?UTF-8?q?=20project=20entry=20doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convention file every new Claude session reads first when opening this repo. Encodes the invariants that aren't visible from any single file: - Five trading-layer non-negotiable rules (sys2 manage-only, released_at, eff_* freezing, HL effective leverage, per-wallet locks). - Module map with the input/output function of every service file. - System 1 vs System 2 comparison table โ€” the single most important distinction in this codebase. - /adopt flow walkthrough, including the race-protection points fixed in 7599d49. - 8-point code-review checklist to run against any trading change. - ADR for why sys2 became manage-only (v2.0). - "Looks like a bug but isn't" + "Known issues not yet fixed" sections to save the next AI agent from chasing ghosts or shipping regressions. Co-Authored-By: Claude Opus 4.7 --- CLAUDE.md | 434 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43907a3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,434 @@ +# Trump Alpha โ€” Backend + +> AI-powered crypto signal aggregator. Surfaces four uncorrelated signal +> streams (Trump Truth Social, Macro Vibes, KOL talks-vs-trades, funding +> reversal) and runs an optional execution layer on Hyperliquid perps. +> Real money โ€” handle every change to the trading layer like surgery. + +This file is the **first thing an AI agent should read** when entering this +repo. It encodes the invariants that aren't visible from any single file. + +--- + +## ๐Ÿ›‘ Read this BEFORE touching anything trading-related + +This backend manages real Hyperliquid leveraged positions for real users. +Bugs cost users money. **Five non-negotiable rules:** + +1. **Two systems, one execution layer.** System 1 (Trump scalp) auto-opens. + System 2 (Macro Vibes / reversal) is **MANAGE-ONLY since v2.0** โ€” the + user opens manually on Hyperliquid, then `/adopt` hands it to the bot. + `process_post()` early-returns for sys2 sources. Re-enabling sys2 + auto-open would silently reintroduce all the leverage/budget/concurrency + race conditions we excised. Don't do it without an ADR. + +2. **`released_at` is the "user took back control" marker.** A trade with + `released_at IS NOT NULL` is **OUT OF BOUNDS for the bot**: + - `recovery.rehydrate_open_trades` skips it + - `reconciler` skips it + - `close_and_finalize`'s atomic claim requires `released_at IS NULL` + unless `force=True` (only manual_close passes that) + - `partial_derisk` and `pyramid_add` early-return idempotent-success + - `/positions/open`, `/positions/today`, telegram_digest all filter it + If you add ANY new code path that touches BotTrade rows, ask yourself + "does this respect released_at?" Almost always yes. + +3. **Effective exit params are FROZEN on the BotTrade row at open time.** + See the `eff_*` columns on `BotTrade`. Recovery rebuilds the watchdog + from these, NOT from the live Subscription. Without this, restarting + the backend silently rewrites every open System-2 reversal's stop loss + to the user's Trump scalp setting (1.5%). NEVER read live + `Subscription.stop_loss_pct` etc. in the close path. + +4. **HL leverage is what HL says, not what the user requested.** Hyperliquid + silently clips the requested leverage to the asset's max (memes capped + at 3ร—). `hyperliquid.open_position()` returns `effective_leverage` โ€” + bot_engine and adoption must use THAT value to compute + `sys2_protective_stop_pct(lev)` and the derisk ladder. Using the + requested value puts the stop OUTSIDE the real liquidation line. + +5. **Per-wallet asyncio locks wrap "check + open" critical sections.** + See `_wallet_lock` in `bot_engine` and `_wallet_adopt_lock` in + `adoption`. Without them, two concurrent signals can both pass the + daily-budget / concurrency / already-open check before either commits. + +--- + +## What this product does (90 seconds) + +``` +Four signal sources โ†’ one bot โ†’ optional Hyperliquid execution + +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ 1. Trump Truth Social โ”‚โ”€โ”€ auto-classify (DeepSeek) โ†’ "buy"/"short"/"noise" +โ”‚ (every post, <3s) โ”‚ if actionable: Trump scalp auto-open (System 1) +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Tight 1.5% SL, 12h cooldown, โ‰ฅ30min min-hold + +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ 2. Macro Vibes โ”‚โ”€โ”€ 8 daily macro indicators (AHR999, F&G, etc.) +โ”‚ (BTC bottom + funding)โ”‚ + 2-of-3 bottom-reversal trigger +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Telegram alert with /adopt CTA โ€” NO auto-open. + User opens on HL โ†’ /adopt โ†’ bot manages with + 5-rung stop ladder, de-risk, pyramid, peak-trail. + +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ 3. KOL talks-vs-trades โ”‚โ”€โ”€ Substack/podcast ingest + ETH on-chain diff +โ”‚ (19 KOLs, daily) โ”‚ Divergence (publicly bullish, secretly selling) +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ is the platform's highest-conviction signal. + Telegram alert only โ€” never auto-trades. + +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ 4. Funding extreme โ”‚โ”€โ”€ Hourly BTC perp funding scan +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Alert only (manage-only via /adopt like Macro) + +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Telegram daily digest โ”‚โ”€โ”€ Once-a-day 3-section brief (Macro/KOL/Trump) +โ”‚ (per-user hour, opt-out)โ”‚ to every subscriber. Cron at minute=0 each hour. +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +Free tier = read everything + Telegram alerts. Pro tier (Hyperliquid wallet +linked) = Trump auto-trade + /adopt manage-only flow for sys2. + +--- + +## Stack + +- **Python 3.9+** / FastAPI / async SQLAlchemy 2.x / APScheduler +- **DB**: SQLite dev, **Postgres prod**. All schema lives in + `alembic/versions/NNN_*.py`, ordered. Currently at head **024**. +- **AI**: DeepSeek via OpenAI-compatible API (`AI_BASE_URL`, `AI_MODEL`). + - Live scoring uses `AI_LIVE_MODEL` (~2s, latency-critical) + - Batch / reanalysis uses `AI_MODEL` (quality, ~10s) +- **Trading**: Hyperliquid SDK; API-wallet keys are envelope-encrypted with + `ENCRYPTION_KEY` (KEK), per-user DEK derivation via `crypto.py`. +- **Prices**: Binance WS (`binance.py`) feeds `price_store` + powers the + `tp_sl_monitor` per-tick evaluator. +- **Telegram**: long-poll mode (single instance), HTML messages, inline + keyboards. `telegram.py` send/edit/answer + `telegram_bot.py` commands. + +--- + +## Module map (where things live) + +``` +app/ +โ”œโ”€โ”€ api/ HTTP routes +โ”‚ โ”œโ”€โ”€ signals.py POST /api/signals/ingest โ† scanners write here +โ”‚ โ”œโ”€โ”€ positions.py /positions/open|today|close|grow|adopt|release|hl +โ”‚ โ”œโ”€โ”€ user.py /subscribe|settings|manual-window|auto-trade +โ”‚ โ”œโ”€โ”€ telegram.py /telegram/{preferences,bind,unbind,test} +โ”‚ โ”œโ”€โ”€ macro.py /macro/{snapshot,history} +โ”‚ โ””โ”€โ”€ kol.py /kol/{posts,digest,wallets,divergence} +โ”œโ”€โ”€ services/ +โ”‚ โ”œโ”€โ”€ bot_engine.py โ˜… TRADING CORE โ€” process_post, _execute_for_subscriber, +โ”‚ โ”‚ close_and_finalize, partial_derisk, pyramid_add +โ”‚ โ”œโ”€โ”€ adoption.py โ˜… /adopt + /release flow (sys2 manage-only) +โ”‚ โ”œโ”€โ”€ tp_sl_monitor.py Per-price-tick close evaluator. on_price_tick is +โ”‚ โ”‚ called from binance.py once per second +โ”‚ โ”œโ”€โ”€ hyperliquid.py HL trader (open/close/reduce/leverage) +โ”‚ โ”œโ”€โ”€ recovery.py Startup rehydration of open BotTrades into watchdog +โ”‚ โ”œโ”€โ”€ reconciler.py Every 60s: compare DB โ†” HL state, mark drift +โ”‚ โ”œโ”€โ”€ circuit_breaker.py Per-system (sys1/sys2) CB, daily DD + N-loss streak +โ”‚ โ”œโ”€โ”€ signal_categories.py CRITICAL CONFIG โ€” sys1/sys2 sources, ladders, +โ”‚ โ”‚ leverage clamping, protective stop formulas +โ”‚ โ”œโ”€โ”€ regime_filter.py Sys1 only โ€” recent-move / vol-contraction gates +โ”‚ โ”œโ”€โ”€ analysis.py AI signal scoring (DeepSeek) +โ”‚ โ”œโ”€โ”€ entry_filter.py Cheap text-based pre-filter (skip RT/URL-only) +โ”‚ โ”œโ”€โ”€ telegram.py send_message / edit_message / answer_callback +โ”‚ โ”œโ”€โ”€ telegram_bot.py Long-poll loop + /start /digest /adopt /release ... +โ”‚ โ”œโ”€โ”€ telegram_digest.py Daily 3-section brief (rule-based; no LLM) +โ”‚ โ”œโ”€โ”€ price_store.py In-memory latest price per asset +โ”‚ โ”œโ”€โ”€ price_backfill.py Backfill historical 5min bars from Binance +โ”‚ โ”œโ”€โ”€ crypto.py HL API-key envelope encryption +โ”‚ โ”œโ”€โ”€ scanner_state.py In-memory toggle + observability for scanners +โ”‚ โ”œโ”€โ”€ macro/ +โ”‚ โ”‚ โ”œโ”€โ”€ fetchers.py 8 macro indicator HTTP fetchers (each @_none_on_fail) +โ”‚ โ”‚ โ”œโ”€โ”€ scoring.py Weighted composite -100..+100 +โ”‚ โ”‚ โ””โ”€โ”€ poll.py Daily UPSERT into macro_snapshots +โ”‚ โ”œโ”€โ”€ scanners/ +โ”‚ โ”‚ โ”œโ”€โ”€ btc_bottom_reversal.py 2-of-3 AHR999 + 200WMA + Pi Bottom +โ”‚ โ”‚ โ”œโ”€โ”€ funding_reversal.py Hourly funding extreme +โ”‚ โ”‚ โ””โ”€โ”€ sma_reclaim.py (archive โ€” not scheduled) +โ”‚ โ”œโ”€โ”€ kol_substack.py RSS ingest for 19 KOL feeds +โ”‚ โ”œโ”€โ”€ kol_onchain.py HL public API + Etherscan diff +โ”‚ โ”œโ”€โ”€ kol_divergence.py Cross-ref talks vs trades within ยฑ7d +โ”‚ โ”œโ”€โ”€ kol_analysis.py AI ticker/direction/conviction extract +โ”‚ โ”œโ”€โ”€ bottom_indicators.py AHR999 / Pi Cycle / 200WMA math +โ”‚ โ””โ”€โ”€ funding_signal.py Real-time funding extreme detector +โ”œโ”€โ”€ scrapers/ +โ”‚ โ””โ”€โ”€ truth_social.py CNN + trumpstruth.org pollers (5s interval) +โ”œโ”€โ”€ ws/ +โ”‚ โ””โ”€โ”€ manager.py WebSocket fan-out for live UI updates +โ”œโ”€โ”€ models.py โ˜… All SQLAlchemy models in one file +โ”œโ”€โ”€ database.py Async engine + session factory +โ”œโ”€โ”€ config.py Pydantic Settings โ€” reads .env +โ””โ”€โ”€ main.py FastAPI lifespan, scheduler setup, route mount + +alembic/versions/ Migrations (numbered NNN). Latest = 024 +scripts/ One-shot ops +โ”œโ”€โ”€ preflight.py Pre-launch readiness gate (env / DB / TG / AI) +โ”œโ”€โ”€ launch_smoke.py End-to-end smoke (14 checks against running API) +โ”œโ”€โ”€ rescore_v5.py Re-score every Post with current AI prompt +โ”œโ”€โ”€ backfill_signals.py Fill in signal for posts missing it +โ””โ”€โ”€ verify_sys2_lifecycle.py Manual System-2 lifecycle walk-through + +tests/ pytest, 64 tests, fast (<1s total) +``` + +--- + +## The two trading systems (memorise this) + +``` + System 1 System 2 + โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +Source "truth" "btc_bottom_reversal" + (+"funding_reversal" alert-only) +Trigger Trump posts a thing Daily scanner: 2-of-3 confluence +Latency need Seconds (price moves fast) Days/weeks (signal lives a long time) +Open path Auto (bot_engine.process_post fires _execute_for_subscriber) + MANUAL: user opens on HL UI, then + /adopt hands it to the bot +Stop loss User-configured + tight sys2_protective_stop_pct(actual_lev) + 1.5% floor (TRUMP_*) = 85% ร— (100/lev), capped at 35% +Exit model TP / trailing / SL 5-rung stop ladder + downside de-risk + + pyramid + peak-trail. NO TP. +Min hold 30 min (suppresses TP) n/a +Max hold 6h 18 months (ladder is the real exit) +Sizing base ร— regime multiplier Whatever user opened on HL +Concurrency cap n/a 3 positions / wallet (correlated beta) +Confidence min 88 (platform) / user 85 (platform) +Circuit breaker sys1_* sys2_* (independent) +Daily budget Full daily_budget_usd n/a โ€” user controls notional on HL +Telegram alert Trump alert format Macro/funding alert + /adopt CTA +``` + +**If you're tempted to put sys2 logic in `_execute_for_subscriber`**: stop. +`process_post()` early-returns for sys2. The function only runs for sys1 now. +The dead sys2 branches inside `_execute_for_subscriber` are kept for diff +minimalism โ€” don't extend them. + +--- + +## The /adopt flow (System-2 lifecycle in detail) + +``` +1. Scanner fires + โ””โ”€ POST /api/signals/ingest (source=btc_bottom_reversal, signal=buy) + โ””โ”€ Post row created + โ””โ”€ process_post() early-returns for sys2 (no auto-open) + โ””โ”€ notify_signal() โ†’ Telegram fan-out with /adopt CTA appended + +2. User opens BTC long on Hyperliquid manually + โ””โ”€ size / leverage of their choice + +3. User in bot: /adopt + โ””โ”€ adoption.list_hl_positions(wallet) reads HL state + โ””โ”€ Telegram inline keyboard: tap [๐ŸŸข BTC long $1500 @72k ยท 2x] + โ””โ”€ Mode picker: [๐Ÿ“ˆ Standard] or [๐Ÿš€ Aggressive] + โ””โ”€ adoption.adopt_position(wallet, asset, mode): + a. Per-wallet asyncio lock acquired + b. Pre-flight: no_subscription / no_hl_key / paper_mode / + circuit_breaker (sys2 CB still gates adopt!) / + already_adopted / concurrency_cap (3) + c. Re-read HL state inside lock (fresh entry/size/lev) + d. Resolve sys2_protective_stop_pct(HL_actual_leverage) + e. INSERT BotTrade with eff_* frozen + sys2_mode + hl_order_id="adopted:" + + trigger_post_id=NULL + f. register_trade() with full ladder/de-risk/addon/peak_trail + โ””โ”€ Telegram confirmation w/ ladder summary + +4. tp_sl_monitor drives the position + โ””โ”€ Stop ratchet, downside de-risk partial reduces, pyramid add-ons, + peak-trail close, max_hold backstop. All staged through the + lock-protected partial_derisk / pyramid_add / close_and_finalize. + +5a. User wants out: /release + โ””โ”€ release_management(wallet, trade_id): + a. Sets BotTrade.released_at = now + b. unregister(trade_id) from watchdog + โ””โ”€ HL position UNTOUCHED โ€” bot stops driving, user has manual control + โ””โ”€ Across restarts: recovery skips released rows (released_at filter) + โ””โ”€ Reconciler skips them. Released trades don't appear in + /positions/open or the digest "your status" line. + +5b. Bot drives the close (ladder / max-hold) + โ””โ”€ close_and_finalize() atomic claim sets closed_at, computes pnl + โ””โ”€ Trade is now CLOSED (closed_at set, exit_price + pnl_usd written) + +5c. User force-closes via UI: POST /api/positions/{id}/close + โ””โ”€ manual_close calls close_and_finalize(force=True) โ€” bypasses + released_at guard. Works on adopted-and-released trades too. + +6. Recovery on restart: + โ””โ”€ recovery.rehydrate_open_trades reads BotTrade WHERE closed_at IS NULL + AND released_at IS NULL + โ””โ”€ For each: rebuild ladder from sys2_mode + (category OR adopted fallback). + The fallback for trigger_post_id IS NULL is the critical fix โ€” + without it adopted trades lose their entire sys2 ladder on restart. +``` + +--- + +## Critical invariants checklist (when reviewing any trading change) + +- [ ] Does this code path respect `released_at IS NULL`? +- [ ] Does it use `eff_*` (frozen) not live `Subscription.*` for exit math? +- [ ] If it opens a new position, does it use HL's actual leverage (not requested)? +- [ ] If it touches an open position concurrently, is it wrapped in the + per-trade `_lock_for(trade_id)` lock? +- [ ] If it opens, is it inside `_wallet_lock(wallet)` so the budget / + concurrency check is atomic with the write? +- [ ] If it closes, does it use the conditional `UPDATE ... WHERE closed_at + IS NULL` atomic claim? +- [ ] Does it handle the `already_closed` path from HL gracefully (preserve + banked partial PnL)? +- [ ] Does it correctly check sys1 vs sys2 CB independently? + +--- + +## Common workflows + +### Add a new signal source + +1. Decide: System 1 (auto-trade) or System 2 (alert + /adopt) or alert-only? +2. Write a scanner under `app/services/scanners/NEW.py` that posts to + `POST /api/signals/ingest` with `{source: "your_new_source", ...}`. +3. Schedule it in `app/main.py` (`_scheduler.add_job`). +4. Add the source to `signal_categories.SYSTEM_1_SOURCES` or + `SYSTEM_2_SOURCES` if it should trade. Leave it out if alert-only. +5. Add a Telegram preference column to `TelegramBinding` (migration) + + a mapping entry in `telegram._pref_column_for_source`. +6. Add a label to `telegram._source_label` and `_signal_emoji`. +7. Add a `/yoursource on|off` command in `telegram_bot.py`. +8. If sys2: extend `signal_categories._CATEGORY_EXITS` if it needs a custom + exit profile (otherwise default works). + +### Add a new bot command + +1. `_cmd_x` async function in `telegram_bot.py`. +2. Route it in `_handle_message`. +3. If it needs inline buttons: build `reply_markup` payload, handle + callbacks in `_handle_callback` (route by `callback_data` prefix). +4. Update `HELP_TEXT` and remind the user to add it to BotFather + `/setcommands` after deploy. + +### Add a column to an existing table + +1. New migration `alembic/versions/NNN_description.py`. + - Use `op.batch_alter_table` (sqlite-compatible). + - Default values via `server_default=` so backfill is implicit. +2. Mirror the field on the SQLAlchemy model in `app/models.py`. +3. Apply locally: `DATABASE_URL= alembic upgrade head`. + +### Deploy + +```bash +# On the server: +DATABASE_URL=$PROD_URL alembic upgrade head +systemctl restart trumpalpha-backend # or whatever the unit is +python scripts/preflight.py # MUST pass before flipping traffic +python scripts/launch_smoke.py --base https://api.trumpalpha.io +``` + +--- + +## Testing + +```bash +source venv/bin/activate +python -m pytest tests/ -q # full suite, ~0.5s +python scripts/preflight.py # env + DB + TG + AI auth checks +python scripts/launch_smoke.py # 14 end-to-end checks vs running API +``` + +64 tests. Adoption + telegram_digest are snapshot-style (no real HL/AI). +End-to-end trading is verified manually via the bot. + +--- + +## Telegram bot mechanics (since it's a custom integration) + +- **Long-poll mode** via `getUpdates`. Only ONE process can long-poll a + given bot token at a time โ€” if you horizontally scale, switch to + webhook (not done yet). +- Bot must be re-bound via `@BotFather` `/setcommands` whenever new + commands are added (the slash-menu users see is separate from what + the bot internally handles). +- `send_message` returns `False` on failure; per-user binding rows track + `total_alerts_sent` / `total_alerts_failed` counters. +- **Inline keyboards** = the `reply_markup` payload to `sendMessage`. + Callback data is capped at 64 bytes; keep it short (`adopt:mode:BTC:standard`). + `_handle_callback` MUST end with `answer_callback` or the button spins + forever on the user's client. +- Free tier = walletless `/start` (chat_id only). Pro tier = wallet bound + via `/start CODE` where CODE comes from Settings UI. + +--- + +## Why "Macro Vibes" became manage-only (the ADR) + +V1.0: System 2 auto-opened sys2 trades on user wallets. Carried real +execution surface: leverage clipping, daily budget split, concurrency caps, +sys2 paper branches, key handling per user. Audit surfaced ~6 bugs. + +V2.0 (current): sys2 manage-only. The strategy is day-K โ€” entry delay of +24h doesn't matter. The valuable part is multi-month exit management +(5-rung ladder, de-risk, pyramid, peak-trail), which still runs against +positions the user adopts. + +**Net effect**: massive reduction in execution risk surface; same alpha +(strategy logic unchanged); legal/responsibility shifts from "bot opened +this for you" to "you opened it, bot manages your discipline". + +--- + +## Things that LOOK like bugs but aren't + +- **`_execute_for_subscriber` has lots of `if sub["_is_system_2"]` branches.** + Dead code under v2.0 (process_post early-returns for sys2). Kept for diff + minimalism โ€” don't extend or re-enable. +- **`funding_reversal` source is in `SYSTEM_2_SOURCES`? No.** It's + intentionally NOT in either supported set โ€” it ingests as a Post for + audit + sends Telegram alert via the CTA path, but doesn't trigger any + auto-trading. Adopt still works (it's asset-based, not signal-based). +- **`Subscription.sys2_budget_pct` defaults to 0.7.** Legacy field from the + auto-open era. With v2.0 manage-only, it's effectively unused โ€” sys1 + (Trump) reads full `daily_budget_usd`. Don't read it for new code. +- **Adopted trades have `hl_order_id` starting with `"adopted:"`.** Distinct + from auto-opened (HL order id integer) and paper (`"paper"` literal). + Useful for telemetry filtering. + +--- + +## Open known issues (not blocking launch but worth fixing later) + +- **Trump daily budget split**: `_execute_for_subscriber` still computes + `daily_cap = total ร— (1 - sys2_pct)` for sys1 โ€” i.e. Trump gets only 30% + of the configured daily budget by default. With sys2 manage-only there's + no reason to split. Fix: when not sys2, use full `total_cap`. (Low-prio: + users who notice can just set `sys2_budget_pct = 0` in DB.) +- **HL high-leverage adoption**: if a user has a 25ร— position on HL, our + `sys2_protective_stop_pct` clamps to 10ร— (the strategy's design max), + which gives a stop FURTHER from entry than the real liquidation point. + Position could get HL-liquidated before our stop fires. Fix: reject + adoptions where actual HL leverage > `SYS2_MAX_LEVERAGE`. +- **`adopt:choose:BTC` callback may show stale prices** if user takes >60s + to tap (HL fees, partial fills can change entry/size). adopt_position + re-reads HL at mode-tap time so the FROZEN BotTrade is always fresh, + but the picker label could be outdated. +- **Telegram bot offset on restart**: `getUpdates` may replay last 24h of + messages on backend restart. Stale `/adopt` could re-fire. User can + /release to recover. + +--- + +## Repos in this project + +- **This repo** (`/Users/k/Public/Claude/backend`) โ€” Python/FastAPI backend +- **Sibling frontend** (`/Users/k/Public/Claude/trumpsignal`) โ€” Next.js 16 + dashboard at trumpsignal.com. See its own CLAUDE.md. + +Both deployed independently. Backend serves the JSON API + Telegram bot. +Frontend is a thin SPA over the API + WebSocket.