| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- from hermes_mcp.narrative_engine import STANCE_TAXONOMY, build_narrative
- from hermes_mcp.state_engine import synthesize_state
- def _bullish_state_payload():
- concern = {"id": "c1", "account_id": "a1", "market_symbol": "BTCUSD", "status": "active"}
- regimes = [
- {"timeframe": "1m", "price": 101, "trend": {"ema_fast": 100, "ema_slow": 99, "sma_long": 98}, "momentum": {"rsi": 62, "macd_histogram": 0.02}, "volatility": {"atr_percent": 0.5}, "bands": {"bollinger": {"middle": 100, "upper": 102, "lower": 98}}, "vwap": 100.2, "reversal": {"direction": "none", "score": 0}},
- {"timeframe": "5m", "price": 102, "trend": {"ema_fast": 101, "ema_slow": 100, "sma_long": 99}, "momentum": {"rsi": 63, "macd_histogram": 0.018}, "volatility": {"atr_percent": 0.6}, "bands": {"bollinger": {"middle": 101, "upper": 103, "lower": 99}}, "vwap": 101.1, "reversal": {"direction": "none", "score": 0}},
- {"timeframe": "15m", "price": 103, "trend": {"ema_fast": 102, "ema_slow": 101, "sma_long": 99.5}, "momentum": {"rsi": 60, "macd_histogram": 0.012}, "volatility": {"atr_percent": 0.8}, "bands": {"bollinger": {"middle": 102, "upper": 104, "lower": 100}}, "vwap": 102.0, "reversal": {"direction": "none", "score": 0}},
- {"timeframe": "1h", "price": 104, "trend": {"ema_fast": 103, "ema_slow": 102, "sma_long": 100}, "momentum": {"rsi": 58, "macd_histogram": 0.01}, "volatility": {"atr_percent": 0.9}, "bands": {"bollinger": {"middle": 103, "upper": 105, "lower": 101}}, "vwap": 103.0, "reversal": {"direction": "none", "score": 0}},
- {"timeframe": "4h", "price": 106, "trend": {"ema_fast": 104, "ema_slow": 103, "sma_long": 101}, "momentum": {"rsi": 57, "macd_histogram": 0.009}, "volatility": {"atr_percent": 1.1}, "bands": {"bollinger": {"middle": 104, "upper": 107, "lower": 101}}, "vwap": 104.5, "reversal": {"direction": "none", "score": 0}},
- {"timeframe": "1d", "price": 110, "trend": {"ema_fast": 108, "ema_slow": 106, "sma_long": 103}, "momentum": {"rsi": 61, "macd_histogram": 0.008}, "volatility": {"atr_percent": 1.4}, "bands": {"bollinger": {"middle": 107, "upper": 112, "lower": 102}}, "vwap": 108.0, "reversal": {"direction": "none", "score": 0}},
- ]
- state = synthesize_state(concern=concern, regimes=regimes)
- return concern, state.payload
- def test_build_narrative_produces_stable_taxonomy_output():
- concern, payload = _bullish_state_payload()
- narrative = build_narrative(concern=concern, state_payload=payload)
- assert narrative.payload["stance"] in STANCE_TAXONOMY
- assert isinstance(narrative.key_drivers, list)
- assert isinstance(narrative.risk_flags, list)
- assert isinstance(narrative.uncertainties, list)
- assert 0.2 <= narrative.confidence <= 0.95
- def test_build_narrative_describes_opportunity_without_deciding():
- concern, payload = _bullish_state_payload()
- narrative = build_narrative(concern=concern, state_payload=payload)
- opportunity_map = narrative.payload["opportunity_map"]
- assert set(opportunity_map) == {"continuation", "mean_reversion", "reversal", "wait"}
- assert max(opportunity_map, key=opportunity_map.get) in {"continuation", "wait", "mean_reversion", "reversal"}
- assert "buy now" not in narrative.summary.lower()
- assert "sell now" not in narrative.summary.lower()
- def test_build_narrative_preserves_argus_context_as_context_only():
- concern, payload = _bullish_state_payload()
- payload["argus_context"] = {
- "snapshot_regime": "risk_off",
- "snapshot_confidence": 0.72,
- "snapshot_summary": "macro stress remains elevated",
- "regime": "real_asset_pressure",
- "regime_confidence": 0.83,
- "regime_summary": "gold and silver confirm defensive cross-market pressure",
- }
- narrative = build_narrative(concern=concern, state_payload=payload)
- assert narrative.payload["argus_context"]["regime"] == "real_asset_pressure"
- assert any("argus context reads real_asset_pressure" in item for item in narrative.key_drivers)
- assert "buy now" not in narrative.summary.lower()
- assert "sell now" not in narrative.summary.lower()
|