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:
k
2026-05-29 11:57:19 +08:00
parent 6471e44aac
commit d6c802ef26
40 changed files with 1833 additions and 209 deletions
+19 -6
View File
@@ -61,12 +61,17 @@ _CB_COLS = {
}
async def _trades_for_system(db: AsyncSession, wallet: str, since, system: str):
async def _trades_for_system(db: AsyncSession, wallet: str, since, system: str, limit: int | None = None):
"""Closed, priced trades for `wallet` since `since`, filtered to the
given system by joining the trigger post's source.
sys2 = trigger_post.source in System-2 set; sys1 = everything else
(currently only "truth"). Trades with no trigger post → treated sys1.
sys2 = trigger_post.source in System-2 set OR adopted trade (identified
by hl_order_id starting with "adopted:" or sys2_mode being non-NULL).
sys1 = everything else (currently only "truth").
Important: adopted trades have trigger_post_id=NULL so their Post join
returns NULL source — they must be identified by the trade row itself,
not the linked post.
"""
from app.models import Post
from app.services.signal_categories import SYSTEM_2_SOURCES
@@ -80,10 +85,17 @@ async def _trades_for_system(db: AsyncSession, wallet: str, since, system: str):
BotTrade.pnl_usd.is_not(None),
)
.order_by(BotTrade.closed_at.desc())
.limit(limit * 4 if limit else None) # fetch a small multiple to account for system filtering
)
out = []
for trade, src in rows.all():
is_s2 = (src or "").lower() in SYSTEM_2_SOURCES
# Adopted trades: trigger_post_id=NULL, hl_order_id starts with "adopted:"
# or sys2_mode is set. Check trade row directly before falling back to source.
is_s2 = (
(trade.hl_order_id or "").startswith("adopted:")
or trade.sys2_mode is not None
or (src or "").lower() in SYSTEM_2_SOURCES
)
if (system == "sys2") == is_s2:
out.append(trade)
return out
@@ -124,9 +136,10 @@ async def check_and_trip(
system, wallet, today_pnl, dd_limit)
if reason is None:
# Consecutive losses within this system (all-time, most recent N).
# Consecutive losses within this system — only need the most recent
# CB_CONSEC_LOSSES trades; no reason to scan full history.
all_sys = await _trades_for_system(
db, wallet, datetime(1970, 1, 1), system,
db, wallet, datetime(1970, 1, 1), system, limit=CB_CONSEC_LOSSES,
)
recent = all_sys[:CB_CONSEC_LOSSES]
if len(recent) >= CB_CONSEC_LOSSES and all((t.pnl_usd or 0) < 0 for t in recent):