Files
momentum-system/tests/unit/strategy/test_population_module.py
T
momentum devandClaude Fable 5 5518625bbb population test: clear env for the bare-shell from_env case
from_env merges os.environ, and importing the .env-loading consumers populates
TRADING_MIN_ENTRY_GAP_PCT — so the "bare shell disables the gap gate" assertion
must delenv the key first. Test-only fix; the module behaviour is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 22:48:17 -04:00

65 lines
2.8 KiB
Python

"""Module-level tests for strategy/population.py — the shared population gate.
Complements tests/unit/test_population_gate.py (which pins the LIVE seam through
the service). Here we test the module directly: from_env key parity with
production, the as_of contract docstring, and fail-open semantics.
"""
import inspect
from strategy.population import PopulationConfig, population_reject
def test_from_env_defaults_match_production_keys():
cfg = PopulationConfig.from_env(env={
"TRADING_PRICE_MIN": "2.0",
"TRADING_PRICE_MAX": "20.0",
"TRADING_MIN_ENTRY_GAP_PCT": "0.10",
"TRADING_MIN_DAY_VOLUME": "300000",
})
assert cfg.price_min == 2.0
assert cfg.price_max == 20.0
assert cfg.min_entry_gap_pct == 0.10
assert cfg.min_day_volume == 300_000
# constants are NOT env-driven
assert cfg.gap_dead_band_lo == 0.35
assert cfg.gap_dead_band_hi == 1.50
assert cfg.squeeze_rvol_min == 5.0
def test_from_env_bare_shell_disables_gap_gate(monkeypatch):
# No production env => min_entry_gap_pct defaults to 0.0 (gap gate off), the
# exact hazard the plan warns offline consumers about. (from_env merges
# os.environ, which .env-loading imports populate — so we clear the key.)
monkeypatch.delenv("TRADING_MIN_ENTRY_GAP_PCT", raising=False)
cfg = PopulationConfig.from_env(env={})
assert cfg.min_entry_gap_pct == 0.0
assert population_reject(cfg, gap_pct=0.05, price=10.0, volume=None, rvol=None) is None
def test_from_env_clamps_gap_floor_to_unit_interval():
# mirrors trading_execution._qfloat: percent-typed 40 must NOT survive as 40.
assert PopulationConfig.from_env(env={"TRADING_MIN_ENTRY_GAP_PCT": "40"}).min_entry_gap_pct == 1.0
assert PopulationConfig.from_env(env={"TRADING_MIN_ENTRY_GAP_PCT": "junk"}).min_entry_gap_pct == 0.0
def test_as_of_contract_documented():
# day-end rejection is by-convention (review-enforced), so the contract MUST
# be spelled out in the signature + docstring.
assert "as_of" in inspect.signature(population_reject).parameters
doc = population_reject.__doc__ or ""
assert "FRACTION" in doc and "forbidden" in doc.lower()
def test_fail_open_on_all_missing():
cfg = PopulationConfig(min_entry_gap_pct=0.10, min_day_volume=300_000)
assert population_reject(cfg, gap_pct=None, price=None, volume=None, rvol=None) is None
def test_offline_consumer_must_pass_prod_thresholds():
# bare config admits everything; explicit prod thresholds gate correctly.
bare = PopulationConfig()
assert population_reject(bare, gap_pct=0.02, price=3.0, volume=1, rvol=None) is None
prod = PopulationConfig(min_entry_gap_pct=0.10, min_day_volume=300_000)
assert population_reject(prod, gap_pct=0.02, price=3.0, volume=1, rvol=None) is not None