fix: pre-launch hardening — HYPE price feed, KOL wallet cleanup, Telegram Trump alert, rate limiting, brittle test
Batch of the pre-launch audit campaign (BUG-01…14 plus three new features): Pricing / TP-SL protection - Add app/services/hl_price_feed.py: supplemental HL allMids poller for HL-native assets (HYPE, PURR) not listed on Binance. Pumps price_store + tp_sl_monitor.on_price_tick so bot trades on these assets keep full stop-loss / take-profit / trailing protection instead of max-hold only. - Wire feed into main.py lifespan (startup task + graceful shutdown cancel). Telegram - Add format_trump_mention + PATH B in _dispatch: crypto-relevant Trump posts with no directional signal (relevant=True, signal=hold) now alert the public channel only (no per-subscriber noise). - Rate limiter (slowapi) on the API; assorted bot/digest fixes. KOL on-chain - seed_kol_wallets.py: KOL_FEEDS coverage cross-check; reversibly deactivate orphaned wallets (handle not in KOL_FEEDS → can never produce divergence) so the scanner stops burning cycles on them. Tests / misc - Fix brittle test_macro_ahr999_uses_same_formula_as_scanner: mock now uses realistic ms timestamps so the in-progress-day drop fires, matching the fetcher's bar count (was 0.3179 vs 0.3178 off-by-one). - Refresh stale notify_signal comment in truth_social.py. Frontend reduce-action type fix lives in the sibling repo. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -96,7 +96,7 @@ linked) = Trump auto-trade + /adopt manage-only flow for sys2.
|
||||
|
||||
- **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**.
|
||||
`alembic/versions/NNN_*.py`, ordered. Currently at head **026**.
|
||||
- **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)
|
||||
@@ -106,6 +106,9 @@ linked) = Trump auto-trade + /adopt manage-only flow for sys2.
|
||||
`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.
|
||||
Public channel broadcast (`TELEGRAM_PUBLIC_CHANNEL_ID` env) sends a
|
||||
sanitised `format_public_post` version (no execution details, tier label
|
||||
instead of raw confidence) after every per-user fan-out in `_dispatch`.
|
||||
|
||||
---
|
||||
|
||||
@@ -165,7 +168,8 @@ app/
|
||||
├── config.py Pydantic Settings — reads .env
|
||||
└── main.py FastAPI lifespan, scheduler setup, route mount
|
||||
|
||||
alembic/versions/ Migrations (numbered NNN). Latest = 024
|
||||
alembic/versions/ Migrations (numbered NNN). Latest = 026
|
||||
026 = composite index (wallet_address, closed_at) on bot_trades
|
||||
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)
|
||||
@@ -304,6 +308,8 @@ minimalism — don't extend them.
|
||||
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).
|
||||
9. Add the deep-link path in `telegram.format_post` AND `telegram.format_public_post`
|
||||
(both the per-user private alert and the public channel use the same path map).
|
||||
|
||||
### Add a new bot command
|
||||
|
||||
@@ -399,29 +405,108 @@ this for you" to "you opened it, bot manages your discipline".
|
||||
- **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.
|
||||
- **`telegram.send_message` accepts `int | str` for `chat_id`.** Intentional.
|
||||
Integer = private chat, string = public channel username (e.g. `"@trumpalpha"`).
|
||||
The public channel uses the string form; per-user alerts still pass integers.
|
||||
- **`format_public_post` deliberately omits `expected_move_pct`,
|
||||
`invalidation_price`, and `/adopt` CTA.** Execution-sensitive data stays
|
||||
private (per-user). The public version shows confidence tier (HIGH/MED/LOW)
|
||||
instead of the raw score for readability.
|
||||
- **`_adopt_locks` in adoption.py looks like it should have a cap like
|
||||
`_wallet_open_locks` (512).** It doesn't yet — see Known Issues below.
|
||||
|
||||
---
|
||||
|
||||
## 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`.
|
||||
- ~~**`_time_stop_check` tasks not rehydrated on restart** (BUG-01, FIXED 2026-05-29):~~
|
||||
`recovery.py` now imports `_time_stop_check` + `_background_tasks` from
|
||||
`bot_engine` and `get_exit_profile` from `signal_categories`. After each
|
||||
`register_trade()` call for a sys2 trade, it checks `exit_profile.time_stop_hours`,
|
||||
computes remaining seconds, and reschedules the task. Elapsed windows (backend
|
||||
was down longer than the time-stop period) fire immediately with `delay_seconds=0`.
|
||||
|
||||
- **Rate limit bypass via proxy x-forwarded-for deletion** (BUG-02, HIGH):
|
||||
`app/api/proxy/[...path]/route.ts` (frontend) strips `x-forwarded-for`
|
||||
before forwarding. Backend `slowapi` sees the Next.js server IP and applies
|
||||
one rate-limit bucket to ALL users. Fix: relay the real client IP from
|
||||
`req.headers.get('x-forwarded-for')` or `x-real-ip` in the proxy.
|
||||
|
||||
- ~~**`close_and_finalize` double-failure leaves DB/HL state inconsistent**
|
||||
(BUG-03, MITIGATED 2026-05-29):~~ Full two-phase-commit is out of scope.
|
||||
Mitigation: `reconciler._reconcile_wallet` now runs a "ghost position" pass —
|
||||
queries DB-closed trades from the last 2h and cross-checks against HL open
|
||||
positions. Mismatches are logged at ERROR level and broadcast via WS
|
||||
(`reconcile_drift.ghost_positions`). Manual close required on HL UI.
|
||||
|
||||
- ~~**`_adopt_locks` has no capacity cap** (BUG-04, FIXED 2026-05-29):~~
|
||||
`adoption._adopt_locks` is now an `OrderedDict` capped at `_ADOPT_LOCK_MAX=512`
|
||||
with LRU eviction — matches the `_WALLET_LOCK_MAX` pattern in `bot_engine`.
|
||||
|
||||
- ~~**Reconciler runs wallets sequentially** (BUG-05, FIXED 2026-05-29):~~
|
||||
`reconcile_all_once` now fans out with `asyncio.gather` + `asyncio.Semaphore(10)`.
|
||||
Worst-case tail latency is `ceil(N/10) × 30s` instead of `N × 30s`.
|
||||
|
||||
- ~~**`/stop` also silently disables daily digest** (BUG-07, FIXED 2026-05-29):~~
|
||||
`send_daily_digest` no longer filters on `alerts_enabled` — digest and
|
||||
real-time alerts are independent. `/stop` reply updated to say "Real-time
|
||||
alerts paused … send `/digest off` to stop the daily brief separately".
|
||||
|
||||
- ~~**`binance.py` ASSET_MAP only had BTC + ETH** (BUG-08, CRITICAL, FIXED 2026-05-29):~~
|
||||
Trump's AI can set `target_asset` to SOL/TRUMP/BNB/DOGE/LINK/AAVE.
|
||||
`ASSET_MAP` now covers all those; `BINANCE_WS_URL` is derived from it
|
||||
automatically so the stream list and routing table can never diverge.
|
||||
**Still missing**: HYPE (HL-native, not on Binance) — trades on HYPE fall
|
||||
back to max-hold only until a supplemental HL price feed is added.
|
||||
|
||||
- ~~**`_close_locks` leaked one entry per concurrent-close loser** (BUG-11, FIXED 2026-05-29):~~
|
||||
`close_and_finalize` now pops `_close_locks[trade_id]` in the
|
||||
`rowcount == 0` early-return path, not just on the success paths.
|
||||
|
||||
- ~~**`signed_request._seen` O(n) purge at 5000 entries** (BUG-12, FIXED 2026-05-29):~~
|
||||
Threshold reduced to `_SEEN_PURGE_THRESHOLD = 1000` and the purge now
|
||||
builds an `expired` list in one pass rather than calling `dict.pop` in a loop.
|
||||
|
||||
- ~~**`_get_max_leverage` makes a fresh HL `meta()` call on every trade open**
|
||||
(BUG-10, FIXED 2026-05-29):~~ `_MAX_LEV_CACHE` added (same 300s TTL as
|
||||
`_SZ_DECIMALS_CACHE`). Cache miss populates ALL coins from the single
|
||||
`meta()` response, so concurrent opens pay one API call, not N.
|
||||
|
||||
- ~~**Trump daily budget split**~~ (FIXED 2026-05-29): System-2 is manage-only
|
||||
so the `sys2_pct` reservation no longer makes sense for auto-opens.
|
||||
`daily_cap` for System-1 (Trump) is now `total_cap × 1.0` instead of
|
||||
`total_cap × (1 - sys2_pct)`. The split logic remains intact so it would
|
||||
work correctly if sys2 auto-open is ever re-enabled via ADR.
|
||||
|
||||
- ~~**HL high-leverage adoption**~~ (BUG-09, FIXED 2026-05-29): `adopt_position`
|
||||
now rejects positions where `leverage > SYS2_MAX_LEVERAGE` with error code
|
||||
`leverage_too_high`. Previously the protective stop was computed for 10×
|
||||
but applied to a 25× position — the stop was OUTSIDE the liquidation band.
|
||||
|
||||
- **`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.
|
||||
|
||||
- ~~**`pyramid_add` double-add on DB commit failure** (BUG-13, FIXED 2026-05-29):~~
|
||||
Same pattern as BUG-03 / partial_derisk. HL `open_position` succeeds but the
|
||||
subsequent `db.commit()` of `addon_steps_done` fails → retrigger sees same
|
||||
`step_idx` and double-adds. Fix: pre-claim `addon_steps_done = step_idx + 1`
|
||||
with a conditional UPDATE (WHERE `addon_steps_done == step_idx`) BEFORE calling
|
||||
HL. If `rowcount == 0`, return early. Second UPDATE writes `entry_price` and
|
||||
`size_usd` after fill confirmation.
|
||||
|
||||
- ~~**`price_impact_monitor` measured wrong asset** (BUG-14, FIXED 2026-05-29):~~
|
||||
`truth_social.py` passed `analysis["asset"]` (BTC/ETH sentiment proxy) to
|
||||
`register_post` instead of `target_asset` (SOL/TRUMP/etc. — the perp we actually
|
||||
trade). Impact % was measuring BTC/ETH not the traded coin. Fix: introduced
|
||||
`tracked_asset = analysis.get("target_asset") or asset` and used it for
|
||||
`price_at_post`, `price_impact_asset`, and `register_post(asset=…)`.
|
||||
|
||||
---
|
||||
|
||||
## Repos in this project
|
||||
|
||||
Reference in New Issue
Block a user