fix: complete v5 routing — API exposure, rescore persistence, leverage cap, key backup doc

Three plumbing fixes + one ops doc that close the gaps from the audit.

scripts/rescore_v5.py
  Was overwriting only signal/conf/reasoning/sentiment/relevant/
  prefilter_reason/analysis_version. Now also persists target_asset,
  category, expected_move_pct — without these the bot can't route
  rescored posts correctly (would silently fall back to BTC).

app/schemas.py + app/api/posts.py
  TrumpPost response model didn't expose target_asset/category/
  expected_move_pct, so the frontend had no way to display "this
  signal will trade SOL". Added the three fields + mapping in
  _post_to_schema(). Pre-v5 posts return null. No frontend changes
  yet — display work is a follow-up.

app/services/hyperliquid.py
  HL caps max leverage per asset (BTC/ETH 50×, SOL 20×, memes 3-5×).
  set_leverage() always tried to push self._leverage — if user set
  30× and bot routed to TRUMP, HL rejected the order and the trade
  silently dropped. Added _get_max_leverage() (queries meta()'s
  maxLeverage field) and _clip_leverage() that caps to HL's max.
  set_leverage now returns the effective leverage so callers can
  use it for notional sizing if needed.

deploy/ENCRYPTION_KEY_BACKUP.md
  Documented mandatory backup procedure for the symmetric key that
  encrypts every user's HL API key. Lost key = all users' bots dead
  with no recovery. Includes rotation procedure + quarterly test
  step + things-not-to-do list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
k
2026-05-08 19:59:30 +08:00
parent 5d1bdc1ff2
commit b941223c88
5 changed files with 125 additions and 4 deletions
+5
View File
@@ -78,6 +78,11 @@ async def update_post(post_id: int, new: dict) -> bool:
post.relevant = new["relevant"]
post.prefilter_reason = new.get("prefilter_reason")
post.analysis_version = new["analysis_version"]
# v5 routing fields — without these the bot can't route old posts
# correctly. analyze_post() always returns these (None for hold).
post.target_asset = new.get("target_asset")
post.category = new.get("category")
post.expected_move_pct = new.get("expected_move_pct")
# Note: do NOT touch price_impact_asset / price_at_post / m5/m15/m1h.
# Those represent actual market behavior (independent of AI's call)
# and stay accurate. The signals/accuracy endpoint will recompute