import math from app.schemas.schemas import PriceData from app.services.ai_context_builder import AIContextBuilder def _build_sample_price_data(bars: int = 220) -> list[PriceData]: base_price = 1950.0 price_data: list[PriceData] = [] for i in range(bars): drift = i * 0.25 wave = math.sin(i / 7.0) * 3.0 close = base_price + drift + wave high = close + 0.8 low = close - 0.8 open_price = close - math.sin(i / 11.0) * 0.5 price_data.append( PriceData( time=i, open=open_price, high=high, low=low, close=close, volume=1000 + i, ) ) return price_data def test_build_metrics_includes_enhanced_indicators(): price_data = _build_sample_price_data() builder = AIContextBuilder() metrics = builder.build_metrics("XAUUSD", "1m", price_data) assert metrics.bb_basis is not None assert metrics.bb_upper is not None assert metrics.bb_lower is not None assert metrics.rsi3 is not None assert metrics.zlsma is not None assert metrics.chandelier_long_stop is not None assert metrics.chandelier_short_stop is not None assert metrics.chandelier_signal in {None, "LONG", "SHORT", "NEUTRAL"} assert metrics.bb_signal in {None, "LONG", "SHORT"} def test_indicator_payload_emits_enhanced_metrics(): price_data = _build_sample_price_data() builder = AIContextBuilder() bars = [ { "time": item.time, "open": item.open, "high": item.high, "low": item.low, "close": item.close, "volume": item.volume, } for item in price_data ] indicator_payload = builder._build_indicators(bars) # type: ignore[attr-defined] indicator_names = {entry["name"] for entry in indicator_payload} assert "RSI_3" in indicator_names assert "BB_20_BASIS" in indicator_names assert "BB_20_UPPER" in indicator_names assert "BB_20_LOWER" in indicator_names assert "ZLSMA_50" in indicator_names assert "CHAND_22_LONG" in indicator_names assert "CHAND_22_SHORT" in indicator_names def test_candlestick_patterns_detected(): candles = [ PriceData(time=0, open=100.0, high=101.0, low=99.0, close=99.0, volume=1000), PriceData(time=1, open=99.2, high=100.0, low=95.2, close=95.5, volume=1005), PriceData(time=2, open=95.0, high=101.2, low=94.8, close=100.8, volume=1010), # Bullish engulfing candle PriceData(time=3, open=100.1, high=100.4, low=99.9, close=100.12, volume=1015), # Doji PriceData(time=4, open=99.8, high=100.2, low=95.5, close=99.7, volume=1020), # Long lower shadow ] builder = AIContextBuilder() metrics = builder.build_metrics("XAUUSD", "1m", candles) names = {signal.pattern for signal in metrics.pattern_signals} assert "Bullish Engulfing" in names assert "Doji" in names assert "Long Lower Shadow" in names