Разработка системы анализа настроений крипто-сообщества
Комплексная система анализа настроений объединяет сигналы из всех платформ крипто-сообщества в единый индекс, дающий целостную картину рыночного sentiment. Это не просто агрегация — это взвешенная, нормализованная, дедублицированная метрика с учётом специфики каждой пла��формы.
Архитектура многоисточниковой системы
Twitter/X ──────────┐
Reddit ─────────────┤
Telegram ───────────┼──► Sentiment Engine ──► Composite Index ──► API / Dashboard
Discord ────────────┤
News Sites ─────────┤
On-chain data ──────┘
Принцип: каждый источник обрабатывается независимым pipeline'ом, нормализуется к единой шкале [-1, 1], затем агрегируется с учётом временного лага и весового коэффициента.
Temporal dynamics разных платформ
Каждая платформа имеет разную скорость реакции на события:
| Платформа | Time lag до цены | Персистентность |
|---|---|---|
| Twitter/X | 0.5–2h | Короткая (часы) |
| Telegram | 0.5–3h | Короткая |
| 4–24h | Средняя (дни) | |
| News | 1–6h | Средняя |
| On-chain | 12–72h | Длинная (недели) |
Для краткосрочного (1h–4h) сигнала: Twitter + Telegram доминируют. Для среднесрочного (1d–1w): Reddit + News более информативны.
Нормализация и дедупликация
Нормализация: z-score по rolling 30-дневному окну для каждого источника:
def normalize_sentiment_source(scores, window_days=30, interval='1h'):
rolling_mean = scores.rolling(window_days * 24).mean()
rolling_std = scores.rolling(window_days * 24).std()
normalized = (scores - rolling_mean) / (rolling_std + 1e-8)
return normalized.clip(-3, 3) / 3 # в диапазон [-1, 1]
Дедупликация: одна и та же новость может появиться на нескольких платформах. Semantic similarity threshold: если two сигнала имеют косинусное сходство > 0.85 в sentence embeddings — это вероятно один и тот же event, учитываем один раз с усиленным весом.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
def deduplicate_signals(signals, similarity_threshold=0.85):
texts = [s['text'] for s in signals]
embeddings = model.encode(texts)
# Cosine similarity matrix
from sklearn.metrics.pairwise import cosine_similarity
sim_matrix = cosine_similarity(embeddings)
# Кластеризуем похожие сигналы
seen = set()
deduped = []
for i, signal in enumerate(signals):
if i in seen:
continue
duplicates = [j for j in range(i+1, len(signals))
if sim_matrix[i][j] > similarity_threshold]
seen.update(duplicates)
# Берём сигнал с наибольшим engagement как представителя кластера
cluster = [signal] + [signals[j] for j in duplicates]
best = max(cluster, key=lambda x: x.get('engagement', 0))
best['boost'] = len(cluster) # усиливаем если много источников
deduped.append(best)
return deduped
Взвешенный Composite Index
class CompositeSentimentIndex:
WEIGHTS = {
'twitter': {'short': 0.30, 'medium': 0.15, 'long': 0.05},
'telegram': {'short': 0.25, 'medium': 0.10, 'long': 0.05},
'reddit': {'short': 0.10, 'medium': 0.25, 'long': 0.20},
'news': {'short': 0.15, 'medium': 0.25, 'long': 0.20},
'on_chain': {'short': 0.05, 'medium': 0.15, 'long': 0.35},
'fear_greed': {'short': 0.15, 'medium': 0.10, 'long': 0.15}
}
def compute(self, signals_dict, horizon='short'):
total_weight = sum(self.WEIGHTS[src][horizon] for src in signals_dict
if src in self.WEIGHTS)
composite = sum(
signals_dict[src] * self.WEIGHTS[src][horizon]
for src in signals_dict
if src in self.WEIGHTS
) / max(total_weight, 0.01)
return composite
def get_multi_horizon(self, signals_dict):
return {
'short': self.compute(signals_dict, 'short'), # 1-4h
'medium': self.compute(signals_dict, 'medium'), # 1-7d
'long': self.compute(signals_dict, 'long') # 1-4w
}
Sentiment regimes
Агрегированный sentiment классифицируется в режимы:
| Composite Score | Режим | Торговая интерпретация |
|---|---|---|
| > 0.6 | Extreme Greed | Осторожность, возможный разворот |
| 0.2–0.6 | Greed | Бычий bias |
| -0.2–0.2 | Neutral | Без сильного сигнала |
| -0.6 – -0.2 | Fear | Медвежий bias, возможен ребаунд |
| < -0.6 | Extreme Fear | Исторически хорошая точка покупки |
Regime change detection: переход из одного режима в другой — торговый сигнал.
Backtesting composite sentiment
def backtest_composite_sentiment(sentiment_history, price_returns,
signal_threshold=0.3, horizon_hours=24):
signals = []
for timestamp, score in sentiment_history.items():
if abs(score) > signal_threshold:
direction = 'long' if score > 0 else 'short'
# Цена через horizon_hours
future_return = get_price_return(price_returns, timestamp, horizon_hours)
correct = (score > 0 and future_return > 0) or (score < 0 and future_return < 0)
signals.append({
'score': score, 'direction': direction,
'future_return': future_return, 'correct': correct
})
df = pd.DataFrame(signals)
accuracy = df['correct'].mean()
avg_return_on_signal = df['future_return'].mean()
return {
'accuracy': accuracy,
'n_signals': len(signals),
'avg_return': avg_return_on_signal,
'sharpe_of_signals': df['future_return'].mean() / df['future_return'].std()
}
Real-time dashboard
Компоненты дашборда:
- Главный gauge: composite sentiment (-100 to +100)
- Multi-horizon панель: short/medium/long sentiment
- Source breakdown: вклад каждого источника
- Trend chart: последние 7 дней sentiment timeline vs цена
- Top trending: токены с наибольшим изменением sentiment за 24h
- Alert feed: последние high-impact события
Технический стек: Python (transformers, pandas, scikit-learn), Apache Kafka для streaming aggregation, PostgreSQL + TimescaleDB для хранения, Redis для realtime кэширования, React + Recharts для dashboard, FastAPI для REST/WebSocket API.
Разрабатываем полноценную multi-source sentiment систему с нормализацией, дедупликацией, composite index, backtesting модулем и realtime dashboard.







