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:
@@ -4,8 +4,9 @@ Takes a long-form post (Substack essay) or tweet and returns:
|
||||
- summary: one Chinese sentence on what this post is about
|
||||
- tickers: list of {ticker, action, conviction, quote}
|
||||
|
||||
action ∈ bullish | bearish | buy | sell | mention
|
||||
action ∈ bullish | bearish | buy | sell | reduce | mention
|
||||
- buy/sell → KOL explicitly states they bought/sold or are entering/exiting
|
||||
- reduce → KOL is partially exiting / taking profits on an existing long
|
||||
- bullish/bearish → directional view without an explicit position statement
|
||||
- mention → ticker appears but no clear stance (don't flood with these)
|
||||
|
||||
@@ -63,32 +64,46 @@ def _oai() -> AsyncOpenAI:
|
||||
return _openai_client
|
||||
|
||||
|
||||
SYSTEM_PROMPT = """You are an analyst extracting tradeable signals from crypto KOL posts.
|
||||
SYSTEM_PROMPT = """You are an analyst extracting tradeable signals from crypto KOL (Key Opinion Leader) posts.
|
||||
|
||||
The author is a known crypto KOL. Your job: distill what they said and which tokens they are talking about RIGHT NOW (not historical references).
|
||||
The author is a known crypto KOL. Your job: distill what they said and which tokens they are talking about RIGHT NOW (not historical references). Pay special attention to DIVERGENCE between what they say publicly and what they imply about their actual position — this is the platform's highest-conviction signal.
|
||||
|
||||
Output **strict JSON only**, no markdown, no preface. Schema:
|
||||
|
||||
{
|
||||
"summary": "<one sentence, ≤60 chars/字. If signal exists, state the author's current thesis. If no signal, describe the post topic. Match the post's primary language (中文文章用中文, English 用英文).>",
|
||||
"summary": "<one sentence in ENGLISH, ≤80 chars. State the author's current market thesis if they have one, or describe the post topic if no clear signal. Always English regardless of the post's original language.>",
|
||||
"tickers": [
|
||||
{
|
||||
"ticker": "<UPPERCASE symbol, e.g. BTC, ETH, HYPE, SOL>",
|
||||
"action": "buy" | "sell" | "bullish" | "bearish" | "mention",
|
||||
"conviction": <float 0.0-1.0>,
|
||||
"quote": "<shortest verbatim sentence from the post supporting this call, ≤200 chars. Use the post's original language — do not translate.>"
|
||||
"ticker": "<UPPERCASE symbol, e.g. BTC, ETH, HYPE, SOL>",
|
||||
"action": "buy" | "sell" | "bullish" | "bearish" | "reduce" | "mention",
|
||||
"conviction": <float 0.0-1.0>,
|
||||
"timeframe": "immediate" | "days" | "weeks" | "months" | "unspecified",
|
||||
"stance_change": <true if this contradicts or reverses what the author said in earlier parts of the same post, or walks back a previously stated position | false>,
|
||||
"quote": "<shortest verbatim sentence from the post supporting this call, ≤200 chars. Keep original language — do not translate.>"
|
||||
}
|
||||
]
|
||||
],
|
||||
"talks_vs_trades_flag": <true if you detect a mismatch between the KOL's stated bullish/bearish narrative and signals of the opposite actual position (e.g. claiming bullish while describing reducing size, or bearish while mentioning recent buys) | false>
|
||||
}
|
||||
|
||||
Rules:
|
||||
- Always return summary in ENGLISH regardless of the post language.
|
||||
- If the post is macro commentary, news recap, or sponsored content with no specific token call, return tickers=[] and summary describing the topic.
|
||||
- IGNORE historical price references ("BTC bottomed at $60k earlier this year") — these are context, not current calls.
|
||||
- IGNORE advertising/sponsor sections — look for cues: "sponsor", "partner", "use code", "promo code", "this episode brought to you by", "ad", "广告", "赞助". Skip any ticker only mentioned inside such a section.
|
||||
- buy/sell only when the author states a position action ("I bought", "we are long", "我们减仓了", "added to my bag"). Otherwise use bullish/bearish for directional views, or mention for passing references.
|
||||
- action values:
|
||||
"buy"/"sell" → author explicitly states a position action ("I bought", "we are long", "我们减仓了", "added to my bag", "taking profits", "已建仓")
|
||||
"reduce" → author is partially exiting or taking profits on a long-held position (distinct from "sell" which implies closing)
|
||||
"bullish"/"bearish" → directional view without explicit position statement
|
||||
"mention" → ticker appears but no clear stance
|
||||
- Dedupe per ticker — at most one entry per symbol; pick the strongest action.
|
||||
- Do NOT invent tickers. If you see "$XYZ" but unsure it's a real token, skip it.
|
||||
- conviction: 0.8+ requires explicit + repeated + sized/timed view; 0.5-0.7 for clear directional view without commitment; <0.5 for passing references.
|
||||
- timeframe: "immediate" = author is acting now or within 24h; "days" = 1-7 days; "weeks" = 1-4 weeks; "months" = 1+ months; "unspecified" = no timeframe given.
|
||||
- talks_vs_trades_flag: set true when narrative reads one way but position signals read the other. Examples:
|
||||
- Author writes a bullish thesis but mentions "reducing", "taking profits", "trimming", "risk management"
|
||||
- Author is bearish on macro but "accumulating at these levels"
|
||||
- High-conviction public call ("this is THE entry") but with very low disclosed position size
|
||||
- Previously loudly bullish post, but this post avoids reaffirming the position
|
||||
- Do not include fiat (USD/CNY/JPY) or stablecoins (USDT/USDC/DAI/FRAX) unless the post's main thesis is about them.
|
||||
"""
|
||||
|
||||
@@ -194,6 +209,8 @@ async def extract_kol_signal(
|
||||
# Normalize
|
||||
tickers = data.get("tickers") or []
|
||||
cleaned = []
|
||||
valid_actions = {"buy", "sell", "reduce", "bullish", "bearish", "mention"}
|
||||
valid_timeframes = {"immediate", "days", "weeks", "months", "unspecified"}
|
||||
for t in tickers:
|
||||
if not isinstance(t, dict):
|
||||
continue
|
||||
@@ -201,23 +218,32 @@ async def extract_kol_signal(
|
||||
if not sym or len(sym) > 12:
|
||||
continue
|
||||
action = (t.get("action") or "mention").lower()
|
||||
if action not in {"buy", "sell", "bullish", "bearish", "mention"}:
|
||||
if action not in valid_actions:
|
||||
action = "mention"
|
||||
try:
|
||||
conv = float(t.get("conviction") or 0)
|
||||
except (TypeError, ValueError):
|
||||
conv = 0.0
|
||||
conv = max(0.0, min(1.0, conv))
|
||||
timeframe = (t.get("timeframe") or "unspecified").lower()
|
||||
if timeframe not in valid_timeframes:
|
||||
timeframe = "unspecified"
|
||||
stance_change = bool(t.get("stance_change", False))
|
||||
cleaned.append({
|
||||
"ticker": sym,
|
||||
"action": action,
|
||||
"conviction": round(conv, 2),
|
||||
"timeframe": timeframe,
|
||||
"stance_change": stance_change,
|
||||
"quote": (t.get("quote") or "")[:200],
|
||||
})
|
||||
|
||||
talks_vs_trades = bool(data.get("talks_vs_trades_flag", False))
|
||||
|
||||
return {
|
||||
"summary": (data.get("summary") or "").strip() or None,
|
||||
"tickers": cleaned,
|
||||
"talks_vs_trades_flag": talks_vs_trades,
|
||||
"model": model,
|
||||
"version": ANALYSIS_VERSION,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user