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:
@@ -112,12 +112,19 @@ async def _process_entry(entry: dict, db: AsyncSession) -> Optional[Post]:
|
||||
analysis = await analyze_post(text)
|
||||
|
||||
asset = analysis["asset"]
|
||||
# `tracked_asset`: the asset whose price impact we measure and display.
|
||||
# Use target_asset (the perp we actually trade — may be SOL/TRUMP/etc.)
|
||||
# when available; fall back to the sentiment asset (BTC/ETH) otherwise.
|
||||
# Bug fix: previously always used `asset` (BTC/ETH), which measured the
|
||||
# wrong price move when the bot traded a different perp.
|
||||
tracked_asset = analysis.get("target_asset") or asset
|
||||
|
||||
# Only capture the price AT post time. The m5/m15/m1h peaks are filled in
|
||||
# asynchronously by price_impact_monitor as the windows elapse — avoids
|
||||
# recording 0.00% because future candles don't exist yet at entry time.
|
||||
price_at_post = None
|
||||
if asset and analysis["relevant"]:
|
||||
price_at_post = price_store.get_price_at(asset, published_at)
|
||||
if tracked_asset and analysis["relevant"]:
|
||||
price_at_post = price_store.get_price_at(tracked_asset, published_at)
|
||||
|
||||
post = Post(
|
||||
external_id=external_id,
|
||||
@@ -131,8 +138,8 @@ async def _process_entry(entry: dict, db: AsyncSession) -> Optional[Post]:
|
||||
prefilter_reason=analysis.get("prefilter_reason"),
|
||||
analysis_version=analysis.get("analysis_version"),
|
||||
relevant=analysis["relevant"],
|
||||
# `asset` (BTC/ETH only) feeds the existing price_impact tracker.
|
||||
price_impact_asset=asset if analysis["relevant"] else None,
|
||||
# Track the actually-traded asset (target_asset ?? sentiment_asset).
|
||||
price_impact_asset=tracked_asset if analysis["relevant"] else None,
|
||||
price_impact_m5=None, # filled by price_impact_monitor after 5 m
|
||||
price_impact_m15=None, # filled by price_impact_monitor after 15 m
|
||||
price_impact_m1h=None, # filled by price_impact_monitor after 1 h
|
||||
@@ -146,11 +153,11 @@ async def _process_entry(entry: dict, db: AsyncSession) -> Optional[Post]:
|
||||
await db.flush()
|
||||
|
||||
# Register with the live peak tracker so it starts watching immediately.
|
||||
if asset and analysis["relevant"] and price_at_post:
|
||||
if tracked_asset and analysis["relevant"] and price_at_post:
|
||||
from app.services.price_impact_monitor import register_post
|
||||
register_post(
|
||||
post_id=post.id,
|
||||
asset=asset,
|
||||
asset=tracked_asset,
|
||||
signal=analysis.get("signal"),
|
||||
entry_price=price_at_post,
|
||||
published_at=published_at,
|
||||
@@ -219,8 +226,9 @@ async def poll_truth_social(db_session_factory) -> None:
|
||||
for post in new_posts:
|
||||
await manager.broadcast(_post_to_ws_payload(post))
|
||||
logger.info("Saved new post id=%d: %s", post.id, post.text[:60])
|
||||
# Telegram fan-out (fire-and-forget). Only fires if
|
||||
# signal is buy/short; noise posts are filtered inside.
|
||||
# Telegram fan-out (fire-and-forget). _dispatch filters
|
||||
# internally: buy/short → per-subscriber + public channel;
|
||||
# relevant-but-hold → public channel only; noise → dropped.
|
||||
try:
|
||||
from app.services.telegram import notify_signal
|
||||
notify_signal(post)
|
||||
|
||||
Reference in New Issue
Block a user