mirror of
https://github.com/PyP-Quant/quant-trading-strategy-templates.git
synced 2026-08-21 22:58:06 +00:00
route xauusd atr breakout to modal
This commit is contained in:
@@ -8,6 +8,6 @@ The default gate is intentionally responsive so PPE produces more events than a
|
|||||||
|
|
||||||
- `breakout_window`: `12`
|
- `breakout_window`: `12`
|
||||||
- `atr_mult`: `0.05`
|
- `atr_mult`: `0.05`
|
||||||
- no runtime package dependencies
|
- assigned runtime: Modal, because this starter uses `pandas`
|
||||||
|
|
||||||
If it overtrades, raise `atr_mult` first. If it is still too quiet, shorten `breakout_window`.
|
If it overtrades, raise `atr_mult` first. If it is still too quiet, shorten `breakout_window`.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"pair": "XAUUSD",
|
"pair": "XAUUSD",
|
||||||
"timeframe": "15m",
|
"timeframe": "15m",
|
||||||
"model_family": "custom Python",
|
"model_family": "custom Python",
|
||||||
"runtime_target": "edge",
|
"runtime_target": "modal",
|
||||||
"artifact_format": "python_bundle",
|
"artifact_format": "python_bundle",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"lookback": 64,
|
"lookback": 64,
|
||||||
@@ -10,8 +10,14 @@
|
|||||||
"breakout_window": 12,
|
"breakout_window": 12,
|
||||||
"atr_mult": 0.05
|
"atr_mult": 0.05
|
||||||
},
|
},
|
||||||
"training_requirements": [],
|
"training_requirements": [
|
||||||
"inference_requirements": [],
|
"numpy",
|
||||||
|
"pandas"
|
||||||
|
],
|
||||||
|
"inference_requirements": [
|
||||||
|
"numpy",
|
||||||
|
"pandas"
|
||||||
|
],
|
||||||
"symbol": "XAUUSD",
|
"symbol": "XAUUSD",
|
||||||
"description": "XAUUSD ATR breakout rule baseline",
|
"description": "XAUUSD ATR breakout rule baseline",
|
||||||
"disclaimer": "Educational template only. Not financial advice."
|
"disclaimer": "Educational template only. Not financial advice."
|
||||||
|
|||||||
@@ -1,34 +1,18 @@
|
|||||||
def _clean_candles(candles):
|
import numpy as np
|
||||||
cleaned = []
|
import pandas as pd
|
||||||
for candle in candles:
|
|
||||||
try:
|
|
||||||
cleaned.append({
|
|
||||||
"open": float(candle[0]),
|
|
||||||
"high": float(candle[1]),
|
|
||||||
"low": float(candle[2]),
|
|
||||||
"close": float(candle[3]),
|
|
||||||
"volume": float(candle[4]) if len(candle) > 4 else 1.0,
|
|
||||||
})
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
return cleaned
|
|
||||||
|
|
||||||
|
|
||||||
def _atr(candles, n):
|
def _df(candles):
|
||||||
if len(candles) < 2:
|
df = pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"])
|
||||||
return 0.0
|
for col in df.columns:
|
||||||
alpha = 2.0 / (n + 1.0)
|
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||||
value = None
|
return df.dropna().reset_index(drop=True)
|
||||||
prev_close = candles[0]["close"]
|
|
||||||
for candle in candles[1:]:
|
|
||||||
tr = max(
|
def _atr(df, n):
|
||||||
candle["high"] - candle["low"],
|
prev = df["close"].shift()
|
||||||
abs(candle["high"] - prev_close),
|
tr = pd.concat([(df["high"] - df["low"]), (df["high"] - prev).abs(), (df["low"] - prev).abs()], axis=1).max(axis=1)
|
||||||
abs(candle["low"] - prev_close),
|
return tr.ewm(span=n, adjust=False).mean()
|
||||||
)
|
|
||||||
value = tr if value is None else value + alpha * (tr - value)
|
|
||||||
prev_close = candle["close"]
|
|
||||||
return float(value or 0.0)
|
|
||||||
|
|
||||||
|
|
||||||
def train(data, config):
|
def train(data, config):
|
||||||
@@ -41,7 +25,7 @@ def train(data, config):
|
|||||||
"atr_mult": float(params.get("atr_mult", 0.05)),
|
"atr_mult": float(params.get("atr_mult", 0.05)),
|
||||||
},
|
},
|
||||||
"name": "xauusd_atr_breakout",
|
"name": "xauusd_atr_breakout",
|
||||||
}, {"training_bars": int(len(data)), "model": "edge_rule_baseline"}
|
}, {"training_bars": int(len(data)), "model": "modal_python_rule_baseline"}
|
||||||
|
|
||||||
|
|
||||||
def predict(model, market_data, config):
|
def predict(model, market_data, config):
|
||||||
@@ -50,44 +34,23 @@ def predict(model, market_data, config):
|
|||||||
atr_window = int(params.get("atr_window", 14))
|
atr_window = int(params.get("atr_window", 14))
|
||||||
breakout_window = int(params.get("breakout_window", 12))
|
breakout_window = int(params.get("breakout_window", 12))
|
||||||
atr_mult = float(params.get("atr_mult", 0.05))
|
atr_mult = float(params.get("atr_mult", 0.05))
|
||||||
candles = _clean_candles(market_data.get("candles", []))
|
candles = market_data.get("candles", [])
|
||||||
|
|
||||||
if len(candles) < lookback:
|
if len(candles) < lookback:
|
||||||
return {
|
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles"}}
|
||||||
"signal": "HOLD",
|
|
||||||
"confidence": 0.0,
|
|
||||||
"metadata": {"reason": "not_enough_candles", "got": len(candles), "need": lookback},
|
|
||||||
}
|
|
||||||
|
|
||||||
window = candles[-lookback:]
|
df = _df(candles[-lookback:])
|
||||||
previous = window[-breakout_window - 1:-1]
|
atr = float(_atr(df, atr_window).iloc[-1])
|
||||||
if len(previous) < breakout_window:
|
close = float(df["close"].iloc[-1])
|
||||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_breakout_window"}}
|
high = float(df["high"].iloc[-breakout_window:-1].max())
|
||||||
|
low = float(df["low"].iloc[-breakout_window:-1].min())
|
||||||
atr = _atr(window, atr_window)
|
|
||||||
close = window[-1]["close"]
|
|
||||||
high = max(candle["high"] for candle in previous)
|
|
||||||
low = min(candle["low"] for candle in previous)
|
|
||||||
upper = high + atr * atr_mult
|
upper = high + atr * atr_mult
|
||||||
lower = low - atr * atr_mult
|
lower = low - atr * atr_mult
|
||||||
|
|
||||||
if close > upper:
|
if close > upper:
|
||||||
edge = (close - upper) / max(atr, 1e-9)
|
edge = (close - upper) / max(atr, 1e-9)
|
||||||
return {
|
return {"signal": "UP", "confidence": round(min(0.55 + edge, 0.92), 4), "metadata": {"breakout": "high", "atr": atr, "upper": upper}}
|
||||||
"signal": "UP",
|
|
||||||
"confidence": round(min(0.55 + edge, 0.92), 4),
|
|
||||||
"metadata": {"breakout": "high", "atr": round(atr, 6), "upper": round(upper, 6), "close": round(close, 6)},
|
|
||||||
}
|
|
||||||
if close < lower:
|
if close < lower:
|
||||||
edge = (lower - close) / max(atr, 1e-9)
|
edge = (lower - close) / max(atr, 1e-9)
|
||||||
return {
|
return {"signal": "DOWN", "confidence": round(min(0.55 + edge, 0.92), 4), "metadata": {"breakout": "low", "atr": atr, "lower": lower}}
|
||||||
"signal": "DOWN",
|
|
||||||
"confidence": round(min(0.55 + edge, 0.92), 4),
|
|
||||||
"metadata": {"breakout": "low", "atr": round(atr, 6), "lower": round(lower, 6), "close": round(close, 6)},
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {"signal": "HOLD", "confidence": 0.2, "metadata": {"high": high, "low": low, "atr": atr, "upper": upper, "lower": lower}}
|
||||||
"signal": "HOLD",
|
|
||||||
"confidence": 0.2,
|
|
||||||
"metadata": {"high": round(high, 6), "low": round(low, 6), "atr": round(atr, 6), "upper": round(upper, 6), "lower": round(lower, 6)},
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user