population: extract strategy/population.py; live gate delegates to it
The tradeable population is now defined ONCE. trading_execution._population_reject becomes a thin wrapper building a PopulationConfig from its settings and calling strategy.population.population_reject — logic moved verbatim (dead-band 0.35/1.50 and squeeze rvol 5.0 are module CONSTANTS, gap floor / price band / volume floor from config). Behaviour byte-identical: the pre-extraction characterization suite stays green through the new public seam. from_env mirrors ExecutionSettings.from_env (same 4 keys + the [0,1] _qfloat clamp) so a live-shaped shell reconstructs the identical gate. gap_pct FRACTION contract and the as_of open-knowable contract are documented for offline consumers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
256481910f
commit
66e81a48f3
3 files changed
+190
-31
No files matched your search
@@ -0,0 +1,62 @@
|
||||
"""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():
|
||||
# No production env => min_entry_gap_pct clamps to 0.0 (gap gate off), the
|
||||
# exact hazard the plan warns offline consumers about.
|
||||
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
|
||||
Reference in new issue
Block a user