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: