Fix on Sell order lossing by blocking time

This commit is contained in:
Harwinder Sekhon
2026-07-16 12:17:56 -04:00
parent fe7d4c5d4a
commit 665ffdda48
6 changed files with 126 additions and 2 deletions
+54 -2
View File
@@ -198,6 +198,8 @@ def _():
assert xc.tp_atr_multiplier == 1.0
assert xc.sl_atr_multiplier == 2.0
assert isinstance(xc.blocked_hours_utc, list)
assert xc.sell_blocked_hours_utc == [], \
"sell hour gate must be off by default (per-TF factories opt in)"
@test("FilterConfig defaults")
@@ -1846,10 +1848,11 @@ def _():
)
if tf == Timeframe.M15:
# Documented per-TF overrides (see xauusd.py docstring):
# 1R minimum targets + mixed-"trending"-regime block,
# both validated for M15 only.
# 1R minimum targets + mixed-"trending"-regime block +
# SELL hour block (15/17 UTC), all validated for M15 only.
inline_cfg.signals.tp_floor_rr = 1.0
inline_cfg.xau_strategy.blocked_regimes = ["trending"]
inline_cfg.xau_strategy.sell_blocked_hours_utc = [15, 17]
if tf == Timeframe.M5:
# Documented per-TF override (see xauusd.py docstring):
# hour 9 UTC blocked on M5 only.
@@ -2063,6 +2066,55 @@ def _():
assert make_config(Timeframe.M5).xau_strategy.blocked_regimes == []
@test("XAU SELL hour gate: blocks 15/17 UTC bar-close entries, spares others")
def _():
from pair_signals.xauusd import make_config
def xau_15min_df(end):
np.random.seed(9)
dates = pd.date_range(end=pd.Timestamp(end, tz="UTC"),
periods=300, freq="15min")
close = 3300.0 + np.cumsum(np.random.randn(300) * 2.0 - 1.0)
return pd.DataFrame({
"Open": close + np.random.randn(300) * 0.5,
"High": close + np.abs(np.random.randn(300)),
"Low": close - np.abs(np.random.randn(300)),
"Close": close,
"Volume": np.random.randint(100, 10000, 300).astype(float),
}, index=pd.DatetimeIndex(dates, name="Datetime"))
def run(end, hours):
cfg = make_config(Timeframe.M15)
cfg.backtest.session_filter = False
# Neutralize the other SELL gates so 6g3 is the one under test.
cfg.xau_strategy.sell_min_quality = ["A+", "A", "B", "C"]
cfg.xau_strategy.sell_vol_regimes = ["low", "normal", "high", "extreme"]
cfg.xau_strategy.blocked_regimes = []
cfg.xau_strategy.sell_blocked_hours_utc = hours
df = xau_15min_df(end)
dfs = {Timeframe.M15: df, Timeframe.M5: df, Timeframe.H1: df}
return SignalAggregator(cfg).analyze_from_dataframes(
symbol=XAUUSD, dfs=dfs, use_news=False)
# Same synthetic downtrend; only the index end (and thus the bar-close
# hour) differs. Label 14:45 closes 15:00 (blocked); 19:45 closes
# 20:00 (allowed). The gate reads bar-CLOSE time, not the label hour.
control = run("2026-07-13 14:45", [])
assert control.signal_quality in ("A+", "A", "B", "C") and control.final_score < 0, \
f"probe data must yield an actionable sell, got {control.signal_quality}"
blocked = run("2026-07-13 14:45", [15, 17])
assert blocked.signal_quality == "D" and blocked.trade_setup is None, \
f"15:00 UTC close must be gated to D, got {blocked.signal_quality}"
assert any("blocked for shorts" in w for w in blocked.warnings), \
f"missing SELL hour warning: {blocked.warnings}"
open_hour = run("2026-07-13 19:45", [15, 17])
assert open_hour.signal_quality == control.signal_quality, \
f"20:00 UTC close must pass the gate, got {open_hour.signal_quality}"
# Factory scoping: M15 opts in, M5 does not (its book must not move).
assert make_config(Timeframe.M15).xau_strategy.sell_blocked_hours_utc == [15, 17]
assert make_config(Timeframe.M5).xau_strategy.sell_blocked_hours_utc == []
@test("NewsEngine._get_affected_pairs includes EUR/USD for EUR and USD events")
def _():
from news_engine import NewsEngine