1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
"""Tests for stock selector engine."""
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from datetime import datetime, timezone
from decimal import Decimal
from shared.models import OrderSide
from shared.sentiment_models import SymbolScore, MarketSentiment, SelectedStock, Candidate
from strategy_engine.stock_selector import (
SentimentCandidateSource,
StockSelector,
_parse_llm_selections,
)
async def test_sentiment_candidate_source():
mock_db = MagicMock()
mock_db.get_top_symbol_scores = AsyncMock(return_value=[
{"symbol": "AAPL", "composite": 0.8, "news_count": 5},
{"symbol": "NVDA", "composite": 0.6, "news_count": 3},
])
source = SentimentCandidateSource(mock_db)
candidates = await source.get_candidates()
assert len(candidates) == 2
assert candidates[0].symbol == "AAPL"
assert candidates[0].source == "sentiment"
def test_parse_llm_selections_valid():
llm_response = """
[
{"symbol": "NVDA", "side": "BUY", "conviction": 0.85, "reason": "AI demand", "key_news": ["NVDA beats earnings"]},
{"symbol": "XOM", "side": "BUY", "conviction": 0.72, "reason": "Oil surge", "key_news": ["Oil prices up"]}
]
"""
selections = _parse_llm_selections(llm_response)
assert len(selections) == 2
assert selections[0].symbol == "NVDA"
assert selections[0].conviction == 0.85
def test_parse_llm_selections_invalid():
selections = _parse_llm_selections("not json")
assert selections == []
def test_parse_llm_selections_with_markdown():
llm_response = """
Here are my picks:
```json
[
{"symbol": "TSLA", "side": "BUY", "conviction": 0.7, "reason": "Momentum", "key_news": ["Tesla rally"]}
]
```
"""
selections = _parse_llm_selections(llm_response)
assert len(selections) == 1
assert selections[0].symbol == "TSLA"
async def test_selector_blocks_on_risk_off():
mock_db = MagicMock()
mock_db.get_latest_market_sentiment = AsyncMock(return_value={
"fear_greed": 15,
"fear_greed_label": "Extreme Fear",
"vix": 35.0,
"fed_stance": "neutral",
"market_regime": "risk_off",
"updated_at": datetime.now(timezone.utc),
})
selector = StockSelector(db=mock_db, broker=MagicMock(), alpaca=MagicMock(), anthropic_api_key="test")
result = await selector.select()
assert result == []
|