simple-RL-ONNX / RL_ONNX_EA.mq5
algorembrant's picture
Upload 7 files
13c3b17 verified
//+------------------------------------------------------------------+
//| RL_ONNX_EA.mq5 |
//| Copyright 2026, Algorembrant |
//| Rembrant Oyangoren Albeos |
//+------------------------------------------------------------------+
#property copyright "Algorembrant, Rembrant Oyangoren Albeos"
#property link "https://github.com/Algorembrant"
#property version "1.00"
#include <Trade\Trade.mqh>
CTrade trade;
// Inputs compatible with the RL training setup
input string ONNX_Filename = "RL_Agent_XAUUSD.onnx";
input double RiskPercent = 2.0;
long model_handle = INVALID_HANDLE;
double max_lot_size = 20.0;
int OnInit() {
Print("Initializing RL XAUUSDc ONNX EA...");
// Load the ONNX model trained in Google Colab
model_handle = OnnxCreate(ONNX_Filename, ONNX_DEFAULT);
if(model_handle == INVALID_HANDLE) {
Print("Error loading ONNX model ", ONNX_Filename, " : ", GetLastError());
return INIT_FAILED;
}
trade.SetExpertMagicNumber(2026101); // 2026 methodologies
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason) {
if(model_handle != INVALID_HANDLE)
OnnxRelease(model_handle);
Print("EA Deinitialized.");
}
void OnTick() {
// Strictly execute on close-price only (Wait for new bar generation)
static datetime last_time = 0;
datetime current_time = iTime(_Symbol, _Period, 0);
// If the bar hasn't closed / new bar hasn't opened, do nothing
if(current_time == last_time) return;
last_time = current_time;
// --- 1. Fetch data & Indicators
// The ONNX model requires the exact 100+ vectorized attributes as built by pandas_ta in python.
// In this production script, we construct the input float array shape based on observation_space.
// Ensure length matches `features.shape[1]` exactly.
float features[];
int num_features = 100; // MUST MATCH exactly the CSV features
ArrayResize(features, num_features);
for(int i=0; i<ArraySize(features); i++) {
// [In a complete system, map MT5 runtime M3 moving averages, FDI, oscillators to these indices here]
features[i] = 0.0f;
}
// --- 2. Predict with ONNX Model
long action_output[] = {3}; // Default 3 = Do Nothing
// onnx model inference
if(!OnnxRun(model_handle, ONNX_NO_CONVERSION, features, action_output)) {
Print("ONNX Run Error: ", GetLastError());
return;
}
long action = action_output[0];
// --- 3. Execute Actions & Handle Fragment/Sizing
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * point;
double closePrice = iClose(_Symbol, _Period, 1);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// System rule: After opened, wait until tookprofit or stopout. No averaging.
if(PositionsTotal() > 0) return;
// StopLoss distance constraints: SL distance never less than spread * 10
double sl_dist = MathMax(closePrice * 0.005, spread * 10.0);
// Calculate Sizing (2% Risk)
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double risk_amount = balance * (RiskPercent / 100.0);
// MT5 Standard contract size computation for Gold (usually $100 per lot per $1)
double sl_dollar_risk_per_lot = sl_dist * 100.0;
double lots = 0.01;
if(sl_dollar_risk_per_lot > 0)
lots = risk_amount / sl_dollar_risk_per_lot;
// Fragmentize execution cap
lots = MathRound(lots * 100.0) / 100.0;
if(lots < 0.01) lots = 0.01;
// Fragmenting Logic: Open multiple positions if lot size exceeds cap
int fragments = 1;
double current_fragment_lot = lots;
if(lots > max_lot_size) {
fragments = (int)MathCeil(lots / max_lot_size);
current_fragment_lot = max_lot_size;
Print("Notice: Position fragmentization triggered. Total Lot = ", lots, " -> Fragmented into ", fragments, " orders.");
}
if(action == 0) { // BUY
double stoploss = ask - sl_dist;
double takeprofit = ask + (sl_dist * 1.5); // TP >= 1R
for(int f=0; f<fragments; f++) {
if(f == fragments - 1 && lots > max_lot_size) {
// Remaining fraction stringency
current_fragment_lot = lots - (max_lot_size * (fragments - 1));
}
trade.Buy(current_fragment_lot, _Symbol, ask, stoploss, takeprofit, "RL_BUY");
}
}
else if(action == 1) { // SELL
double stoploss = bid + sl_dist;
double takeprofit = bid - (sl_dist * 1.5); // TP >= 1R
for(int f=0; f<fragments; f++) {
if(f == fragments - 1 && lots > max_lot_size) {
current_fragment_lot = lots - (max_lot_size * (fragments - 1));
}
trade.Sell(current_fragment_lot, _Symbol, bid, stoploss, takeprofit, "RL_SELL");
}
}
}