feat: add daily reentry policy matrix

This commit is contained in:
2026-07-17 16:11:18 +02:00
parent 13f57b2525
commit 0cd9ee7689
4 changed files with 764 additions and 5 deletions
+53
View File
@@ -543,6 +543,7 @@ class TestSimulatePortfolio:
sim = bt._simulate_portfolio([cand], prices, None, "hold", 3)
assert sim is not None
assert sim["trades"] == 1
assert sim["cost_per_side_pct"] == pytest.approx(0.1)
# 20 shares (1% risk / $5 stop distance), exit at the day-3 close 106:
# pnl = 2120 2000 2.00 entry cost 2.12 exit cost = 115.88
assert sim["final_equity"] == pytest.approx(10_115.88, abs=0.01)
@@ -556,6 +557,29 @@ class TestSimulatePortfolio:
{"year": 2025, "return_pct": pytest.approx(1.2, abs=0.05)}
]
def test_cost_parameter_changes_cash_and_position_path(self):
closes = [100.0, 102.0, 104.0, 106.0]
prices = {"AAA": _sim_prices(self.ORD, closes)}
cand = _sim_cand("AAA", self.ORD, entry=100.0, stop=95.0, target=130.0)
free = bt._simulate_portfolio(
[cand], prices, None, "hold", 3, cost_per_side=0.0
)
stressed = bt._simulate_portfolio(
[cand], prices, None, "hold", 3, cost_per_side=0.002
)
assert free is not None and stressed is not None
assert free["final_equity"] == pytest.approx(10_120.0, abs=0.01)
assert stressed["cost_per_side_pct"] == pytest.approx(0.2)
assert stressed["final_equity"] == pytest.approx(10_111.76, abs=0.01)
def test_cost_parameter_rejects_invalid_rate(self):
with pytest.raises(ValueError, match="cost_per_side"):
bt._simulate_portfolio(
[], {}, None, "hold", 3, cost_per_side=-0.001
)
def test_target_policy_exits_at_target(self):
closes = [100.0, 102.0, 104.0, 106.0, 108.0, 110.0]
prices = {"AAA": _sim_prices(self.ORD, closes)}
@@ -626,6 +650,35 @@ class TestSimulatePortfolio:
self.ORD + 6
).isoformat()
def test_post_stop_reentry_cannot_cross_holdout_end(self):
prices = {"AAA": _sim_prices(self.ORD, [100.0, 94.0, 96.0, 98.0])}
candidate = _sim_cand(
"AAA", self.ORD, entry=100.0, stop=95.0, target=120.0
)
callback_dates: list[int] = []
def reenter_after_split(symbol, asof_ord, _state, _bar):
callback_dates.append(asof_ord)
if asof_ord < self.ORD + 2:
return None
return _sim_cand(
symbol, asof_ord, entry=96.0, stop=90.0, target=115.0
)
sim = bt._simulate_portfolio(
[candidate],
prices,
None,
"hold",
3,
end_date=date.fromordinal(self.ORD + 2),
post_stop_reentry_fn=reenter_after_split,
)
assert sim is not None
assert sim["trades"] == 1
assert callback_dates == [self.ORD + 1]
def test_production_monitor_applies_live_reentry_lockdown(self, monkeypatch):
def fake_simulator(*_args, **kwargs):
return {