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
I Luk Kim 4 months ago
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…
Cancel
Save