Add live_pre_screen unit tests including min/max_atr_pct filters
12 tests covering all screener filters: min_price, min_atr_14, min_avg_dollar_volume, date selection (latest-before, future excluded), and the V23 quality filters min_atr_pct / max_atr_pct added last session but previously untested. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
c5fa1394ed
commit
11d1bf6bfe
@ -0,0 +1,123 @@
|
|||||||
|
"""Unit tests for apps.orb_trader.screener.live_pre_screen."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from apps.orb_trader.screener import live_pre_screen
|
||||||
|
from libs.intraday.domain import ORBStrategyParams
|
||||||
|
|
||||||
|
|
||||||
|
def _enrich(ticker: str, *, prev_close: float, atr_14: float, avg_dollar_vol: float) -> dict:
|
||||||
|
"""Build minimal enrichment dict for a single ticker."""
|
||||||
|
return {
|
||||||
|
ticker: {
|
||||||
|
"2026-01-04": {
|
||||||
|
"prev_close": prev_close,
|
||||||
|
"atr_14": atr_14,
|
||||||
|
"avg_dollar_vol_30d": avg_dollar_vol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _default_params(**overrides) -> ORBStrategyParams:
|
||||||
|
defaults = dict(
|
||||||
|
min_price=10.0,
|
||||||
|
min_atr_14=0.50,
|
||||||
|
min_avg_dollar_volume=25_000_000.0,
|
||||||
|
min_atr_pct=None,
|
||||||
|
max_atr_pct=None,
|
||||||
|
)
|
||||||
|
defaults.update(overrides)
|
||||||
|
return ORBStrategyParams(**defaults)
|
||||||
|
|
||||||
|
|
||||||
|
_DATE = "2026-01-05"
|
||||||
|
|
||||||
|
|
||||||
|
class TestBasicFilters:
|
||||||
|
def test_passes_all_filters(self):
|
||||||
|
enrich = _enrich("AAPL", prev_close=150.0, atr_14=3.0, avg_dollar_vol=100_000_000)
|
||||||
|
result = live_pre_screen(enrich, _DATE, _default_params())
|
||||||
|
assert "AAPL" in result
|
||||||
|
|
||||||
|
def test_fails_min_price(self):
|
||||||
|
enrich = _enrich("AAPL", prev_close=9.0, atr_14=0.8, avg_dollar_vol=30_000_000)
|
||||||
|
result = live_pre_screen(enrich, _DATE, _default_params(min_price=10.0))
|
||||||
|
assert "AAPL" not in result
|
||||||
|
|
||||||
|
def test_fails_min_atr_14(self):
|
||||||
|
enrich = _enrich("AAPL", prev_close=50.0, atr_14=0.3, avg_dollar_vol=50_000_000)
|
||||||
|
result = live_pre_screen(enrich, _DATE, _default_params(min_atr_14=0.50))
|
||||||
|
assert "AAPL" not in result
|
||||||
|
|
||||||
|
def test_fails_min_dollar_volume(self):
|
||||||
|
enrich = _enrich("AAPL", prev_close=50.0, atr_14=1.0, avg_dollar_vol=10_000_000)
|
||||||
|
result = live_pre_screen(enrich, _DATE, _default_params(min_avg_dollar_volume=25_000_000))
|
||||||
|
assert "AAPL" not in result
|
||||||
|
|
||||||
|
def test_uses_latest_enrichment_date(self):
|
||||||
|
# Two enrichment dates; the newer one passes filters, older one fails
|
||||||
|
enrich = {
|
||||||
|
"AAPL": {
|
||||||
|
"2025-12-31": {"prev_close": 5.0, "atr_14": 0.1, "avg_dollar_vol_30d": 1_000_000},
|
||||||
|
"2026-01-04": {"prev_close": 150.0, "atr_14": 3.0, "avg_dollar_vol_30d": 100_000_000},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = live_pre_screen(enrich, _DATE, _default_params())
|
||||||
|
assert "AAPL" in result
|
||||||
|
|
||||||
|
def test_future_enrichment_dates_excluded(self):
|
||||||
|
# date_str="2026-01-05"; enrichment only has "2026-01-06" → nothing qualifies
|
||||||
|
enrich = {
|
||||||
|
"AAPL": {
|
||||||
|
"2026-01-06": {"prev_close": 150.0, "atr_14": 3.0, "avg_dollar_vol_30d": 100_000_000},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = live_pre_screen(enrich, _DATE, _default_params())
|
||||||
|
assert "AAPL" not in result
|
||||||
|
|
||||||
|
|
||||||
|
class TestAtrPctFilters:
|
||||||
|
def test_min_atr_pct_passes(self):
|
||||||
|
# atr_14=3.0, prev_close=100.0 → atr_ratio=3% ≥ min_atr_pct=2% → pass
|
||||||
|
enrich = _enrich("AAPL", prev_close=100.0, atr_14=3.0, avg_dollar_vol=50_000_000)
|
||||||
|
params = _default_params(min_atr_pct=0.02)
|
||||||
|
assert "AAPL" in live_pre_screen(enrich, _DATE, params)
|
||||||
|
|
||||||
|
def test_min_atr_pct_fails(self):
|
||||||
|
# atr_14=1.0, prev_close=100.0 → atr_ratio=1% < min_atr_pct=2% → fail
|
||||||
|
enrich = _enrich("AAPL", prev_close=100.0, atr_14=1.0, avg_dollar_vol=50_000_000)
|
||||||
|
params = _default_params(min_atr_pct=0.02)
|
||||||
|
assert "AAPL" not in live_pre_screen(enrich, _DATE, params)
|
||||||
|
|
||||||
|
def test_max_atr_pct_passes(self):
|
||||||
|
# atr_ratio=3% < max_atr_pct=5% → pass
|
||||||
|
enrich = _enrich("AAPL", prev_close=100.0, atr_14=3.0, avg_dollar_vol=50_000_000)
|
||||||
|
params = _default_params(max_atr_pct=0.05)
|
||||||
|
assert "AAPL" in live_pre_screen(enrich, _DATE, params)
|
||||||
|
|
||||||
|
def test_max_atr_pct_fails(self):
|
||||||
|
# atr_14=8.0, prev_close=100.0 → atr_ratio=8% > max_atr_pct=5% → fail
|
||||||
|
enrich = _enrich("AAPL", prev_close=100.0, atr_14=8.0, avg_dollar_vol=50_000_000)
|
||||||
|
params = _default_params(max_atr_pct=0.05)
|
||||||
|
assert "AAPL" not in live_pre_screen(enrich, _DATE, params)
|
||||||
|
|
||||||
|
def test_both_bounds_respected(self):
|
||||||
|
# Only ticker in [2%, 5%] range should pass; below and above both fail
|
||||||
|
enrich = {
|
||||||
|
"LOW": {"2026-01-04": {"prev_close": 100.0, "atr_14": 1.0, "avg_dollar_vol_30d": 50_000_000}},
|
||||||
|
"OK": {"2026-01-04": {"prev_close": 100.0, "atr_14": 3.0, "avg_dollar_vol_30d": 50_000_000}},
|
||||||
|
"HI": {"2026-01-04": {"prev_close": 100.0, "atr_14": 7.0, "avg_dollar_vol_30d": 50_000_000}},
|
||||||
|
}
|
||||||
|
params = _default_params(min_atr_pct=0.02, max_atr_pct=0.05)
|
||||||
|
result = live_pre_screen(enrich, _DATE, params)
|
||||||
|
assert "OK" in result
|
||||||
|
assert "LOW" not in result
|
||||||
|
assert "HI" not in result
|
||||||
|
|
||||||
|
def test_none_bounds_no_filter(self):
|
||||||
|
# min_atr_pct=None, max_atr_pct=None → no ATR% filtering
|
||||||
|
enrich = _enrich("AAPL", prev_close=100.0, atr_14=0.1, avg_dollar_vol=50_000_000)
|
||||||
|
params = _default_params(min_atr_14=0.0, min_atr_pct=None, max_atr_pct=None)
|
||||||
|
assert "AAPL" in live_pre_screen(enrich, _DATE, params)
|
||||||
Loading…
Reference in New Issue