test(protocol): integration test gating temporale + sma
This commit is contained in:
@@ -208,3 +208,48 @@ def test_compile_minute_of_hour_zero_on_1h_timeframe(ohlcv: pd.DataFrame) -> Non
|
|||||||
fn = compile_strategy(ast)
|
fn = compile_strategy(ast)
|
||||||
signal = fn(ohlcv)
|
signal = fn(ohlcv)
|
||||||
assert (signal == Side.LONG).all()
|
assert (signal == Side.LONG).all()
|
||||||
|
|
||||||
|
|
||||||
|
def test_rule_with_temporal_gating_compiles_and_executes(ohlcv: pd.DataFrame) -> None:
|
||||||
|
# Rule: entry-long if hour > 14 AND close > sma(20).
|
||||||
|
# close in fixture is strictly increasing, so close > sma(20) holds after warmup.
|
||||||
|
# entry-long should appear only on rows with hour > 14.
|
||||||
|
src = json.dumps(
|
||||||
|
{
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"condition": {
|
||||||
|
"op": "and",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"op": "gt",
|
||||||
|
"args": [
|
||||||
|
{"kind": "feature", "name": "hour"},
|
||||||
|
{"kind": "literal", "value": 14.0},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "gt",
|
||||||
|
"args": [
|
||||||
|
{"kind": "feature", "name": "close"},
|
||||||
|
{"kind": "indicator", "name": "sma", "params": [20]},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"action": "entry-long",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
ast = parse_strategy(src)
|
||||||
|
fn = compile_strategy(ast)
|
||||||
|
signal = fn(ohlcv)
|
||||||
|
|
||||||
|
# Bars with hour <= 14: never LONG (temporal gate blocks).
|
||||||
|
morning = signal[signal.index.hour <= 14]
|
||||||
|
assert (morning == Side.FLAT).all()
|
||||||
|
|
||||||
|
# Bars with hour > 14 AND past SMA warmup (>=20 bars): LONG.
|
||||||
|
afternoon_warm = signal[(signal.index.hour > 14) & (np.arange(len(signal)) >= 20)]
|
||||||
|
assert (afternoon_warm == Side.LONG).all()
|
||||||
|
|||||||
Reference in New Issue
Block a user