Files
trumpsignal-backend/scripts/backfill_signals.py
T
k d6c802ef26 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>
2026-05-29 11:57:19 +08:00

97 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Backfill AI analysis onto historical Truth posts.
Safety rules:
* Only re-analyze `source='truth'` rows. Technical/scanner posts already
carry their own signal payloads and must not be sent through the Trump
text analyzer.
* Persist the full analysis payload so history doesn't become a mixed schema
of old partial rows and new v5 rows.
"""
import asyncio
import argparse
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.services.analysis import analyze_post
from app.database import AsyncSessionLocal
from app.models import Post
from sqlalchemy import select
CONCURRENCY = 5 # 并发数,避免触发API限速
async def process_one(post: Post, semaphore: asyncio.Semaphore, overwrite: bool) -> str:
"""分析单条帖子并写回DB,返回状态字符串"""
if not overwrite and post.signal is not None:
return "skip"
async with semaphore:
analysis = await analyze_post(post.text)
await asyncio.sleep(0.1) # 轻度限速
async with AsyncSessionLocal() as db:
result = await db.execute(select(Post).where(Post.id == post.id))
p = result.scalar_one_or_none()
if p:
p.sentiment = analysis["sentiment"]
p.signal = analysis["signal"]
p.ai_confidence = analysis["confidence"]
p.ai_reasoning = analysis["reasoning"]
p.relevant = analysis["relevant"]
p.prefilter_reason = analysis.get("prefilter_reason")
p.analysis_version = analysis.get("analysis_version")
p.price_impact_asset = analysis["asset"] if analysis["relevant"] else None
p.target_asset = analysis.get("target_asset")
p.category = analysis.get("category")
p.expected_move_pct = analysis.get("expected_move_pct")
p.invalidation_price = analysis.get("invalidation_price")
await db.commit()
sig = analysis["signal"]
return f"{sig}:{analysis['confidence']}%"
async def main(limit: int, overwrite: bool):
async with AsyncSessionLocal() as db:
result = await db.execute(
select(Post)
.where(Post.source == "truth")
.order_by(Post.published_at.desc())
.limit(limit)
)
posts = result.scalars().all()
total = len(posts)
print(f"{total} 条帖子,并发={CONCURRENCY}overwrite={overwrite}\n")
semaphore = asyncio.Semaphore(CONCURRENCY)
tasks = [process_one(p, semaphore, overwrite) for p in posts]
done = 0
buy = short = sell = hold = skipped = 0
for coro in asyncio.as_completed(tasks):
status = await coro
done += 1
if status == "skip":
skipped += 1
elif status.startswith("buy"): buy += 1
elif status.startswith("short"): short += 1
elif status.startswith("sell"): sell += 1
else: hold += 1
if done % 20 == 0 or done == total:
print(f" 进度 {done}/{total} | BUY={buy} SHORT={short} SELL={sell} HOLD={hold} SKIP={skipped}")
print(f"\n✅ 完成: BUY={buy} SHORT={short} SELL={sell} HOLD={hold} SKIP={skipped}")
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("--limit", type=int, default=400)
ap.add_argument("--overwrite", action="store_true", help="重新分析已有signal的帖子")
args = ap.parse_args()
asyncio.run(main(args.limit, args.overwrite))