From eb8ee9ea910bcec7d3e9e56268d80d86a6c219b5 Mon Sep 17 00:00:00 2001 From: k Date: Fri, 29 May 2026 20:04:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(kol-v2):=20fix=20soft-signal=20recall=20?= =?UTF-8?q?=E2=80=94=20bullish/bearish=20over=20mention,=20category=20tick?= =?UTF-8?q?ers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two failure modes addressed: 1. Soft/indirect bullish signals classified as 'mention' instead of 'bullish' The prompt now explicitly lists casual English and Chinese expressions that count as bullish/bearish (not mention): - EN: 'looks interesting here', 'has room to run', 'wouldn't be short' - ZH: '感觉还有空间', '可以关注', '这里不错', '有上涨空间' mention is now reserved for ZERO directional lean (historical refs, neutral examples). Previously the vague boundary caused the model to use mention conservatively, losing real signals. 2. Category-level calls lost when no specific ticker named 'memecoin season', 'memes look good', '梗币最近不错' had no ticker to attach to. Added synthetic tickers: MEME, ALTCOIN, DEFI, AI — the model uses these when the view is about the category, not a specific coin. Normalizer allows synthetic tickers through the len>12 guard. Co-Authored-By: Claude Sonnet 4.6 --- app/services/kol_analysis.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/app/services/kol_analysis.py b/app/services/kol_analysis.py index d203812..7fe6a29 100644 --- a/app/services/kol_analysis.py +++ b/app/services/kol_analysis.py @@ -107,10 +107,28 @@ TICKERS: - action values: "buy"/"sell" → author explicitly states a position action ("I bought", "we are long", "我们减仓了", "added to my bag", "taking profits", "已建仓", "我加仓了", "建了仓") "reduce" → partially exiting or taking profits on an existing long ("trimming", "taking some off", "减了一部分", "止盈了一些") - "bullish"/"bearish" → directional view without explicit position statement - "mention" → ticker appears but no clear stance — use sparingly, only when ticker is central to the post + "bullish" → ANY expression of upside view, including SOFT/INDIRECT/CASUAL language. Err on the side of bullish over mention. + English soft bullish: "X looks interesting here", "I like X at these levels", "X has room to run", + "wouldn't be short X", "X is setting up well", "compelling setup in X", "X is undervalued", + "I'd be a buyer of X", "X deserves attention here", "keeping an eye on X" + Chinese soft bullish: "感觉还有空间", "这里可以关注", "值得关注", "看好", "还有机会", + "这个位置不错", "可以考虑", "有上涨空间", "这波不错", "可以关注一下", "挺有意思的" + "bearish" → ANY expression of downside view or caution, including soft signals. + English soft bearish: "X looks extended", "I'd be careful here", "not touching X", + "X has further to fall", "selling into strength on X", "X is a trap" + Chinese soft bearish: "感觉要跌", "要小心", "不敢碰", "还会跌", "高位了", "风险很大" + "mention" → ticker appears but author has ZERO directional lean — pure neutral reference, + historical context only, or just used as an example. If there is ANY lean at all, + use bullish or bearish instead. Reserve mention for truly neutral uses like: + "BTC started in 2009" or "unlike ETH, which uses proof-of-stake" - Dedupe per ticker — at most one entry per symbol; pick the strongest action. - Do NOT invent tickers. Skip "$XYZ" if unsure it is a real crypto token. +- For CATEGORY-LEVEL calls (no specific ticker named), use these synthetic tickers: + "MEME" → author is bullish/bearish on memecoins as a category ("memes look good", "memecoin season", "梗币最近不错") + "ALTCOIN" → author is bullish/bearish on altcoins broadly ("alts are waking up", "山寨季", "altseason") + "DEFI" → author is bullish/bearish on DeFi protocols broadly + "AI" → author is bullish/bearish on AI-related tokens broadly + Only use synthetic tickers when the view is genuinely about the category, not just a passing mention. - conviction: 0.8+ = explicit + repeated + sized or timed; 0.5–0.7 = clear view, no commitment; <0.5 = passing reference. - timeframe: "immediate" = acting now or within 24h; "days" = 1–7d; "weeks" = 1–4w; "months" = 1+ months; "unspecified" = not stated. - Do not include fiat (USD/CNY/JPY) or stablecoins (USDT/USDC/DAI/FRAX/USDE) unless the post's main thesis is about them. @@ -246,12 +264,19 @@ async def extract_kol_signal( valid_actions = {"buy", "sell", "reduce", "bullish", "bearish", "mention"} valid_timeframes = {"immediate", "days", "weeks", "months", "unspecified"} valid_post_types = {"trade_update", "macro_thesis", "research", "news_recap", "opinion", "other"} + # Synthetic category tickers — allowed even though they're not real on-chain tokens. + # Used when the KOL expresses a view on a category without naming specific coins. + SYNTHETIC_TICKERS = {"MEME", "ALTCOIN", "DEFI", "AI"} for t in tickers: if not isinstance(t, dict): continue sym = (t.get("ticker") or "").strip().upper() - if not sym or len(sym) > 12: + if not sym: + continue + # Allow synthetic category tickers; reject anything else > 12 chars + # (12 chars covers most real tickers; synthetic ones are all ≤ 7 chars) + if sym not in SYNTHETIC_TICKERS and len(sym) > 12: continue action = (t.get("action") or "mention").lower() if action not in valid_actions: