Files

105 lines
3.2 KiB
Python

"""
Build a minimal ONNX ensemble model using raw ONNX ops (no sklearn needed).
Creates a simple linear classifier + softmax as a demo.
Replace this with a real trained model later (Python 3.11/3.12 + sklearn + skl2onnx).
"""
import json
import numpy as np
import onnx
from onnx import helper, TensorProto, numpy_helper
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
MODELS_DIR = ROOT / "models"
MODELS_DIR.mkdir(exist_ok=True)
FEATURES = [
"returns_1", "returns_3", "returns_6",
"sma_10", "sma_20", "sma_50",
"macd", "macd_signal",
"rsi_14", "atr_14", "atr_pct",
"vol_ratio", "high_low_range", "dist_sma20",
]
# Dummy weights for demo — replace with real trained weights
rng = np.random.default_rng(123)
W = rng.standard_normal((14, 3)).astype(np.float32) * 0.1
b = np.zeros(3, dtype=np.float32)
# Normalize weights roughly
W = W / np.maximum(np.abs(W).sum(axis=0, keepdims=True), 1e-6)
scale = np.array([
1000.0, 1000.0, 1000.0,
1.0, 1.0, 1.0,
10.0, 10.0,
1.0, 10.0, 1.0,
1.0, 1.0, 1.0,
], dtype=np.float32).reshape(1, 14)
bias = np.zeros((1, 14), dtype=np.float32)
def build_model():
# Input
x = helper.make_tensor_value_info("float_input", TensorProto.FLOAT, [None, 14])
# Scale nodes: x_scaled = x * scale + bias
scale_tensor = numpy_helper.from_array(scale, name="scale")
bias_tensor = numpy_helper.from_array(bias, name="bias")
mul_node = helper.make_node("Mul", ["float_input", "scale"], ["x_scaled"])
add_node = helper.make_node("Add", ["x_scaled", "bias"], ["x_scaled_centered"])
# Linear layer: logits = x_scaled @ W + b
W_tensor = numpy_helper.from_array(W, name="W")
b_tensor = numpy_helper.from_array(b, name="b")
matmul_node = helper.make_node("MatMul", ["x_scaled_centered", "W"], ["logits"])
add_bias_node = helper.make_node("Add", ["logits", "b"], ["logits_biased"])
# Softmax
softmax_node = helper.make_node("Softmax", ["logits_biased"], ["probs"], axis=1)
# Output
y = helper.make_tensor_value_info("probs", TensorProto.FLOAT, [None, 3])
graph = helper.make_graph(
[mul_node, add_node, matmul_node, add_bias_node, softmax_node],
"mad_turtle_ensemble",
[x],
[y],
[scale_tensor, bias_tensor, W_tensor, b_tensor],
)
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 15)])
model.ir_version = 8
onnx.checker.check_model(model)
out = MODELS_DIR / "xauusd_h1_ensemble.onnx"
onnx.save(model, str(out))
print(f"Saved demo ONNX model -> {out}")
def save_metadata():
meta = {
"symbol": "XAUUSD",
"timeframe": "H1",
"features": FEATURES,
"target_horizon": 3,
"built_at": __import__('datetime').datetime.utcnow().isoformat(),
"models": {
"ensemble": {
"features": FEATURES,
"path": "models/xauusd_h1_ensemble.onnx",
"note": "Demo model with random weights. Replace with real trained model.",
}
},
}
out = MODELS_DIR / "metadata.json"
with open(out, "w") as f:
json.dump(meta, f, indent=2)
print(f"Saved metadata -> {out}")
if __name__ == "__main__":
build_model()
save_metadata()