Fix on Sell order lossing by blocking time
This commit is contained in:
+54
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user