fix: harden post-stop reentry lockdown
This commit is contained in:
@@ -1356,7 +1356,7 @@ def _simulate_portfolio(
|
||||
max_positions: int = SIM_MAX_POSITIONS,
|
||||
risk_per_trade: float = SIM_RISK_PER_TRADE,
|
||||
atr_trail_multiplier: float = ATR_TRAIL_MULTIPLIER,
|
||||
reentry_cooldown_days: int = 0,
|
||||
reentry_cooldown_sessions: int = 0,
|
||||
initial_stop_refresh_fn: (
|
||||
Callable[[str, int, float, dict, Any], float | None] | None
|
||||
) = None,
|
||||
@@ -1380,7 +1380,7 @@ def _simulate_portfolio(
|
||||
runs the ATR trail *and* the S/R take-profit together — the trade ends at
|
||||
whichever comes first. Stops fill at the worse of stop or open (gaps
|
||||
modeled); positions still open at the end are closed at their last mark.
|
||||
``reentry_cooldown_days`` blocks a ticker for that many market sessions
|
||||
``reentry_cooldown_sessions`` blocks a ticker for that many market sessions
|
||||
after an initial-stop loss. Profitable trailing-stop exits do not trigger
|
||||
it. ``initial_stop_refresh_fn`` may supply a lower, point-in-time valid long
|
||||
stop when the active initial stop is touched; the replacement is still
|
||||
@@ -1541,7 +1541,7 @@ def _simulate_portfolio(
|
||||
def _marked_equity() -> float:
|
||||
return cash + sum(p["shares"] * p["last_close"] for p in positions.values())
|
||||
|
||||
cooldown_days = max(0, int(reentry_cooldown_days))
|
||||
cooldown_sessions = max(0, int(reentry_cooldown_sessions))
|
||||
for calendar_index, o in enumerate(calendar):
|
||||
# 1) exits on today's bars (stop intraday, target intraday, time at close)
|
||||
for sym in list(positions):
|
||||
@@ -1579,8 +1579,8 @@ def _simulate_portfolio(
|
||||
if not survived_refresh:
|
||||
fill = min(pos["stop"], bar.open)
|
||||
closed_pos = _close_trade(sym, fill, reason)
|
||||
if reason == "stop" and cooldown_days:
|
||||
cooldown_until_index[sym] = calendar_index + cooldown_days
|
||||
if reason == "stop" and cooldown_sessions:
|
||||
cooldown_until_index[sym] = calendar_index + cooldown_sessions
|
||||
if reason == "stop" and post_stop_reentry_fn is not None:
|
||||
post_stop_events += 1
|
||||
post_stop_states[sym] = {
|
||||
@@ -1833,8 +1833,8 @@ def _simulate_portfolio(
|
||||
result["equity_curve"] = curve_payload
|
||||
if benchmark_payload is not None:
|
||||
result["benchmark_curve"] = benchmark_payload
|
||||
if cooldown_days:
|
||||
result["reentry_cooldown_days"] = cooldown_days
|
||||
if cooldown_sessions:
|
||||
result["reentry_cooldown_sessions"] = cooldown_sessions
|
||||
result["skipped_cooldown"] = skipped_cooldown
|
||||
if initial_stop_refresh_fn is not None:
|
||||
result["stop_refresh_attempts"] = stop_refresh_attempts
|
||||
@@ -2403,7 +2403,7 @@ def _min_rr_sweep(
|
||||
max_positions=int(entry_cfg["max_positions"]),
|
||||
risk_per_trade=float(entry_cfg["risk_per_trade"]),
|
||||
atr_trail_multiplier=trail_multiplier,
|
||||
reentry_cooldown_days=reentry_lockdown_sessions,
|
||||
reentry_cooldown_sessions=reentry_lockdown_sessions,
|
||||
start_date=sweep_start,
|
||||
)
|
||||
if sim is None:
|
||||
@@ -2516,7 +2516,7 @@ def _holdout_evaluation(
|
||||
max_positions=int(entry_cfg["max_positions"]),
|
||||
risk_per_trade=float(entry_cfg["risk_per_trade"]),
|
||||
atr_trail_multiplier=trail_multiplier,
|
||||
reentry_cooldown_days=reentry_lockdown_sessions,
|
||||
reentry_cooldown_sessions=reentry_lockdown_sessions,
|
||||
start_date=start,
|
||||
end_date=end,
|
||||
include_curve=True,
|
||||
@@ -2592,7 +2592,7 @@ def _portfolio_monitor(
|
||||
max_positions=int(entry_cfg["max_positions"]),
|
||||
risk_per_trade=float(entry_cfg["risk_per_trade"]),
|
||||
atr_trail_multiplier=trail_multiplier,
|
||||
reentry_cooldown_days=reentry_lockdown_sessions,
|
||||
reentry_cooldown_sessions=reentry_lockdown_sessions,
|
||||
start_date=start,
|
||||
include_curve=True,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user