fix(trading): close five risk gaps surfaced by pre-launch audit

1. hyperliquid.open_position now returns effective_leverage. HL clips
   the requested leverage to each asset's max (e.g. memes capped at 3×)
   and silently applied the lower value; we were discarding that. For
   System-2 this meant sys2_protective_stop_pct was computed against the
   REQUESTED leverage, so the "inside-liquidation" full-close rung was
   actually well inside an 8.5% stop while the real liquidation sat ~33%
   away — strategy intent broken. bot_engine now reads the effective
   value back, recomputes the protective stop + derisk ladder against
   it, and freezes the correct values into BotTrade.eff_* / .leverage.

2. circuit_breaker.clear_trip now clears BOTH systems by default. The
   sys2 column was never reachable from any reset path — a sys2 trip
   forced users to wait the full 24h lockout even after explicit
   human re-arm.

3. /api/user/.../manual-window and /auto-trade re-arm endpoints now
   clear sys1 AND sys2 CB on the same human ack. Symmetry with
   check_and_trip / is_tripped which already supported both systems.

4. telegram.py deep-link map: btc_bottom_reversal + funding_reversal
   alerts now point at /en/macro instead of the now-404 /en/btc.

5. (Already landed: _sys2_mode UnboundLocalError fix in bot_engine —
   restated here for the audit trail.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
k
2026-05-26 01:05:06 +08:00
parent 4442e97f28
commit fc735f251a
5 changed files with 113 additions and 34 deletions
+15 -5
View File
@@ -172,8 +172,13 @@ class HyperliquidTrader:
coin = asset.upper()
is_buy = side.lower() == "long"
# 1. Set leverage before opening
await self.set_leverage(coin)
# 1. Set leverage before opening. Capture HL's clipped value — for
# illiquid / meme assets the requested leverage may exceed the
# asset's max and HL silently clips. Caller MUST use this
# `effective_leverage` for downstream risk math (protective stop,
# liquidation distance) or stops will fire far short of the real
# HL liquidation line.
effective_leverage = await self.set_leverage(coin)
# 2. Compute coin size from USD notional
mid = await self._mid_price(coin)
@@ -232,9 +237,14 @@ class HyperliquidTrader:
oid_src = filled_info or status.get("resting") or {}
order_id = str(oid_src.get("oid", "")) if oid_src else ""
logger.info("Opened %s %s @ %.2f size=%.5f (order_id=%s)",
side, coin, fill_price, total_sz, order_id)
return {"order_id": order_id, "fill_price": fill_price, "size_coins": total_sz}
logger.info("Opened %s %s @ %.2f size=%.5f (order_id=%s, lev=%dx)",
side, coin, fill_price, total_sz, order_id, effective_leverage)
return {
"order_id": order_id,
"fill_price": fill_price,
"size_coins": total_sz,
"effective_leverage": effective_leverage,
}
async def close_position(self, asset: str) -> dict:
"""