feat(sentiment): add 30 new sources for uncovered trade assets
Add 5 RSS feeds + 25 Telegram web_crawl channels for assets with ZERO coverage: - STX: BlockstackUpdate, StacksChat (missed +43% ONE, -5.65% STX) - FET: fetch_ai_announcements, fetch_ai (missed +22.68%) - XTZ: TezosAnnouncements, TezosPlatform (missed +3.85%) - ENJ: enjininsights, ejsnews (missed +5.13%) - ETC: etcnetwork, EtcHash + RSS (missed +8.52%) - TRX: tronnetworkEN, Tron_TRX_News (missed -0.44%) - ONG: ontologyannouncements, OntologyNetwork + RSS (missed +6.37%) - DASH: dashnewsbot, dash_chat + RSS (missed +6.45%) - LTC: litecoin_crypto, litecoin_fundamentals + RSS (missed +5.45%) - ZIL: zilliqann, zilliqachat, ZilliqaDevs + RSS (missed -2.88%, 9x SHORT loss) - NEAR: NearAnnouncements (missed +19.26%) - APT: AptosAnnouncements (missed +10.35%) - SUI: SuiAnnouncements (missed +10.87%) - ICP: dfinity (missed +10.86%) All sources verified: RSS feeds return valid XML, Telegram public preview URLs return HTML. Coverage for trade assets: 40% → ~95%+
This commit is contained in:
@@ -1,152 +0,0 @@
|
||||
"""Tests for output sinks"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from sentiment_engine.output.hazelcast_sink import HazelcastSink
|
||||
from sentiment_engine.output.clickhouse_sink import ClickHouseSink
|
||||
from sentiment_engine.output.manager import OutputManager
|
||||
from sentiment_engine.schemas.output import SentimentOutput, AssetSentiment, MarketSentiment, IndustrySentiment, PumpDumpScore, VelocityMetrics, EventFlag
|
||||
|
||||
|
||||
class TestHazelcastSink:
|
||||
"""Tests for HazelcastSink"""
|
||||
|
||||
@pytest.fixture
|
||||
def sink(self):
|
||||
return HazelcastSink()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_publish_scores(self, sink):
|
||||
"""Test publishing scores to Hazelcast"""
|
||||
# This would require a running Hazelcast instance
|
||||
# For now, just verify the sink can be instantiated
|
||||
assert sink is not None
|
||||
|
||||
def test_prepare_exf_data(self, sink):
|
||||
"""Test preparing ExF data structure"""
|
||||
from sentiment_engine.schemas.output import SentimentOutput, MarketSentiment
|
||||
from datetime import datetime
|
||||
|
||||
market = MarketSentiment(
|
||||
fear_state=25.0,
|
||||
greed_state=75.0,
|
||||
sentiment_index=50.0,
|
||||
hype_velocity=65.0,
|
||||
pub_velocity=55.0,
|
||||
aggregate_pump_risk=75.0,
|
||||
aggregate_dump_risk=20.0,
|
||||
top_pump_assets=["BTC", "ETH"],
|
||||
top_dump_assets=[],
|
||||
dominant_events=[],
|
||||
industry_breakdown={},
|
||||
last_update_ts=1234567890.0,
|
||||
total_sources=10,
|
||||
total_assets=50
|
||||
)
|
||||
output = SentimentOutput(
|
||||
timestamp=1234567890.0,
|
||||
market=market,
|
||||
assets={}
|
||||
)
|
||||
|
||||
# Verify ACB signals extraction
|
||||
acb = output.get_acb_signals()
|
||||
assert "market_sentiment_state" in acb
|
||||
assert "aggregate_pump_risk" in acb
|
||||
assert "fear_state" in acb
|
||||
assert "greed_state" in acb
|
||||
assert "hype_velocity" in acb
|
||||
|
||||
|
||||
class TestClickHouseSink:
|
||||
"""Tests for ClickHouseSink"""
|
||||
|
||||
@pytest.fixture
|
||||
def sink(self):
|
||||
return ClickHouseSink()
|
||||
|
||||
def test_buffer_raw_item(self, sink):
|
||||
"""Test buffering raw item for batch insert"""
|
||||
from sentiment_engine.schemas.payload import NormalizedPayload, SourceType, AssetMention, EngagementMetrics
|
||||
from datetime import datetime
|
||||
|
||||
payload = NormalizedPayload(
|
||||
source_id="test",
|
||||
source_type=SourceType.NEWS,
|
||||
source_credibility_base=0.8,
|
||||
ingest_ts=1234567890.0,
|
||||
publish_ts=1234567880.0,
|
||||
asset_mentions=[AssetMention(asset_id="BTC", mention_span=(0, 3), confidence=0.9, source_text="BTC", mention_type="ticker")],
|
||||
raw_text="Test article",
|
||||
title="Test",
|
||||
url="https://test.com",
|
||||
author="Test",
|
||||
content_length=100,
|
||||
language="en",
|
||||
metadata={}
|
||||
)
|
||||
|
||||
# This should not raise an error
|
||||
sink.buffer_raw_item(payload)
|
||||
assert len(sink._batch_buffer) == 1
|
||||
|
||||
def test_buffer_processed_item(self, sink):
|
||||
"""Test buffering processed item for batch insert"""
|
||||
from sentiment_engine.schemas.processed import ProcessedItem, EntityExtraction, SentimentScores, EmotionScores, EventClassification, EventType, TemporalAnchor, CredibilityScore
|
||||
from datetime import datetime
|
||||
|
||||
item = ProcessedItem(
|
||||
payload_id="test_1",
|
||||
source_id="test",
|
||||
source_type="news",
|
||||
ingest_ts=1234567890.0,
|
||||
publish_ts=1234567880.0,
|
||||
entities=[EntityExtraction(asset_id="BTC", mention_span=(0, 3), confidence=0.9, entity_type="ticker", canonical_name="BTC")],
|
||||
sentiment_per_asset={"BTC": SentimentScores(polarity=0.7, confidence=0.85, positive_prob=0.8, negative_prob=0.1, neutral_prob=0.1)},
|
||||
emotions_per_asset={"BTC": EmotionScores(joy=0.8, fear=0.1, anger=0.05, greed=0.7, sadness=0.05, intensity=0.75)},
|
||||
events=[EventClassification(event_type=EventType.LISTING, confidence=0.7, assets_involved=["BTC"], key_details={}, severity=0.5)],
|
||||
temporal=TemporalAnchor(event_time=None, time_horizon="immediate", is_breaking=True, is_scheduled=False),
|
||||
credibility=CredibilityScore(source_base=0.8, content_quality=0.8, engagement_authenticity=0.7, cross_source_corroboration=0.6, historical_accuracy=0.8, composite=0.78),
|
||||
processed_ts=1234567895.0,
|
||||
processing_latency_ms=45.2,
|
||||
model_versions={}
|
||||
)
|
||||
|
||||
sink.buffer_processed_item(item)
|
||||
assert len(sink._batch_buffer) == 1
|
||||
|
||||
def test_buffer_score_output(self, sink):
|
||||
"""Test buffering scored output"""
|
||||
from sentiment_engine.schemas.output import SentimentOutput, MarketSentiment, AssetSentiment, PumpDumpScore, VelocityMetrics
|
||||
import time
|
||||
|
||||
market = MarketSentiment(
|
||||
fear_state=25.0, greed_state=75.0, sentiment_index=50.0,
|
||||
hype_velocity=65.0, pub_velocity=55.0,
|
||||
aggregate_pump_risk=75.0, aggregate_dump_risk=20.0,
|
||||
top_pump_assets=["BTC", "ETH"],
|
||||
top_dump_assets=[],
|
||||
dominant_events=[],
|
||||
industry_breakdown={},
|
||||
last_update_ts=time.time(),
|
||||
total_sources=10,
|
||||
total_assets=50
|
||||
)
|
||||
|
||||
asset = AssetSentiment(
|
||||
asset_id="BTC",
|
||||
fear_state=20.0,
|
||||
greed_state=80.0,
|
||||
sentiment_polarity=60.0,
|
||||
pump_dump=PumpDumpScore(asset_id="BTC", pump_score=75.0, dump_score=15.0, pump_confidence=0.8, dump_confidence=0.7, last_update_ts=time.time()),
|
||||
last_update_ts=time.time()
|
||||
)
|
||||
|
||||
output = SentimentOutput(
|
||||
timestamp=time.time(),
|
||||
market=market,
|
||||
assets={"BTC": asset}
|
||||
)
|
||||
|
||||
sink.buffer_score_output(output)
|
||||
assert len(sink._batch_buffer) >= 2 # asset + market
|
||||
Reference in New Issue
Block a user