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
+47
View File
@@ -5,6 +5,8 @@ from app.services.macro.fetchers import (
_latest_closed_daily_point,
_parse_farside_latest_total,
)
from app.services.bottom_indicators import ahr999 as scanner_ahr999
from app.services.macro import fetchers
def test_drop_in_progress_daily_klines_removes_today_open_bar():
@@ -48,3 +50,48 @@ def test_parse_farside_latest_total_uses_newest_date_not_first_row():
assert parsed["value"] == 321_000_000.0
assert parsed["raw"]["date"] == "25 May 2026"
def test_macro_ahr999_uses_same_formula_as_scanner(monkeypatch):
# Build 300 daily bars with realistic OPEN timestamps so the fetcher's
# in-progress-day drop (_drop_in_progress_daily_klines) actually fires on
# the last bar (today's 00:00 UTC). With fake integer timestamps the drop
# is a no-op, so the fetcher would compute AHR999 over one MORE bar than
# the scanner below — producing a spurious 4th-decimal mismatch (the
# fetcher and scanner share the exact same ahr999() function, so identical
# inputs must yield identical output).
now = datetime.now(timezone.utc)
midnight_ms = int(
now.replace(hour=0, minute=0, second=0, microsecond=0).timestamp() * 1000
)
day_ms = 86_400_000
# Row i opens at (299 - i) days before today's midnight; the last row
# (i=299) opens at today's midnight → still in progress → dropped.
closes = [
[midnight_ms - (299 - i) * day_ms, 0, 0, 0, str(50_000 + i)]
for i in range(300)
]
class _Resp:
def raise_for_status(self):
return None
def json(self):
return closes
class _Client:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
return None
async def get(self, *_args, **_kwargs):
return _Resp()
monkeypatch.setattr(fetchers.httpx, "AsyncClient", lambda *a, **kw: _Client())
result = __import__("asyncio").run(fetchers.fetch_ahr999())
expected = scanner_ahr999([float(r[4]) for r in closes[:-1]])
assert result["value"] == round(expected or 0, 4)