mirror of
https://github.com/PyP-Quant/quant-trading-strategy-templates.git
synced 2026-07-27 18:47:47 +00:00
feat: expand quant templates to fifty projects
This commit is contained in:
@@ -26,20 +26,60 @@ Every project includes:
|
||||
|
||||
## Templates
|
||||
|
||||
This repository currently contains 50 PyP Quant strategy templates.
|
||||
|
||||
| Template | Pair | Timeframe | Model family | Use case |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `eurusd-logistic-15m` | EURUSD | 15m | sklearn | Baseline directional classifier |
|
||||
| `eurusd-xgboost-1h` | EURUSD | 1h | XGBoost | Feature-rich trend classifier |
|
||||
| `gbpusd-breakout-rf` | GBPUSD | 30m | sklearn RandomForest | Range breakout classifier |
|
||||
| `usdjpy-mean-reversion` | USDJPY | 1h | sklearn | Mean reversion baseline |
|
||||
| `xauusd-regime-xgboost-v11` | XAUUSD | 1h | XGBoost | Less restrictive Au-79-style gold model |
|
||||
| `xauusd-atr-breakout` | XAUUSD | 15m | custom Python | ATR breakout rules |
|
||||
| `btcusdt-lightgbm-1h` | BTCUSDT | 1h | LightGBM | Crypto trend classifier |
|
||||
| `ethusdt-volatility-classifier` | ETHUSDT | 30m | sklearn | Volatility regime classifier |
|
||||
| `solusdt-scalp-baseline` | SOLUSDT | 1m | custom Python | High-volatility scalp baseline |
|
||||
| `onnx-export-sklearn-starter` | EURUSD | 1h | sklearn to ONNX | ONNX export starter |
|
||||
| `statsmodels-arima-direction` | EURUSD | 1h | statsmodels | Statistical direction baseline |
|
||||
| `lightgbm-fx-multifeature` | EURUSD | 30m | LightGBM | Multi-feature FX classifier |
|
||||
| `adausdt-volatility-rf-15m` | ADAUSDT | 15m | sklearn RandomForest | sklearn RandomForest PyP Quant template for ADAUSDT 15m. |
|
||||
| `audjpy-randomforest-1h` | AUDJPY | 1h | sklearn RandomForest | sklearn RandomForest PyP Quant template for AUDJPY 1h. |
|
||||
| `audusd-logistic-30m` | AUDUSD | 30m | sklearn LogisticRegression | sklearn LogisticRegression PyP Quant template for AUDUSD 30m. |
|
||||
| `avaxusdt-volatility-rf-30m` | AVAXUSDT | 30m | sklearn RandomForest | sklearn RandomForest PyP Quant template for AVAXUSDT 30m. |
|
||||
| `bnbusdt-trend-rf-30m` | BNBUSDT | 30m | sklearn RandomForest | sklearn RandomForest PyP Quant template for BNBUSDT 30m. |
|
||||
| `brentoil-trend-rf-1h` | BRENTUSD | 1h | sklearn RandomForest | sklearn RandomForest PyP Quant template for BRENTUSD 1h. |
|
||||
| `btcusdt-breakout-rf-15m` | BTCUSDT | 15m | sklearn RandomForest | sklearn RandomForest PyP Quant template for BTCUSDT 15m. |
|
||||
| `btcusdt-lightgbm-1h` | BTCUSDT | 1h | LightGBM | BTCUSDT LightGBM crypto trend classifier |
|
||||
| `btcusdt-mean-reversion-5m` | BTCUSDT | 5m | custom Python | custom Python PyP Quant template for BTCUSDT 5m. |
|
||||
| `cadjpy-breakout-1h` | CADJPY | 1h | sklearn RandomForest | sklearn RandomForest PyP Quant template for CADJPY 1h. |
|
||||
| `chfjpy-mean-reversion-1h` | CHFJPY | 1h | custom Python | custom Python PyP Quant template for CHFJPY 1h. |
|
||||
| `copperusd-mean-reversion-1h` | COPPERUSD | 1h | custom Python | custom Python PyP Quant template for COPPERUSD 1h. |
|
||||
| `dogeusdt-scalp-logistic-1m` | DOGEUSDT | 1m | sklearn LogisticRegression | sklearn LogisticRegression PyP Quant template for DOGEUSDT 1m. |
|
||||
| `dotusdt-mean-reversion-30m` | DOTUSDT | 30m | custom Python | custom Python PyP Quant template for DOTUSDT 30m. |
|
||||
| `ethusdt-volatility-classifier` | ETHUSDT | 30m | sklearn RandomForest | ETHUSDT volatility-aware directional classifier |
|
||||
| `eurgbp-range-classifier-30m` | EURGBP | 30m | sklearn LogisticRegression | sklearn LogisticRegression PyP Quant template for EURGBP 30m. |
|
||||
| `eurjpy-trend-rf-1h` | EURJPY | 1h | sklearn RandomForest | sklearn RandomForest PyP Quant template for EURJPY 1h. |
|
||||
| `eurnzd-volatility-rf-4h` | EURNZD | 4h | sklearn RandomForest | sklearn RandomForest PyP Quant template for EURNZD 4h. |
|
||||
| `eurusd-logistic-15m` | EURUSD | 15m | sklearn LogisticRegression | EURUSD logistic regression baseline classifier |
|
||||
| `eurusd-xgboost-1h` | EURUSD | 1h | XGBoost | EURUSD feature-rich XGBoost trend classifier |
|
||||
| `gbpjpy-breakout-rf-1h` | GBPJPY | 1h | sklearn RandomForest | sklearn RandomForest PyP Quant template for GBPJPY 1h. |
|
||||
| `gbpusd-breakout-rf` | GBPUSD | 30m | sklearn RandomForest | GBPUSD range breakout random forest classifier |
|
||||
| `gbpusd-logistic-15m` | GBPUSD | 15m | sklearn LogisticRegression | sklearn LogisticRegression PyP Quant template for GBPUSD 15m. |
|
||||
| `ger40-breakout-rf-30m` | GER40 | 30m | sklearn RandomForest | sklearn RandomForest PyP Quant template for GER40 30m. |
|
||||
| `lightgbm-fx-multifeature` | EURUSD | 30m | LightGBM | Multi-feature FX LightGBM classifier |
|
||||
| `linkusdt-trend-logistic-30m` | LINKUSDT | 30m | sklearn LogisticRegression | sklearn LogisticRegression PyP Quant template for LINKUSDT 30m. |
|
||||
| `ltcusdt-trend-rf-1h` | LTCUSDT | 1h | sklearn RandomForest | sklearn RandomForest PyP Quant template for LTCUSDT 1h. |
|
||||
| `maticusdt-scalp-baseline-5m` | MATICUSDT | 5m | custom Python | custom Python PyP Quant template for MATICUSDT 5m. |
|
||||
| `nas100-breakout-rf-15m` | NAS100 | 15m | sklearn RandomForest | sklearn RandomForest PyP Quant template for NAS100 15m. |
|
||||
| `naturalgas-volatility-rf-4h` | NATGAS | 4h | sklearn RandomForest | sklearn RandomForest PyP Quant template for NATGAS 4h. |
|
||||
| `nzdusd-mean-reversion-1h` | NZDUSD | 1h | custom Python | custom Python PyP Quant template for NZDUSD 1h. |
|
||||
| `oilwtico-atr-breakout-1h` | WTICOUSD | 1h | custom Python | custom Python PyP Quant template for WTICOUSD 1h. |
|
||||
| `onnx-export-sklearn-starter` | EURUSD | 1h | sklearn to ONNX | Sklearn classifier starter for ONNX export |
|
||||
| `solusdt-scalp-baseline` | SOLUSDT | 1m | custom Python | SOLUSDT high-volatility scalp baseline |
|
||||
| `spx500-trend-logistic-1h` | SPX500 | 1h | sklearn LogisticRegression | sklearn LogisticRegression PyP Quant template for SPX500 1h. |
|
||||
| `statsmodels-arima-direction` | EURUSD | 1h | statsmodels | ARIMA-style statistical direction baseline |
|
||||
| `uk100-range-logistic-1h` | UK100 | 1h | sklearn LogisticRegression | sklearn LogisticRegression PyP Quant template for UK100 1h. |
|
||||
| `us30-mean-reversion-30m` | US30 | 30m | custom Python | custom Python PyP Quant template for US30 30m. |
|
||||
| `usdcad-trend-rf-1h` | USDCAD | 1h | sklearn RandomForest | sklearn RandomForest PyP Quant template for USDCAD 1h. |
|
||||
| `usdchf-range-logistic-1h` | USDCHF | 1h | sklearn LogisticRegression | sklearn LogisticRegression PyP Quant template for USDCHF 1h. |
|
||||
| `usdjpy-mean-reversion` | USDJPY | 1h | custom Python | USDJPY mean-reversion baseline |
|
||||
| `usdmxn-volatility-rf-4h` | USDMXN | 4h | sklearn RandomForest | sklearn RandomForest PyP Quant template for USDMXN 4h. |
|
||||
| `xagusd-atr-breakout-1h` | XAGUSD | 1h | custom Python | custom Python PyP Quant template for XAGUSD 1h. |
|
||||
| `xagusd-mean-reversion-30m` | XAGUSD | 30m | custom Python | custom Python PyP Quant template for XAGUSD 30m. |
|
||||
| `xauusd-atr-breakout` | XAUUSD | 15m | custom Python | XAUUSD ATR breakout rule baseline |
|
||||
| `xauusd-london-breakout-15m` | XAUUSD | 15m | custom Python | custom Python PyP Quant template for XAUUSD 15m. |
|
||||
| `xauusd-regime-xgboost-v11` | XAUUSD | 1h | XGBoost | Less restrictive Au-79-style XAUUSD regime classifier |
|
||||
| `xauusd-scalp-logistic-5m` | XAUUSD | 5m | sklearn LogisticRegression | sklearn LogisticRegression PyP Quant template for XAUUSD 5m. |
|
||||
| `xptusd-trend-rf-1h` | XPTUSD | 1h | sklearn RandomForest | sklearn RandomForest PyP Quant template for XPTUSD 1h. |
|
||||
| `xrpusdt-breakout-rf-15m` | XRPUSDT | 15m | sklearn RandomForest | sklearn RandomForest PyP Quant template for XRPUSDT 15m. |
|
||||
|
||||
## Use With PyP
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# adausdt-volatility-rf-15m
|
||||
|
||||
ADA volatility classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `ADAUSDT` on the `15m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `ADAUSDT`
|
||||
- Timeframe: `15m`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "adausdt-volatility-rf-15m",
|
||||
"description": "sklearn RandomForest PyP Quant template for ADAUSDT 15m.",
|
||||
"symbol": "ADAUSDT",
|
||||
"timeframe": "15m",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 100,
|
||||
"horizon": 4,
|
||||
"threshold": 0.004,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "ADAUSDT"
|
||||
MODEL_NAME = "adausdt-volatility-rf-15m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.004))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 100)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# audjpy-randomforest-1h
|
||||
|
||||
AUDJPY carry-risk classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `AUDJPY` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `AUDJPY`
|
||||
- Timeframe: `1h`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "audjpy-randomforest-1h",
|
||||
"description": "sklearn RandomForest PyP Quant template for AUDJPY 1h.",
|
||||
"symbol": "AUDJPY",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0018,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "AUDJPY"
|
||||
MODEL_NAME = "audjpy-randomforest-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.0018))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 140)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# audusd-logistic-30m
|
||||
|
||||
AUDUSD momentum classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `AUDUSD` on the `30m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `AUDUSD`
|
||||
- Timeframe: `30m`
|
||||
- Family: `sklearn LogisticRegression`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "audusd-logistic-30m",
|
||||
"description": "sklearn LogisticRegression PyP Quant template for AUDUSD 30m.",
|
||||
"symbol": "AUDUSD",
|
||||
"timeframe": "30m",
|
||||
"model_family": "sklearn LogisticRegression",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.0012,
|
||||
"min_confidence": 0.48,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.linear_model import LogisticRegression
|
||||
from sklearn.pipeline import Pipeline
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
|
||||
|
||||
SYMBOL = "AUDUSD"
|
||||
MODEL_NAME = "audusd-logistic-30m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
rng = (df["high"] - df["low"]).replace(0, np.nan)
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 3, 6, 12, 24]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["range_pct"] = rng / c
|
||||
f["body_pct"] = (c - df["open"]) / (rng + 1e-9)
|
||||
f["close_pos"] = (c - df["low"]) / (rng + 1e-9)
|
||||
f["ema_8_21"] = (c.ewm(span=8, adjust=False).mean() - c.ewm(span=21, adjust=False).mean()) / c
|
||||
f["ema_21_55"] = (c.ewm(span=21, adjust=False).mean() - c.ewm(span=55, adjust=False).mean()) / c
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["volume_ratio"] = df["volume"] / (df["volume"].rolling(24).mean() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.0012))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", LogisticRegression(max_iter=1000, class_weight="balanced")),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 120)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.48)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# avaxusdt-volatility-rf-30m
|
||||
|
||||
AVAX volatility classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `AVAXUSDT` on the `30m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `AVAXUSDT`
|
||||
- Timeframe: `30m`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "avaxusdt-volatility-rf-30m",
|
||||
"description": "sklearn RandomForest PyP Quant template for AVAXUSDT 30m.",
|
||||
"symbol": "AVAXUSDT",
|
||||
"timeframe": "30m",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.004,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "AVAXUSDT"
|
||||
MODEL_NAME = "avaxusdt-volatility-rf-30m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.004))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 120)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# bnbusdt-trend-rf-30m
|
||||
|
||||
BNB trend classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `BNBUSDT` on the `30m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `BNBUSDT`
|
||||
- Timeframe: `30m`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "bnbusdt-trend-rf-30m",
|
||||
"description": "sklearn RandomForest PyP Quant template for BNBUSDT 30m.",
|
||||
"symbol": "BNBUSDT",
|
||||
"timeframe": "30m",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.004,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "BNBUSDT"
|
||||
MODEL_NAME = "bnbusdt-trend-rf-30m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.004))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 120)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# brentoil-trend-rf-1h
|
||||
|
||||
Brent crude trend classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `BRENTUSD` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `BRENTUSD`
|
||||
- Timeframe: `1h`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "brentoil-trend-rf-1h",
|
||||
"description": "sklearn RandomForest PyP Quant template for BRENTUSD 1h.",
|
||||
"symbol": "BRENTUSD",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.003,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "BRENTUSD"
|
||||
MODEL_NAME = "brentoil-trend-rf-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.003))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 140)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# btcusdt-breakout-rf-15m
|
||||
|
||||
BTC breakout classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `BTCUSDT` on the `15m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `BTCUSDT`
|
||||
- Timeframe: `15m`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "btcusdt-breakout-rf-15m",
|
||||
"description": "sklearn RandomForest PyP Quant template for BTCUSDT 15m.",
|
||||
"symbol": "BTCUSDT",
|
||||
"timeframe": "15m",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 100,
|
||||
"horizon": 4,
|
||||
"threshold": 0.004,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
|
||||
|
||||
SYMBOL = "BTCUSDT"
|
||||
MODEL_NAME = "btcusdt-breakout-rf-15m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
h = df["high"]
|
||||
l = df["low"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
high_break = h.rolling(36).max().shift()
|
||||
low_break = l.rolling(36).min().shift()
|
||||
f["breakout_up"] = (c - high_break) / c
|
||||
f["breakout_down"] = (c - low_break) / c
|
||||
f["ret4"] = c.pct_change(4)
|
||||
f["ret12"] = c.pct_change(12)
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["range_pct"] = (h - l) / c
|
||||
f["ema_slope"] = c.ewm(span=12, adjust=False).mean().pct_change(6)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.004))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
clf = RandomForestClassifier(n_estimators=300, max_depth=7, min_samples_leaf=6, class_weight="balanced_subsample", random_state=42, n_jobs=-1)
|
||||
clf.fit(x.values.astype(np.float32), y.values)
|
||||
pred = clf.predict(x.values.astype(np.float32))
|
||||
return {"model": clf, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 100)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -1 +1,30 @@
|
||||
{"pair":"BTCUSDT","timeframe":"1h","model_family":"lightgbm","runtime_target":"modal","artifact_format":"joblib_bundle","parameters":{"lookback":120,"horizon":3,"threshold":0.006,"min_confidence":0.46},"training_requirements":["numpy","pandas","scikit-learn","lightgbm","joblib"],"inference_requirements":["numpy","pandas","scikit-learn","lightgbm","joblib"]}
|
||||
{
|
||||
"pair": "BTCUSDT",
|
||||
"timeframe": "1h",
|
||||
"model_family": "LightGBM",
|
||||
"runtime_target": "modal",
|
||||
"artifact_format": "joblib_bundle",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 3,
|
||||
"threshold": 0.006,
|
||||
"min_confidence": 0.46
|
||||
},
|
||||
"training_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"lightgbm",
|
||||
"joblib"
|
||||
],
|
||||
"inference_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"lightgbm",
|
||||
"joblib"
|
||||
],
|
||||
"symbol": "BTCUSDT",
|
||||
"description": "BTCUSDT LightGBM crypto trend classifier",
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# btcusdt-mean-reversion-5m
|
||||
|
||||
BTC mean-reversion baseline for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `BTCUSDT` on the `5m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `BTCUSDT`
|
||||
- Timeframe: `5m`
|
||||
- Family: `custom Python`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "btcusdt-mean-reversion-5m",
|
||||
"description": "custom Python PyP Quant template for BTCUSDT 5m.",
|
||||
"symbol": "BTCUSDT",
|
||||
"timeframe": "5m",
|
||||
"model_family": "custom Python",
|
||||
"artifact_target": "python",
|
||||
"parameters": {
|
||||
"lookback": 100,
|
||||
"horizon": 4,
|
||||
"threshold": 0.004,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
SYMBOL = "BTCUSDT"
|
||||
MODEL_NAME = "btcusdt-mean-reversion-5m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _zscore(close, window):
|
||||
mean = close.rolling(window).mean()
|
||||
std = close.rolling(window).std()
|
||||
return (close - mean) / (std + 1e-9)
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
return {"window": int(p.get("window", 48)), "entry_z": float(p.get("entry_z", 1.4)), "exit_z": float(p.get("exit_z", 0.3)), "symbol": SYMBOL}, {"model_family": "rule_based_mean_reversion", "training_bars": int(len(data))}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
lookback = int(p.get("lookback", 100))
|
||||
if len(candles) < lookback:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
window = int(model.get("window", p.get("window", 48)))
|
||||
z = float(_zscore(df["close"], window).iloc[-1])
|
||||
entry_z = float(model.get("entry_z", p.get("entry_z", 1.4)))
|
||||
signal = "HOLD"
|
||||
if z <= -entry_z:
|
||||
signal = "UP"
|
||||
elif z >= entry_z:
|
||||
signal = "DOWN"
|
||||
confidence = min(abs(z) / max(entry_z, 1e-9), 1.0)
|
||||
return {"signal": signal, "confidence": round(float(confidence), 4), "metadata": {"zscore": round(z, 4), "window": window, "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# cadjpy-breakout-1h
|
||||
|
||||
CADJPY breakout classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `CADJPY` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `CADJPY`
|
||||
- Timeframe: `1h`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "cadjpy-breakout-1h",
|
||||
"description": "sklearn RandomForest PyP Quant template for CADJPY 1h.",
|
||||
"symbol": "CADJPY",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0018,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
|
||||
|
||||
SYMBOL = "CADJPY"
|
||||
MODEL_NAME = "cadjpy-breakout-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
h = df["high"]
|
||||
l = df["low"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
high_break = h.rolling(36).max().shift()
|
||||
low_break = l.rolling(36).min().shift()
|
||||
f["breakout_up"] = (c - high_break) / c
|
||||
f["breakout_down"] = (c - low_break) / c
|
||||
f["ret4"] = c.pct_change(4)
|
||||
f["ret12"] = c.pct_change(12)
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["range_pct"] = (h - l) / c
|
||||
f["ema_slope"] = c.ewm(span=12, adjust=False).mean().pct_change(6)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.0018))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
clf = RandomForestClassifier(n_estimators=300, max_depth=7, min_samples_leaf=6, class_weight="balanced_subsample", random_state=42, n_jobs=-1)
|
||||
clf.fit(x.values.astype(np.float32), y.values)
|
||||
pred = clf.predict(x.values.astype(np.float32))
|
||||
return {"model": clf, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 140)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# chfjpy-mean-reversion-1h
|
||||
|
||||
CHFJPY mean-reversion baseline for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `CHFJPY` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `CHFJPY`
|
||||
- Timeframe: `1h`
|
||||
- Family: `custom Python`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "chfjpy-mean-reversion-1h",
|
||||
"description": "custom Python PyP Quant template for CHFJPY 1h.",
|
||||
"symbol": "CHFJPY",
|
||||
"timeframe": "1h",
|
||||
"model_family": "custom Python",
|
||||
"artifact_target": "python",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0018,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
SYMBOL = "CHFJPY"
|
||||
MODEL_NAME = "chfjpy-mean-reversion-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _zscore(close, window):
|
||||
mean = close.rolling(window).mean()
|
||||
std = close.rolling(window).std()
|
||||
return (close - mean) / (std + 1e-9)
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
return {"window": int(p.get("window", 48)), "entry_z": float(p.get("entry_z", 1.4)), "exit_z": float(p.get("exit_z", 0.3)), "symbol": SYMBOL}, {"model_family": "rule_based_mean_reversion", "training_bars": int(len(data))}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
lookback = int(p.get("lookback", 140))
|
||||
if len(candles) < lookback:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
window = int(model.get("window", p.get("window", 48)))
|
||||
z = float(_zscore(df["close"], window).iloc[-1])
|
||||
entry_z = float(model.get("entry_z", p.get("entry_z", 1.4)))
|
||||
signal = "HOLD"
|
||||
if z <= -entry_z:
|
||||
signal = "UP"
|
||||
elif z >= entry_z:
|
||||
signal = "DOWN"
|
||||
confidence = min(abs(z) / max(entry_z, 1e-9), 1.0)
|
||||
return {"signal": signal, "confidence": round(float(confidence), 4), "metadata": {"zscore": round(z, 4), "window": window, "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# copperusd-mean-reversion-1h
|
||||
|
||||
Copper mean-reversion baseline for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `COPPERUSD` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `COPPERUSD`
|
||||
- Timeframe: `1h`
|
||||
- Family: `custom Python`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "copperusd-mean-reversion-1h",
|
||||
"description": "custom Python PyP Quant template for COPPERUSD 1h.",
|
||||
"symbol": "COPPERUSD",
|
||||
"timeframe": "1h",
|
||||
"model_family": "custom Python",
|
||||
"artifact_target": "python",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.003,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
SYMBOL = "COPPERUSD"
|
||||
MODEL_NAME = "copperusd-mean-reversion-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _zscore(close, window):
|
||||
mean = close.rolling(window).mean()
|
||||
std = close.rolling(window).std()
|
||||
return (close - mean) / (std + 1e-9)
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
return {"window": int(p.get("window", 48)), "entry_z": float(p.get("entry_z", 1.4)), "exit_z": float(p.get("exit_z", 0.3)), "symbol": SYMBOL}, {"model_family": "rule_based_mean_reversion", "training_bars": int(len(data))}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
lookback = int(p.get("lookback", 140))
|
||||
if len(candles) < lookback:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
window = int(model.get("window", p.get("window", 48)))
|
||||
z = float(_zscore(df["close"], window).iloc[-1])
|
||||
entry_z = float(model.get("entry_z", p.get("entry_z", 1.4)))
|
||||
signal = "HOLD"
|
||||
if z <= -entry_z:
|
||||
signal = "UP"
|
||||
elif z >= entry_z:
|
||||
signal = "DOWN"
|
||||
confidence = min(abs(z) / max(entry_z, 1e-9), 1.0)
|
||||
return {"signal": signal, "confidence": round(float(confidence), 4), "metadata": {"zscore": round(z, 4), "window": window, "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# dogeusdt-scalp-logistic-1m
|
||||
|
||||
DOGE high-volatility scalp classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `DOGEUSDT` on the `1m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `DOGEUSDT`
|
||||
- Timeframe: `1m`
|
||||
- Family: `sklearn LogisticRegression`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "dogeusdt-scalp-logistic-1m",
|
||||
"description": "sklearn LogisticRegression PyP Quant template for DOGEUSDT 1m.",
|
||||
"symbol": "DOGEUSDT",
|
||||
"timeframe": "1m",
|
||||
"model_family": "sklearn LogisticRegression",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.003,
|
||||
"min_confidence": 0.48,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.linear_model import LogisticRegression
|
||||
from sklearn.pipeline import Pipeline
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
|
||||
|
||||
SYMBOL = "DOGEUSDT"
|
||||
MODEL_NAME = "dogeusdt-scalp-logistic-1m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
rng = (df["high"] - df["low"]).replace(0, np.nan)
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 3, 6, 12, 24]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["range_pct"] = rng / c
|
||||
f["body_pct"] = (c - df["open"]) / (rng + 1e-9)
|
||||
f["close_pos"] = (c - df["low"]) / (rng + 1e-9)
|
||||
f["ema_8_21"] = (c.ewm(span=8, adjust=False).mean() - c.ewm(span=21, adjust=False).mean()) / c
|
||||
f["ema_21_55"] = (c.ewm(span=21, adjust=False).mean() - c.ewm(span=55, adjust=False).mean()) / c
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["volume_ratio"] = df["volume"] / (df["volume"].rolling(24).mean() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.003))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", LogisticRegression(max_iter=1000, class_weight="balanced")),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 120)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.48)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# dotusdt-mean-reversion-30m
|
||||
|
||||
DOT mean-reversion baseline for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `DOTUSDT` on the `30m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `DOTUSDT`
|
||||
- Timeframe: `30m`
|
||||
- Family: `custom Python`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "dotusdt-mean-reversion-30m",
|
||||
"description": "custom Python PyP Quant template for DOTUSDT 30m.",
|
||||
"symbol": "DOTUSDT",
|
||||
"timeframe": "30m",
|
||||
"model_family": "custom Python",
|
||||
"artifact_target": "python",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.004,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
SYMBOL = "DOTUSDT"
|
||||
MODEL_NAME = "dotusdt-mean-reversion-30m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _zscore(close, window):
|
||||
mean = close.rolling(window).mean()
|
||||
std = close.rolling(window).std()
|
||||
return (close - mean) / (std + 1e-9)
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
return {"window": int(p.get("window", 48)), "entry_z": float(p.get("entry_z", 1.4)), "exit_z": float(p.get("exit_z", 0.3)), "symbol": SYMBOL}, {"model_family": "rule_based_mean_reversion", "training_bars": int(len(data))}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
lookback = int(p.get("lookback", 120))
|
||||
if len(candles) < lookback:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
window = int(model.get("window", p.get("window", 48)))
|
||||
z = float(_zscore(df["close"], window).iloc[-1])
|
||||
entry_z = float(model.get("entry_z", p.get("entry_z", 1.4)))
|
||||
signal = "HOLD"
|
||||
if z <= -entry_z:
|
||||
signal = "UP"
|
||||
elif z >= entry_z:
|
||||
signal = "DOWN"
|
||||
confidence = min(abs(z) / max(entry_z, 1e-9), 1.0)
|
||||
return {"signal": signal, "confidence": round(float(confidence), 4), "metadata": {"zscore": round(z, 4), "window": window, "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -1 +1,28 @@
|
||||
{"pair":"ETHUSDT","timeframe":"30m","model_family":"sklearn","runtime_target":"edge","artifact_format":"weights_bundle","parameters":{"lookback":120,"horizon":4,"threshold":0.005,"min_confidence":0.5},"training_requirements":["numpy","pandas","scikit-learn","joblib"],"inference_requirements":["numpy","pandas","scikit-learn","joblib"]}
|
||||
{
|
||||
"pair": "ETHUSDT",
|
||||
"timeframe": "30m",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"runtime_target": "edge",
|
||||
"artifact_format": "weights_bundle",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.005,
|
||||
"min_confidence": 0.5
|
||||
},
|
||||
"training_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"joblib"
|
||||
],
|
||||
"inference_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"joblib"
|
||||
],
|
||||
"symbol": "ETHUSDT",
|
||||
"description": "ETHUSDT volatility-aware directional classifier",
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# eurgbp-range-classifier-30m
|
||||
|
||||
EURGBP range classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `EURGBP` on the `30m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `EURGBP`
|
||||
- Timeframe: `30m`
|
||||
- Family: `sklearn LogisticRegression`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "eurgbp-range-classifier-30m",
|
||||
"description": "sklearn LogisticRegression PyP Quant template for EURGBP 30m.",
|
||||
"symbol": "EURGBP",
|
||||
"timeframe": "30m",
|
||||
"model_family": "sklearn LogisticRegression",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.0012,
|
||||
"min_confidence": 0.48,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.linear_model import LogisticRegression
|
||||
from sklearn.pipeline import Pipeline
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
|
||||
|
||||
SYMBOL = "EURGBP"
|
||||
MODEL_NAME = "eurgbp-range-classifier-30m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
rng = (df["high"] - df["low"]).replace(0, np.nan)
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 3, 6, 12, 24]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["range_pct"] = rng / c
|
||||
f["body_pct"] = (c - df["open"]) / (rng + 1e-9)
|
||||
f["close_pos"] = (c - df["low"]) / (rng + 1e-9)
|
||||
f["ema_8_21"] = (c.ewm(span=8, adjust=False).mean() - c.ewm(span=21, adjust=False).mean()) / c
|
||||
f["ema_21_55"] = (c.ewm(span=21, adjust=False).mean() - c.ewm(span=55, adjust=False).mean()) / c
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["volume_ratio"] = df["volume"] / (df["volume"].rolling(24).mean() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.0012))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", LogisticRegression(max_iter=1000, class_weight="balanced")),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 120)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.48)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# eurjpy-trend-rf-1h
|
||||
|
||||
EURJPY trend classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `EURJPY` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `EURJPY`
|
||||
- Timeframe: `1h`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "eurjpy-trend-rf-1h",
|
||||
"description": "sklearn RandomForest PyP Quant template for EURJPY 1h.",
|
||||
"symbol": "EURJPY",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0018,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "EURJPY"
|
||||
MODEL_NAME = "eurjpy-trend-rf-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.0018))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 140)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# eurnzd-volatility-rf-4h
|
||||
|
||||
EURNZD volatility classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `EURNZD` on the `4h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `EURNZD`
|
||||
- Timeframe: `4h`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "eurnzd-volatility-rf-4h",
|
||||
"description": "sklearn RandomForest PyP Quant template for EURNZD 4h.",
|
||||
"symbol": "EURNZD",
|
||||
"timeframe": "4h",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 180,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0012,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "EURNZD"
|
||||
MODEL_NAME = "eurnzd-volatility-rf-4h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.0012))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 180)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"pair": "EURUSD",
|
||||
"timeframe": "15m",
|
||||
"model_family": "sklearn",
|
||||
"model_family": "sklearn LogisticRegression",
|
||||
"runtime_target": "edge",
|
||||
"artifact_format": "weights_bundle",
|
||||
"parameters": {
|
||||
@@ -10,6 +10,19 @@
|
||||
"threshold": 0.0008,
|
||||
"min_confidence": 0.48
|
||||
},
|
||||
"training_requirements": ["numpy", "pandas", "scikit-learn", "joblib"],
|
||||
"inference_requirements": ["numpy", "pandas", "scikit-learn", "joblib"]
|
||||
"training_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"joblib"
|
||||
],
|
||||
"inference_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"joblib"
|
||||
],
|
||||
"symbol": "EURUSD",
|
||||
"description": "EURUSD logistic regression baseline classifier",
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
|
||||
@@ -1 +1,30 @@
|
||||
{"pair":"EURUSD","timeframe":"1h","model_family":"xgboost","runtime_target":"modal","artifact_format":"joblib_bundle","parameters":{"lookback":160,"horizon":3,"threshold":0.001,"min_confidence":0.48},"training_requirements":["numpy","pandas","scikit-learn","xgboost","joblib"],"inference_requirements":["numpy","pandas","scikit-learn","xgboost","joblib"]}
|
||||
{
|
||||
"pair": "EURUSD",
|
||||
"timeframe": "1h",
|
||||
"model_family": "XGBoost",
|
||||
"runtime_target": "modal",
|
||||
"artifact_format": "joblib_bundle",
|
||||
"parameters": {
|
||||
"lookback": 160,
|
||||
"horizon": 3,
|
||||
"threshold": 0.001,
|
||||
"min_confidence": 0.48
|
||||
},
|
||||
"training_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"xgboost",
|
||||
"joblib"
|
||||
],
|
||||
"inference_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"xgboost",
|
||||
"joblib"
|
||||
],
|
||||
"symbol": "EURUSD",
|
||||
"description": "EURUSD feature-rich XGBoost trend classifier",
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# gbpjpy-breakout-rf-1h
|
||||
|
||||
GBPJPY breakout classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `GBPJPY` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `GBPJPY`
|
||||
- Timeframe: `1h`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "gbpjpy-breakout-rf-1h",
|
||||
"description": "sklearn RandomForest PyP Quant template for GBPJPY 1h.",
|
||||
"symbol": "GBPJPY",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0018,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
|
||||
|
||||
SYMBOL = "GBPJPY"
|
||||
MODEL_NAME = "gbpjpy-breakout-rf-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
h = df["high"]
|
||||
l = df["low"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
high_break = h.rolling(36).max().shift()
|
||||
low_break = l.rolling(36).min().shift()
|
||||
f["breakout_up"] = (c - high_break) / c
|
||||
f["breakout_down"] = (c - low_break) / c
|
||||
f["ret4"] = c.pct_change(4)
|
||||
f["ret12"] = c.pct_change(12)
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["range_pct"] = (h - l) / c
|
||||
f["ema_slope"] = c.ewm(span=12, adjust=False).mean().pct_change(6)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.0018))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
clf = RandomForestClassifier(n_estimators=300, max_depth=7, min_samples_leaf=6, class_weight="balanced_subsample", random_state=42, n_jobs=-1)
|
||||
clf.fit(x.values.astype(np.float32), y.values)
|
||||
pred = clf.predict(x.values.astype(np.float32))
|
||||
return {"model": clf, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 140)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -1 +1,28 @@
|
||||
{"pair":"GBPUSD","timeframe":"30m","model_family":"sklearn","runtime_target":"edge","artifact_format":"weights_bundle","parameters":{"lookback":120,"horizon":4,"threshold":0.0012,"min_confidence":0.5},"training_requirements":["numpy","pandas","scikit-learn","joblib"],"inference_requirements":["numpy","pandas","scikit-learn","joblib"]}
|
||||
{
|
||||
"pair": "GBPUSD",
|
||||
"timeframe": "30m",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"runtime_target": "edge",
|
||||
"artifact_format": "weights_bundle",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.0012,
|
||||
"min_confidence": 0.5
|
||||
},
|
||||
"training_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"joblib"
|
||||
],
|
||||
"inference_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"joblib"
|
||||
],
|
||||
"symbol": "GBPUSD",
|
||||
"description": "GBPUSD range breakout random forest classifier",
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# gbpusd-logistic-15m
|
||||
|
||||
GBPUSD directional baseline for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `GBPUSD` on the `15m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `GBPUSD`
|
||||
- Timeframe: `15m`
|
||||
- Family: `sklearn LogisticRegression`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "gbpusd-logistic-15m",
|
||||
"description": "sklearn LogisticRegression PyP Quant template for GBPUSD 15m.",
|
||||
"symbol": "GBPUSD",
|
||||
"timeframe": "15m",
|
||||
"model_family": "sklearn LogisticRegression",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 100,
|
||||
"horizon": 4,
|
||||
"threshold": 0.0012,
|
||||
"min_confidence": 0.48,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.linear_model import LogisticRegression
|
||||
from sklearn.pipeline import Pipeline
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
|
||||
|
||||
SYMBOL = "GBPUSD"
|
||||
MODEL_NAME = "gbpusd-logistic-15m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
rng = (df["high"] - df["low"]).replace(0, np.nan)
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 3, 6, 12, 24]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["range_pct"] = rng / c
|
||||
f["body_pct"] = (c - df["open"]) / (rng + 1e-9)
|
||||
f["close_pos"] = (c - df["low"]) / (rng + 1e-9)
|
||||
f["ema_8_21"] = (c.ewm(span=8, adjust=False).mean() - c.ewm(span=21, adjust=False).mean()) / c
|
||||
f["ema_21_55"] = (c.ewm(span=21, adjust=False).mean() - c.ewm(span=55, adjust=False).mean()) / c
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["volume_ratio"] = df["volume"] / (df["volume"].rolling(24).mean() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.0012))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", LogisticRegression(max_iter=1000, class_weight="balanced")),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 100)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.48)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# ger40-breakout-rf-30m
|
||||
|
||||
DAX breakout classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `GER40` on the `30m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `GER40`
|
||||
- Timeframe: `30m`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "ger40-breakout-rf-30m",
|
||||
"description": "sklearn RandomForest PyP Quant template for GER40 30m.",
|
||||
"symbol": "GER40",
|
||||
"timeframe": "30m",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.0025,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
|
||||
|
||||
SYMBOL = "GER40"
|
||||
MODEL_NAME = "ger40-breakout-rf-30m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
h = df["high"]
|
||||
l = df["low"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
high_break = h.rolling(36).max().shift()
|
||||
low_break = l.rolling(36).min().shift()
|
||||
f["breakout_up"] = (c - high_break) / c
|
||||
f["breakout_down"] = (c - low_break) / c
|
||||
f["ret4"] = c.pct_change(4)
|
||||
f["ret12"] = c.pct_change(12)
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["range_pct"] = (h - l) / c
|
||||
f["ema_slope"] = c.ewm(span=12, adjust=False).mean().pct_change(6)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.0025))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
clf = RandomForestClassifier(n_estimators=300, max_depth=7, min_samples_leaf=6, class_weight="balanced_subsample", random_state=42, n_jobs=-1)
|
||||
clf.fit(x.values.astype(np.float32), y.values)
|
||||
pred = clf.predict(x.values.astype(np.float32))
|
||||
return {"model": clf, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 120)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -1 +1,30 @@
|
||||
{"pair":"EURUSD","timeframe":"30m","model_family":"lightgbm","runtime_target":"modal","artifact_format":"joblib_bundle","parameters":{"lookback":140,"horizon":4,"threshold":0.001,"min_confidence":0.48},"training_requirements":["numpy","pandas","scikit-learn","lightgbm","joblib"],"inference_requirements":["numpy","pandas","scikit-learn","lightgbm","joblib"]}
|
||||
{
|
||||
"pair": "EURUSD",
|
||||
"timeframe": "30m",
|
||||
"model_family": "LightGBM",
|
||||
"runtime_target": "modal",
|
||||
"artifact_format": "joblib_bundle",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 4,
|
||||
"threshold": 0.001,
|
||||
"min_confidence": 0.48
|
||||
},
|
||||
"training_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"lightgbm",
|
||||
"joblib"
|
||||
],
|
||||
"inference_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"lightgbm",
|
||||
"joblib"
|
||||
],
|
||||
"symbol": "EURUSD",
|
||||
"description": "Multi-feature FX LightGBM classifier",
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# linkusdt-trend-logistic-30m
|
||||
|
||||
LINK trend classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `LINKUSDT` on the `30m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `LINKUSDT`
|
||||
- Timeframe: `30m`
|
||||
- Family: `sklearn LogisticRegression`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "linkusdt-trend-logistic-30m",
|
||||
"description": "sklearn LogisticRegression PyP Quant template for LINKUSDT 30m.",
|
||||
"symbol": "LINKUSDT",
|
||||
"timeframe": "30m",
|
||||
"model_family": "sklearn LogisticRegression",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.003,
|
||||
"min_confidence": 0.48,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.linear_model import LogisticRegression
|
||||
from sklearn.pipeline import Pipeline
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
|
||||
|
||||
SYMBOL = "LINKUSDT"
|
||||
MODEL_NAME = "linkusdt-trend-logistic-30m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
rng = (df["high"] - df["low"]).replace(0, np.nan)
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 3, 6, 12, 24]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["range_pct"] = rng / c
|
||||
f["body_pct"] = (c - df["open"]) / (rng + 1e-9)
|
||||
f["close_pos"] = (c - df["low"]) / (rng + 1e-9)
|
||||
f["ema_8_21"] = (c.ewm(span=8, adjust=False).mean() - c.ewm(span=21, adjust=False).mean()) / c
|
||||
f["ema_21_55"] = (c.ewm(span=21, adjust=False).mean() - c.ewm(span=55, adjust=False).mean()) / c
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["volume_ratio"] = df["volume"] / (df["volume"].rolling(24).mean() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.003))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", LogisticRegression(max_iter=1000, class_weight="balanced")),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 120)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.48)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# ltcusdt-trend-rf-1h
|
||||
|
||||
LTC trend classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `LTCUSDT` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `LTCUSDT`
|
||||
- Timeframe: `1h`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "ltcusdt-trend-rf-1h",
|
||||
"description": "sklearn RandomForest PyP Quant template for LTCUSDT 1h.",
|
||||
"symbol": "LTCUSDT",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.004,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "LTCUSDT"
|
||||
MODEL_NAME = "ltcusdt-trend-rf-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.004))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 140)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# maticusdt-scalp-baseline-5m
|
||||
|
||||
MATIC scalp baseline for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `MATICUSDT` on the `5m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `MATICUSDT`
|
||||
- Timeframe: `5m`
|
||||
- Family: `custom Python`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "maticusdt-scalp-baseline-5m",
|
||||
"description": "custom Python PyP Quant template for MATICUSDT 5m.",
|
||||
"symbol": "MATICUSDT",
|
||||
"timeframe": "5m",
|
||||
"model_family": "custom Python",
|
||||
"artifact_target": "python",
|
||||
"parameters": {
|
||||
"lookback": 100,
|
||||
"horizon": 4,
|
||||
"threshold": 0.004,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.4,
|
||||
"tp_percent": 0.8
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
SYMBOL = "MATICUSDT"
|
||||
MODEL_NAME = "maticusdt-scalp-baseline-5m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _atr(df, n=14):
|
||||
h, l, c = df["high"], df["low"], df["close"]
|
||||
tr = pd.concat([(h - l), (h - c.shift()).abs(), (l - c.shift()).abs()], axis=1).max(axis=1)
|
||||
return tr.ewm(span=n, adjust=False).mean()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
return {"atr_window": int(p.get("atr_window", 14)), "breakout_window": int(p.get("breakout_window", 36)), "atr_mult": float(p.get("atr_mult", 0.25)), "symbol": SYMBOL}, {"model_family": "rule_based_atr_breakout", "training_bars": int(len(data))}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
lookback = int(p.get("lookback", 100))
|
||||
if len(candles) < lookback:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
atr_window = int(model.get("atr_window", p.get("atr_window", 14)))
|
||||
breakout_window = int(model.get("breakout_window", p.get("breakout_window", 36)))
|
||||
atr_mult = float(model.get("atr_mult", p.get("atr_mult", 0.25)))
|
||||
atr = _atr(df, atr_window)
|
||||
close = float(df["close"].iloc[-1])
|
||||
upper = float(df["high"].rolling(breakout_window).max().shift().iloc[-1] + atr.iloc[-1] * atr_mult)
|
||||
lower = float(df["low"].rolling(breakout_window).min().shift().iloc[-1] - atr.iloc[-1] * atr_mult)
|
||||
signal = "HOLD"
|
||||
distance = 0.0
|
||||
if close > upper:
|
||||
signal = "UP"
|
||||
distance = (close - upper) / max(atr.iloc[-1], 1e-9)
|
||||
elif close < lower:
|
||||
signal = "DOWN"
|
||||
distance = (lower - close) / max(atr.iloc[-1], 1e-9)
|
||||
confidence = min(max(distance, 0.0), 1.0)
|
||||
return {"signal": signal, "confidence": round(float(confidence), 4), "metadata": {"upper": round(upper, 6), "lower": round(lower, 6), "close": round(close, 6), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# nas100-breakout-rf-15m
|
||||
|
||||
NASDAQ index breakout classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `NAS100` on the `15m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `NAS100`
|
||||
- Timeframe: `15m`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "nas100-breakout-rf-15m",
|
||||
"description": "sklearn RandomForest PyP Quant template for NAS100 15m.",
|
||||
"symbol": "NAS100",
|
||||
"timeframe": "15m",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 100,
|
||||
"horizon": 4,
|
||||
"threshold": 0.0025,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
|
||||
|
||||
SYMBOL = "NAS100"
|
||||
MODEL_NAME = "nas100-breakout-rf-15m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
h = df["high"]
|
||||
l = df["low"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
high_break = h.rolling(36).max().shift()
|
||||
low_break = l.rolling(36).min().shift()
|
||||
f["breakout_up"] = (c - high_break) / c
|
||||
f["breakout_down"] = (c - low_break) / c
|
||||
f["ret4"] = c.pct_change(4)
|
||||
f["ret12"] = c.pct_change(12)
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["range_pct"] = (h - l) / c
|
||||
f["ema_slope"] = c.ewm(span=12, adjust=False).mean().pct_change(6)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.0025))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
clf = RandomForestClassifier(n_estimators=300, max_depth=7, min_samples_leaf=6, class_weight="balanced_subsample", random_state=42, n_jobs=-1)
|
||||
clf.fit(x.values.astype(np.float32), y.values)
|
||||
pred = clf.predict(x.values.astype(np.float32))
|
||||
return {"model": clf, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 100)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# naturalgas-volatility-rf-4h
|
||||
|
||||
Natural gas volatility classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `NATGAS` on the `4h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `NATGAS`
|
||||
- Timeframe: `4h`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "naturalgas-volatility-rf-4h",
|
||||
"description": "sklearn RandomForest PyP Quant template for NATGAS 4h.",
|
||||
"symbol": "NATGAS",
|
||||
"timeframe": "4h",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 180,
|
||||
"horizon": 6,
|
||||
"threshold": 0.003,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "NATGAS"
|
||||
MODEL_NAME = "naturalgas-volatility-rf-4h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.003))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 180)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# nzdusd-mean-reversion-1h
|
||||
|
||||
NZDUSD mean-reversion baseline for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `NZDUSD` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `NZDUSD`
|
||||
- Timeframe: `1h`
|
||||
- Family: `custom Python`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "nzdusd-mean-reversion-1h",
|
||||
"description": "custom Python PyP Quant template for NZDUSD 1h.",
|
||||
"symbol": "NZDUSD",
|
||||
"timeframe": "1h",
|
||||
"model_family": "custom Python",
|
||||
"artifact_target": "python",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0012,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
SYMBOL = "NZDUSD"
|
||||
MODEL_NAME = "nzdusd-mean-reversion-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _zscore(close, window):
|
||||
mean = close.rolling(window).mean()
|
||||
std = close.rolling(window).std()
|
||||
return (close - mean) / (std + 1e-9)
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
return {"window": int(p.get("window", 48)), "entry_z": float(p.get("entry_z", 1.4)), "exit_z": float(p.get("exit_z", 0.3)), "symbol": SYMBOL}, {"model_family": "rule_based_mean_reversion", "training_bars": int(len(data))}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
lookback = int(p.get("lookback", 140))
|
||||
if len(candles) < lookback:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
window = int(model.get("window", p.get("window", 48)))
|
||||
z = float(_zscore(df["close"], window).iloc[-1])
|
||||
entry_z = float(model.get("entry_z", p.get("entry_z", 1.4)))
|
||||
signal = "HOLD"
|
||||
if z <= -entry_z:
|
||||
signal = "UP"
|
||||
elif z >= entry_z:
|
||||
signal = "DOWN"
|
||||
confidence = min(abs(z) / max(entry_z, 1e-9), 1.0)
|
||||
return {"signal": signal, "confidence": round(float(confidence), 4), "metadata": {"zscore": round(z, 4), "window": window, "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# oilwtico-atr-breakout-1h
|
||||
|
||||
WTI crude oil ATR breakout baseline for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `WTICOUSD` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `WTICOUSD`
|
||||
- Timeframe: `1h`
|
||||
- Family: `custom Python`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "oilwtico-atr-breakout-1h",
|
||||
"description": "custom Python PyP Quant template for WTICOUSD 1h.",
|
||||
"symbol": "WTICOUSD",
|
||||
"timeframe": "1h",
|
||||
"model_family": "custom Python",
|
||||
"artifact_target": "python",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.003,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
SYMBOL = "WTICOUSD"
|
||||
MODEL_NAME = "oilwtico-atr-breakout-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _atr(df, n=14):
|
||||
h, l, c = df["high"], df["low"], df["close"]
|
||||
tr = pd.concat([(h - l), (h - c.shift()).abs(), (l - c.shift()).abs()], axis=1).max(axis=1)
|
||||
return tr.ewm(span=n, adjust=False).mean()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
return {"atr_window": int(p.get("atr_window", 14)), "breakout_window": int(p.get("breakout_window", 36)), "atr_mult": float(p.get("atr_mult", 0.25)), "symbol": SYMBOL}, {"model_family": "rule_based_atr_breakout", "training_bars": int(len(data))}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
lookback = int(p.get("lookback", 140))
|
||||
if len(candles) < lookback:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
atr_window = int(model.get("atr_window", p.get("atr_window", 14)))
|
||||
breakout_window = int(model.get("breakout_window", p.get("breakout_window", 36)))
|
||||
atr_mult = float(model.get("atr_mult", p.get("atr_mult", 0.25)))
|
||||
atr = _atr(df, atr_window)
|
||||
close = float(df["close"].iloc[-1])
|
||||
upper = float(df["high"].rolling(breakout_window).max().shift().iloc[-1] + atr.iloc[-1] * atr_mult)
|
||||
lower = float(df["low"].rolling(breakout_window).min().shift().iloc[-1] - atr.iloc[-1] * atr_mult)
|
||||
signal = "HOLD"
|
||||
distance = 0.0
|
||||
if close > upper:
|
||||
signal = "UP"
|
||||
distance = (close - upper) / max(atr.iloc[-1], 1e-9)
|
||||
elif close < lower:
|
||||
signal = "DOWN"
|
||||
distance = (lower - close) / max(atr.iloc[-1], 1e-9)
|
||||
confidence = min(max(distance, 0.0), 1.0)
|
||||
return {"signal": signal, "confidence": round(float(confidence), 4), "metadata": {"upper": round(upper, 6), "lower": round(lower, 6), "close": round(close, 6), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -1 +1,28 @@
|
||||
{"pair":"EURUSD","timeframe":"1h","model_family":"sklearn","runtime_target":"container","artifact_format":"onnx","parameters":{"lookback":120,"horizon":3,"threshold":0.001,"min_confidence":0.5},"training_requirements":["numpy","pandas","scikit-learn","joblib","skl2onnx","onnx"],"inference_requirements":["numpy","onnxruntime"]}
|
||||
{
|
||||
"pair": "EURUSD",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn to ONNX",
|
||||
"runtime_target": "container",
|
||||
"artifact_format": "onnx",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 3,
|
||||
"threshold": 0.001,
|
||||
"min_confidence": 0.5
|
||||
},
|
||||
"training_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"joblib",
|
||||
"skl2onnx",
|
||||
"onnx"
|
||||
],
|
||||
"inference_requirements": [
|
||||
"numpy",
|
||||
"onnxruntime"
|
||||
],
|
||||
"symbol": "EURUSD",
|
||||
"description": "Sklearn classifier starter for ONNX export",
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"pair": "SOLUSDT",
|
||||
"timeframe": "1m",
|
||||
"model_family": "custom_python",
|
||||
"model_family": "custom Python",
|
||||
"runtime_target": "edge",
|
||||
"artifact_format": "python_bundle",
|
||||
"parameters": {
|
||||
@@ -11,6 +11,15 @@
|
||||
"vol_window": 20,
|
||||
"min_move": 0.0006
|
||||
},
|
||||
"training_requirements": ["numpy", "pandas"],
|
||||
"inference_requirements": ["numpy", "pandas"]
|
||||
"training_requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"inference_requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"symbol": "SOLUSDT",
|
||||
"description": "SOLUSDT high-volatility scalp baseline",
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# spx500-trend-logistic-1h
|
||||
|
||||
S&P 500 trend classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `SPX500` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `SPX500`
|
||||
- Timeframe: `1h`
|
||||
- Family: `sklearn LogisticRegression`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "spx500-trend-logistic-1h",
|
||||
"description": "sklearn LogisticRegression PyP Quant template for SPX500 1h.",
|
||||
"symbol": "SPX500",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn LogisticRegression",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0025,
|
||||
"min_confidence": 0.48,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.linear_model import LogisticRegression
|
||||
from sklearn.pipeline import Pipeline
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
|
||||
|
||||
SYMBOL = "SPX500"
|
||||
MODEL_NAME = "spx500-trend-logistic-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
rng = (df["high"] - df["low"]).replace(0, np.nan)
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 3, 6, 12, 24]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["range_pct"] = rng / c
|
||||
f["body_pct"] = (c - df["open"]) / (rng + 1e-9)
|
||||
f["close_pos"] = (c - df["low"]) / (rng + 1e-9)
|
||||
f["ema_8_21"] = (c.ewm(span=8, adjust=False).mean() - c.ewm(span=21, adjust=False).mean()) / c
|
||||
f["ema_21_55"] = (c.ewm(span=21, adjust=False).mean() - c.ewm(span=55, adjust=False).mean()) / c
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["volume_ratio"] = df["volume"] / (df["volume"].rolling(24).mean() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.0025))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", LogisticRegression(max_iter=1000, class_weight="balanced")),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 140)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.48)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -1 +1,26 @@
|
||||
{"pair":"EURUSD","timeframe":"1h","model_family":"statsmodels","runtime_target":"modal","artifact_format":"joblib_bundle","parameters":{"lookback":96,"entry_threshold":0.0004},"training_requirements":["numpy","pandas","statsmodels","joblib"],"inference_requirements":["numpy","pandas","statsmodels","joblib"]}
|
||||
{
|
||||
"pair": "EURUSD",
|
||||
"timeframe": "1h",
|
||||
"model_family": "statsmodels",
|
||||
"runtime_target": "modal",
|
||||
"artifact_format": "joblib_bundle",
|
||||
"parameters": {
|
||||
"lookback": 96,
|
||||
"entry_threshold": 0.0004
|
||||
},
|
||||
"training_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"statsmodels",
|
||||
"joblib"
|
||||
],
|
||||
"inference_requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"statsmodels",
|
||||
"joblib"
|
||||
],
|
||||
"symbol": "EURUSD",
|
||||
"description": "ARIMA-style statistical direction baseline",
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# uk100-range-logistic-1h
|
||||
|
||||
FTSE range classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `UK100` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `UK100`
|
||||
- Timeframe: `1h`
|
||||
- Family: `sklearn LogisticRegression`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "uk100-range-logistic-1h",
|
||||
"description": "sklearn LogisticRegression PyP Quant template for UK100 1h.",
|
||||
"symbol": "UK100",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn LogisticRegression",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0025,
|
||||
"min_confidence": 0.48,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.linear_model import LogisticRegression
|
||||
from sklearn.pipeline import Pipeline
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
|
||||
|
||||
SYMBOL = "UK100"
|
||||
MODEL_NAME = "uk100-range-logistic-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
rng = (df["high"] - df["low"]).replace(0, np.nan)
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 3, 6, 12, 24]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["range_pct"] = rng / c
|
||||
f["body_pct"] = (c - df["open"]) / (rng + 1e-9)
|
||||
f["close_pos"] = (c - df["low"]) / (rng + 1e-9)
|
||||
f["ema_8_21"] = (c.ewm(span=8, adjust=False).mean() - c.ewm(span=21, adjust=False).mean()) / c
|
||||
f["ema_21_55"] = (c.ewm(span=21, adjust=False).mean() - c.ewm(span=55, adjust=False).mean()) / c
|
||||
f["volatility"] = c.pct_change().rolling(24).std()
|
||||
f["volume_ratio"] = df["volume"] / (df["volume"].rolling(24).mean() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 4))
|
||||
threshold = float(p.get("threshold", 0.0025))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", LogisticRegression(max_iter=1000, class_weight="balanced")),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 140)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.48)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# us30-mean-reversion-30m
|
||||
|
||||
Dow index mean-reversion baseline for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `US30` on the `30m` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `US30`
|
||||
- Timeframe: `30m`
|
||||
- Family: `custom Python`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "us30-mean-reversion-30m",
|
||||
"description": "custom Python PyP Quant template for US30 30m.",
|
||||
"symbol": "US30",
|
||||
"timeframe": "30m",
|
||||
"model_family": "custom Python",
|
||||
"artifact_target": "python",
|
||||
"parameters": {
|
||||
"lookback": 120,
|
||||
"horizon": 4,
|
||||
"threshold": 0.0025,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
SYMBOL = "US30"
|
||||
MODEL_NAME = "us30-mean-reversion-30m"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _zscore(close, window):
|
||||
mean = close.rolling(window).mean()
|
||||
std = close.rolling(window).std()
|
||||
return (close - mean) / (std + 1e-9)
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
return {"window": int(p.get("window", 48)), "entry_z": float(p.get("entry_z", 1.4)), "exit_z": float(p.get("exit_z", 0.3)), "symbol": SYMBOL}, {"model_family": "rule_based_mean_reversion", "training_bars": int(len(data))}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
lookback = int(p.get("lookback", 120))
|
||||
if len(candles) < lookback:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
window = int(model.get("window", p.get("window", 48)))
|
||||
z = float(_zscore(df["close"], window).iloc[-1])
|
||||
entry_z = float(model.get("entry_z", p.get("entry_z", 1.4)))
|
||||
signal = "HOLD"
|
||||
if z <= -entry_z:
|
||||
signal = "UP"
|
||||
elif z >= entry_z:
|
||||
signal = "DOWN"
|
||||
confidence = min(abs(z) / max(entry_z, 1e-9), 1.0)
|
||||
return {"signal": signal, "confidence": round(float(confidence), 4), "metadata": {"zscore": round(z, 4), "window": window, "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
@@ -0,0 +1,29 @@
|
||||
# usdcad-trend-rf-1h
|
||||
|
||||
USDCAD trend classifier for PyP Quant Mode.
|
||||
|
||||
This is an educational starter template for `USDCAD` on the `1h` timeframe. It implements the PyP Quant contract:
|
||||
|
||||
```python
|
||||
train(data, config)
|
||||
predict(model, market_data, config)
|
||||
```
|
||||
|
||||
Use it as a baseline, then validate with PPE before any live deployment.
|
||||
|
||||
## Model
|
||||
|
||||
- Symbol: `USDCAD`
|
||||
- Timeframe: `1h`
|
||||
- Family: `sklearn RandomForest`
|
||||
- Output: `UP`, `DOWN`, or `HOLD`
|
||||
|
||||
## PyP Links
|
||||
|
||||
- Quant docs: https://pyp.stanl.ink/docs/quant/what-is-quant-mode
|
||||
- Quant landing page: https://pyp.stanl.ink/for-quant-traders
|
||||
- Create project: https://pyp.stanlink.online/projects/quant/new
|
||||
|
||||
## Risk
|
||||
|
||||
This is not financial advice and is not a verified profitable strategy.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "usdcad-trend-rf-1h",
|
||||
"description": "sklearn RandomForest PyP Quant template for USDCAD 1h.",
|
||||
"symbol": "USDCAD",
|
||||
"timeframe": "1h",
|
||||
"model_family": "sklearn RandomForest",
|
||||
"artifact_target": "joblib",
|
||||
"parameters": {
|
||||
"lookback": 140,
|
||||
"horizon": 6,
|
||||
"threshold": 0.0012,
|
||||
"min_confidence": 0.5,
|
||||
"sl_percent": 0.25,
|
||||
"tp_percent": 0.5
|
||||
},
|
||||
"requirements": [
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn"
|
||||
],
|
||||
"disclaimer": "Educational template only. Not financial advice."
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
|
||||
SYMBOL = "USDCAD"
|
||||
MODEL_NAME = "usdcad-trend-rf-1h"
|
||||
|
||||
|
||||
def _prep(data):
|
||||
df = data.copy()
|
||||
df.columns = [str(c).strip().lower() for c in df.columns]
|
||||
if "volume" not in df.columns:
|
||||
df["volume"] = 1.0
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
return df.dropna(subset=["open", "high", "low", "close"]).reset_index(drop=True)
|
||||
|
||||
|
||||
def _features(df):
|
||||
c = df["close"]
|
||||
v = df["volume"]
|
||||
f = pd.DataFrame(index=df.index)
|
||||
for n in [1, 4, 8, 16, 32, 64]:
|
||||
f[f"ret{n}"] = c.pct_change(n)
|
||||
f["ema_12_48"] = (c.ewm(span=12, adjust=False).mean() - c.ewm(span=48, adjust=False).mean()) / c
|
||||
f["ema_24_96"] = (c.ewm(span=24, adjust=False).mean() - c.ewm(span=96, adjust=False).mean()) / c
|
||||
f["volatility_fast"] = c.pct_change().rolling(16).std()
|
||||
f["volatility_slow"] = c.pct_change().rolling(64).std()
|
||||
f["volatility_ratio"] = f["volatility_fast"] / (f["volatility_slow"] + 1e-9)
|
||||
f["volume_z"] = (v - v.rolling(48).mean()) / (v.rolling(48).std() + 1e-9)
|
||||
return f.replace([np.inf, -np.inf], np.nan).dropna()
|
||||
|
||||
|
||||
def train(data, config):
|
||||
p = config.get("parameters", {})
|
||||
df = _prep(data)
|
||||
x = _features(df)
|
||||
horizon = int(p.get("horizon", 6))
|
||||
threshold = float(p.get("threshold", 0.0012))
|
||||
fwd = df["close"].pct_change(horizon).shift(-horizon)
|
||||
y = pd.Series(1, index=df.index)
|
||||
y[fwd > threshold] = 2
|
||||
y[fwd < -threshold] = 0
|
||||
y = y.reindex(x.index).fillna(1).astype(int)
|
||||
model = Pipeline([
|
||||
("scaler", StandardScaler()),
|
||||
("clf", RandomForestClassifier(n_estimators=350, max_depth=8, min_samples_leaf=8, class_weight="balanced_subsample", random_state=42, n_jobs=-1)),
|
||||
])
|
||||
model.fit(x.values.astype(np.float32), y.values)
|
||||
pred = model.predict(x.values.astype(np.float32))
|
||||
return {"model": model, "features": list(x.columns), "symbol": SYMBOL}, {"training_bars": int(len(x)), "feature_count": int(x.shape[1]), "buy_signals": int((pred == 2).sum()), "sell_signals": int((pred == 0).sum()), "hold_signals": int((pred == 1).sum())}
|
||||
|
||||
|
||||
def predict(model, market_data, config):
|
||||
p = config.get("parameters", {})
|
||||
candles = market_data.get("candles", [])
|
||||
if len(candles) < int(p.get("lookback", 140)):
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "not_enough_candles", "model": MODEL_NAME}}
|
||||
df = _prep(pd.DataFrame(candles, columns=["open", "high", "low", "close", "volume"]))
|
||||
row = _features(df).tail(1)
|
||||
if row.empty:
|
||||
return {"signal": "HOLD", "confidence": 0.0, "metadata": {"reason": "no_features", "model": MODEL_NAME}}
|
||||
prob = model["model"].predict_proba(row[model["features"]].values.astype(np.float32))[0]
|
||||
klass = int(np.argmax(prob))
|
||||
conf = float(np.max(prob))
|
||||
signal = {0: "DOWN", 1: "HOLD", 2: "UP"}[klass]
|
||||
if conf < float(p.get("min_confidence", 0.5)):
|
||||
signal = "HOLD"
|
||||
return {"signal": signal, "confidence": round(conf, 4), "metadata": {"p_sell": round(float(prob[0]), 4), "p_hold": round(float(prob[1]), 4), "p_buy": round(float(prob[2]), 4), "model": MODEL_NAME, "symbol": SYMBOL}}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user