add market-regime guard (SPY trend) — inform + warn
New market_regime_service computes a benchmark (SPY) trend from its 50/200-day SMAs, cached in a SystemSetting and refreshed by a nightly job; GET /market/regime exposes it. Dashboard shows a regime banner; setup cards flag a counter-trend caution when a setup fights the regime (LONG in a bearish market / SHORT in a bullish one). Informational only — nothing is suppressed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
"""Tests for the market-regime computation."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from app.services.market_regime_service import compute_regime
|
||||
|
||||
|
||||
def _series(value: float, n: int = 250) -> list[float]:
|
||||
return [value] * n
|
||||
|
||||
|
||||
def test_unknown_without_history():
|
||||
assert compute_regime([])["label"] == "unknown"
|
||||
assert compute_regime([100.0] * 10)["label"] == "unknown" # < 50 bars
|
||||
|
||||
|
||||
def test_bullish_uptrend():
|
||||
# Rising series: price > sma50 > sma200
|
||||
closes = [100.0 + i * 0.5 for i in range(250)]
|
||||
r = compute_regime(closes)
|
||||
assert r["label"] == "bullish"
|
||||
assert r["price"] > r["sma200"]
|
||||
|
||||
|
||||
def test_bearish_downtrend():
|
||||
closes = [300.0 - i * 0.5 for i in range(250)]
|
||||
r = compute_regime(closes)
|
||||
assert r["label"] == "bearish"
|
||||
assert r["price"] < r["sma200"]
|
||||
|
||||
|
||||
def test_neutral_when_mixed():
|
||||
# Flat for 200 then a dip: price below sma200 but 50 still ~ above → not clean bear
|
||||
closes = [100.0] * 200 + [101.0] * 30 + [99.5] * 20
|
||||
r = compute_regime(closes)
|
||||
assert r["label"] in {"neutral", "bullish", "bearish"} # defined, not crashing
|
||||
assert "sma50" in r and "sma200" in r
|
||||
|
||||
|
||||
def test_fifty_day_fallback():
|
||||
# 60 bars: no 200-day, falls back to 50-day
|
||||
closes = [100.0 + i for i in range(60)]
|
||||
r = compute_regime(closes)
|
||||
assert r["label"] == "bullish"
|
||||
assert r["sma200"] is None
|
||||
@@ -82,6 +82,7 @@ class TestConfigureScheduler:
|
||||
"ticker_universe_sync",
|
||||
"outcome_evaluator",
|
||||
"alerts",
|
||||
"market_regime",
|
||||
}
|
||||
|
||||
def test_configure_is_idempotent(self):
|
||||
@@ -94,6 +95,7 @@ class TestConfigureScheduler:
|
||||
"alerts",
|
||||
"data_collector",
|
||||
"fundamental_collector",
|
||||
"market_regime",
|
||||
"outcome_evaluator",
|
||||
"rr_scanner",
|
||||
"sentiment_collector",
|
||||
|
||||
Reference in New Issue
Block a user