Update
This commit is contained in:
+231
-62
@@ -11,9 +11,14 @@
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
//--- Resource: Embed ONNX model in EA
|
||||
// Based on: https://www.mql5.com/en/docs/onnx/onnx_test
|
||||
// Path is relative to MQL5 directory (not starting with \\Files\\)
|
||||
#resource "Files\\XAUUSD_H1_model.onnx" as uchar ExtModel[]
|
||||
|
||||
//--- Input parameters
|
||||
input group "ONNX Model Settings"
|
||||
input string InpModelPath = "models\\XAUUSD_H1_model.onnx"; // ONNX Model Path
|
||||
input string InpModelPath = ""; // ONNX Model Path (leave empty to use embedded resource)
|
||||
input int InpLookback = 60; // Lookback Period (bars)
|
||||
input bool InpUsePrediction = true; // Use Model Prediction
|
||||
|
||||
@@ -21,8 +26,13 @@ input group "Trading Settings"
|
||||
input double InpLotSize = 0.01; // Lot Size
|
||||
input int InpMagicNumber = 123456; // Magic Number
|
||||
input int InpSlippage = 3; // Slippage (points)
|
||||
input int InpStopLoss = 50; // Stop Loss (pips)
|
||||
input int InpTakeProfit = 100; // Take Profit (pips)
|
||||
input bool InpUsePredictedSLTP = true; // Use Predicted SL/TP (based on prediction & volatility)
|
||||
input int InpStopLoss = 50; // Stop Loss (pips) - used if InpUsePredictedSLTP=false
|
||||
input int InpTakeProfit = 100; // Take Profit (pips) - used if InpUsePredictedSLTP=false
|
||||
input double InpSLMultiplier = 1.5; // SL Multiplier (ATR-based, e.g., 1.5 = 1.5x ATR)
|
||||
input double InpTPMultiplier = 2.0; // TP Multiplier (ATR-based, e.g., 2.0 = 2x ATR)
|
||||
input double InpMinSLATR = 0.5; // Minimum SL (ATR multiplier)
|
||||
input double InpMinTPATR = 1.0; // Minimum TP (ATR multiplier)
|
||||
|
||||
input group "Prediction Settings"
|
||||
input double InpPredictionThreshold = 0.00005; // Min Prediction Change (0.005% as decimal, e.g., 0.00005 = 0.005%)
|
||||
@@ -47,33 +57,40 @@ int OnInit()
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
// Load ONNX model
|
||||
string model_path = InpModelPath;
|
||||
// Based on: https://www.mql5.com/en/docs/onnx/onnx_test
|
||||
Print("Loading ONNX model from embedded resource...");
|
||||
|
||||
// Convert relative path to full path
|
||||
if(StringFind(model_path, "\\") == 0 || StringFind(model_path, "/") == 0)
|
||||
{
|
||||
// Already absolute path
|
||||
}
|
||||
else
|
||||
{
|
||||
// Relative path - prepend terminal data folder
|
||||
model_path = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\" + model_path;
|
||||
}
|
||||
|
||||
// Replace forward slashes with backslashes for Windows
|
||||
StringReplace(model_path, "/", "\\");
|
||||
|
||||
Print("Loading ONNX model from: ", model_path);
|
||||
|
||||
onnx_handle = OnnxCreate(model_path, ONNX_DEFAULT);
|
||||
// Create model from resource buffer
|
||||
onnx_handle = OnnxCreateFromBuffer(ExtModel, ONNX_DEBUG_LOGS);
|
||||
|
||||
if(onnx_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("ERROR: Failed to load ONNX model. Error: ", GetLastError());
|
||||
Print("Make sure the model file exists at: ", model_path);
|
||||
int error = GetLastError();
|
||||
Print("ERROR: Failed to create ONNX model from resource. Error: ", error);
|
||||
Print("Make sure the model file exists at: MQL5\\Files\\XAUUSD_H1_model.onnx");
|
||||
Print("Then recompile the EA to embed it as a resource.");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Set input shape - per MQL5 documentation
|
||||
const long ExtInputShape[] = {1, InpLookback, 13}; // batch=1, lookback bars, 13 features
|
||||
if(!OnnxSetInputShape(onnx_handle, 0, ExtInputShape))
|
||||
{
|
||||
Print("OnnxSetInputShape failed, error ", GetLastError());
|
||||
OnnxRelease(onnx_handle);
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Set output shape - per MQL5 documentation
|
||||
const long ExtOutputShape[] = {1, 1}; // batch=1, single output value
|
||||
if(!OnnxSetOutputShape(onnx_handle, 0, ExtOutputShape))
|
||||
{
|
||||
Print("OnnxSetOutputShape failed, error ", GetLastError());
|
||||
OnnxRelease(onnx_handle);
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
|
||||
// Get model info
|
||||
long input_count = OnnxGetInputCount(onnx_handle);
|
||||
long output_count = OnnxGetOutputCount(onnx_handle);
|
||||
@@ -137,23 +154,59 @@ void OnTick()
|
||||
return;
|
||||
}
|
||||
|
||||
// Run ONNX model
|
||||
float output_data[];
|
||||
if(!RunONNXModel(input_data, output_data))
|
||||
// Check if input data is valid
|
||||
if(ArraySize(input_data) != InpLookback * 13)
|
||||
{
|
||||
Print("ERROR: Input data size mismatch. Expected: ", InpLookback * 13, ", Got: ", ArraySize(input_data));
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert flat array to matrixf for OnnxRun
|
||||
// Shape: [lookback, features] = [60, 13] - batch dimension is added automatically
|
||||
matrixf input_matrix;
|
||||
input_matrix.Resize(InpLookback, 13);
|
||||
|
||||
// Fill matrix from flat array
|
||||
int idx = 0;
|
||||
for(int i = 0; i < InpLookback; i++)
|
||||
{
|
||||
for(int j = 0; j < 13; j++)
|
||||
{
|
||||
if(idx >= ArraySize(input_data))
|
||||
{
|
||||
Print("ERROR: Index out of bounds when filling matrix. idx=", idx, ", array size=", ArraySize(input_data));
|
||||
return;
|
||||
}
|
||||
input_matrix[i][j] = input_data[idx++];
|
||||
}
|
||||
}
|
||||
|
||||
// Verify matrix is not empty
|
||||
if(input_matrix.Rows() == 0 || input_matrix.Cols() == 0)
|
||||
{
|
||||
Print("ERROR: Input matrix is empty. Rows: ", input_matrix.Rows(), ", Cols: ", input_matrix.Cols());
|
||||
return;
|
||||
}
|
||||
|
||||
Print("Input matrix prepared: Rows=", input_matrix.Rows(), ", Cols=", input_matrix.Cols());
|
||||
|
||||
// Run ONNX model - use matrixf and vectorf per MQL5 documentation
|
||||
vectorf output_vector(1);
|
||||
if(!RunONNXModel(input_matrix, output_vector))
|
||||
{
|
||||
Print("ERROR: Failed to run ONNX model");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get prediction
|
||||
if(ArraySize(output_data) == 0)
|
||||
if(output_vector.Size() == 0)
|
||||
{
|
||||
Print("ERROR: Empty output from ONNX model");
|
||||
return;
|
||||
}
|
||||
|
||||
// Model now predicts price change percentage directly (e.g., -0.003 = -0.3%)
|
||||
double predicted_change_pct = output_data[0];
|
||||
double predicted_change_pct = output_vector[0];
|
||||
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
// Check if prediction is percentage (between -1 and 1) or absolute price (old format)
|
||||
@@ -190,20 +243,85 @@ void OnTick()
|
||||
last_prediction = predicted_price;
|
||||
last_confidence = confidence;
|
||||
|
||||
// Calculate ATR for dynamic SL/TP
|
||||
double atr_value = 0.0;
|
||||
double atr_array[];
|
||||
ArraySetAsSeries(atr_array, true);
|
||||
int atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14);
|
||||
if(atr_handle != INVALID_HANDLE)
|
||||
{
|
||||
if(CopyBuffer(atr_handle, 0, 0, 1, atr_array) > 0)
|
||||
{
|
||||
atr_value = atr_array[0];
|
||||
}
|
||||
IndicatorRelease(atr_handle);
|
||||
}
|
||||
|
||||
// Calculate predicted SL/TP based on prediction, confidence, and volatility
|
||||
double predicted_sl = 0.0;
|
||||
double predicted_tp = 0.0;
|
||||
if(InpUsePredictedSLTP && atr_value > 0)
|
||||
{
|
||||
// Calculate SL/TP based on ATR, prediction, and confidence
|
||||
double predicted_move = MathAbs(predicted_price - current_price);
|
||||
|
||||
// SL: Based on ATR and confidence
|
||||
// Higher confidence = tighter SL, lower confidence = wider SL
|
||||
double sl_atr_mult = InpSLMultiplier / MathMax(confidence, 0.1);
|
||||
sl_atr_mult = MathMax(sl_atr_mult, InpMinSLATR);
|
||||
predicted_sl = atr_value * sl_atr_mult;
|
||||
|
||||
// TP: Use a fraction of predicted move (not the full move)
|
||||
// Take 30-50% of predicted move as TP, but ensure minimum
|
||||
double tp_fraction = 0.3 + (confidence * 0.2); // 30-50% based on confidence
|
||||
double tp_from_prediction = predicted_move * tp_fraction;
|
||||
|
||||
// Also calculate TP from ATR multiplier
|
||||
double tp_from_atr = atr_value * InpTPMultiplier;
|
||||
|
||||
// Use the smaller of the two (more conservative)
|
||||
predicted_tp = MathMin(tp_from_prediction, tp_from_atr);
|
||||
predicted_tp = MathMax(predicted_tp, atr_value * InpMinTPATR); // Minimum TP
|
||||
|
||||
// Ensure TP is at least 1.5x SL for risk/reward
|
||||
if(predicted_tp < predicted_sl * 1.5)
|
||||
{
|
||||
predicted_tp = predicted_sl * 1.5;
|
||||
}
|
||||
|
||||
// Cap TP at maximum 80% of predicted move (don't be too greedy)
|
||||
double max_tp = predicted_move * 0.8;
|
||||
if(predicted_tp > max_tp)
|
||||
{
|
||||
predicted_tp = max_tp;
|
||||
}
|
||||
}
|
||||
|
||||
// Log prediction
|
||||
Print("Prediction: Current=", current_price,
|
||||
" Predicted Change=", price_change_pct, "%",
|
||||
" Predicted Price=", predicted_price,
|
||||
" Confidence=", confidence);
|
||||
" Confidence=", confidence,
|
||||
" ATR=", atr_value);
|
||||
if(InpUsePredictedSLTP && predicted_sl > 0 && predicted_tp > 0)
|
||||
{
|
||||
Print(" Predicted SL=", predicted_sl, " (", predicted_sl/current_price*100, "%)",
|
||||
" Predicted TP=", predicted_tp, " (", predicted_tp/current_price*100, "%)");
|
||||
}
|
||||
|
||||
// Check if we should trade
|
||||
if(!InpUseConfidence || confidence >= InpMinConfidence)
|
||||
{
|
||||
// Check if prediction is significant
|
||||
// price_change_pct is now in percentage (e.g., 0.1 = 0.1%), so compare with threshold * 100
|
||||
// OR: if model outputs decimal (0.001), compare directly
|
||||
double abs_change_decimal = MathAbs(predicted_change_pct); // Use raw prediction for threshold check
|
||||
if(abs_change_decimal >= InpPredictionThreshold)
|
||||
// price_change_pct is in percentage (e.g., 5.72 = 5.72%)
|
||||
// InpPredictionThreshold is in decimal (e.g., 0.00005 = 0.005%)
|
||||
// Convert threshold to percentage for comparison
|
||||
double threshold_pct = InpPredictionThreshold * 100.0;
|
||||
double abs_change_pct = MathAbs(price_change_pct); // Already in percentage
|
||||
|
||||
Print("Trade Check: Change=", price_change_pct, "% Threshold=", threshold_pct, "% Confidence=", confidence);
|
||||
|
||||
if(abs_change_pct >= threshold_pct)
|
||||
{
|
||||
// Check existing position
|
||||
if(PositionSelect(_Symbol))
|
||||
@@ -214,17 +332,26 @@ void OnTick()
|
||||
else
|
||||
{
|
||||
// Open new position based on prediction
|
||||
// Use raw prediction (decimal format) for threshold comparison
|
||||
if(predicted_change_pct > InpPredictionThreshold)
|
||||
if(price_change_pct > threshold_pct)
|
||||
{
|
||||
OpenBuyPosition();
|
||||
Print(">>> Opening BUY position: Change=", price_change_pct, "% Threshold=", threshold_pct, "%");
|
||||
OpenBuyPosition(predicted_sl, predicted_tp);
|
||||
}
|
||||
else if(predicted_change_pct < -InpPredictionThreshold)
|
||||
else if(price_change_pct < -threshold_pct)
|
||||
{
|
||||
OpenSellPosition();
|
||||
Print(">>> Opening SELL position: Change=", price_change_pct, "% Threshold=", threshold_pct, "%");
|
||||
OpenSellPosition(predicted_sl, predicted_tp);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Prediction below threshold: Change=", price_change_pct, "% < Threshold=", threshold_pct, "%");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Confidence too low: ", confidence, " < ", InpMinConfidence);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,16 +378,36 @@ bool PrepareInputData(float &input_array[])
|
||||
ArraySetAsSeries(close, true);
|
||||
ArraySetAsSeries(volume, true);
|
||||
|
||||
if(CopyOpen(_Symbol, PERIOD_CURRENT, 0, lookback + 50, open) < lookback)
|
||||
int copied_open = CopyOpen(_Symbol, PERIOD_CURRENT, 0, lookback + 50, open);
|
||||
if(copied_open < lookback)
|
||||
{
|
||||
Print("ERROR: CopyOpen failed. Got ", copied_open, " bars, need ", lookback);
|
||||
return false;
|
||||
if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, lookback + 50, high) < lookback)
|
||||
}
|
||||
int copied_high = CopyHigh(_Symbol, PERIOD_CURRENT, 0, lookback + 50, high);
|
||||
if(copied_high < lookback)
|
||||
{
|
||||
Print("ERROR: CopyHigh failed. Got ", copied_high, " bars, need ", lookback);
|
||||
return false;
|
||||
if(CopyLow(_Symbol, PERIOD_CURRENT, 0, lookback + 50, low) < lookback)
|
||||
}
|
||||
int copied_low = CopyLow(_Symbol, PERIOD_CURRENT, 0, lookback + 50, low);
|
||||
if(copied_low < lookback)
|
||||
{
|
||||
Print("ERROR: CopyLow failed. Got ", copied_low, " bars, need ", lookback);
|
||||
return false;
|
||||
if(CopyClose(_Symbol, PERIOD_CURRENT, 0, lookback + 50, close) < lookback)
|
||||
}
|
||||
int copied_close = CopyClose(_Symbol, PERIOD_CURRENT, 0, lookback + 50, close);
|
||||
if(copied_close < lookback)
|
||||
{
|
||||
Print("ERROR: CopyClose failed. Got ", copied_close, " bars, need ", lookback);
|
||||
return false;
|
||||
if(CopyTickVolume(_Symbol, PERIOD_CURRENT, 0, lookback + 50, volume) < lookback)
|
||||
}
|
||||
int copied_volume = CopyTickVolume(_Symbol, PERIOD_CURRENT, 0, lookback + 50, volume);
|
||||
if(copied_volume < lookback)
|
||||
{
|
||||
Print("ERROR: CopyTickVolume failed. Got ", copied_volume, " bars, need ", lookback);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate indicators (simplified - you may need to match training exactly)
|
||||
double rsi[], ema20[], ema50[], atr[];
|
||||
@@ -379,21 +526,15 @@ bool PrepareInputData(float &input_array[])
|
||||
//+------------------------------------------------------------------+
|
||||
//| Run ONNX model |
|
||||
//+------------------------------------------------------------------+
|
||||
bool RunONNXModel(float &input_data[], float &output_data[])
|
||||
bool RunONNXModel(matrixf &input_matrix, vectorf &output_vector)
|
||||
{
|
||||
if(onnx_handle == INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
// Set input shape
|
||||
long input_shape[] = {1, InpLookback, 13}; // 13 features
|
||||
if(!OnnxSetInputShape(onnx_handle, 0, input_shape))
|
||||
{
|
||||
Print("ERROR: Failed to set input shape. Error: ", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Run model
|
||||
if(!OnnxRun(onnx_handle, ONNX_NO_CONVERSION, input_data, output_data))
|
||||
// Run model - shapes are already set in OnInit per MQL5 documentation
|
||||
// Based on: https://www.mql5.com/en/docs/onnx/onnx_test
|
||||
// OnnxRun expects matrixf and vectorf, not flat arrays
|
||||
if(!OnnxRun(onnx_handle, ONNX_DEBUG_LOGS | ONNX_NO_CONVERSION, input_matrix, output_vector))
|
||||
{
|
||||
Print("ERROR: Failed to run ONNX model. Error: ", GetLastError());
|
||||
return false;
|
||||
@@ -405,15 +546,29 @@ bool RunONNXModel(float &input_data[], float &output_data[])
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
void OpenBuyPosition(double predicted_sl = 0.0, double predicted_tp = 0.0)
|
||||
{
|
||||
double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
double sl = InpStopLoss > 0 ? price - InpStopLoss * _Point * 10 : 0;
|
||||
double tp = InpTakeProfit > 0 ? price + InpTakeProfit * _Point * 10 : 0;
|
||||
double sl = 0.0;
|
||||
double tp = 0.0;
|
||||
|
||||
if(InpUsePredictedSLTP && predicted_sl > 0 && predicted_tp > 0)
|
||||
{
|
||||
// Use predicted SL/TP
|
||||
sl = price - predicted_sl;
|
||||
tp = price + predicted_tp;
|
||||
Print("Using predicted SL/TP: SL=", sl, " TP=", tp);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use fixed SL/TP from input parameters
|
||||
sl = InpStopLoss > 0 ? price - InpStopLoss * _Point * 10 : 0;
|
||||
tp = InpTakeProfit > 0 ? price + InpTakeProfit * _Point * 10 : 0;
|
||||
}
|
||||
|
||||
if(trade.Buy(InpLotSize, _Symbol, price, sl, tp, "ONNX Buy Signal"))
|
||||
{
|
||||
Print("Buy order opened. Ticket: ", trade.ResultOrder());
|
||||
Print("Buy order opened. Ticket: ", trade.ResultOrder(), " Price: ", price, " SL: ", sl, " TP: ", tp);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -424,15 +579,29 @@ void OpenBuyPosition()
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
void OpenSellPosition(double predicted_sl = 0.0, double predicted_tp = 0.0)
|
||||
{
|
||||
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double sl = InpStopLoss > 0 ? price + InpStopLoss * _Point * 10 : 0;
|
||||
double tp = InpTakeProfit > 0 ? price - InpTakeProfit * _Point * 10 : 0;
|
||||
double sl = 0.0;
|
||||
double tp = 0.0;
|
||||
|
||||
if(InpUsePredictedSLTP && predicted_sl > 0 && predicted_tp > 0)
|
||||
{
|
||||
// Use predicted SL/TP
|
||||
sl = price + predicted_sl;
|
||||
tp = price - predicted_tp;
|
||||
Print("Using predicted SL/TP: SL=", sl, " TP=", tp);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use fixed SL/TP from input parameters
|
||||
sl = InpStopLoss > 0 ? price + InpStopLoss * _Point * 10 : 0;
|
||||
tp = InpTakeProfit > 0 ? price - InpTakeProfit * _Point * 10 : 0;
|
||||
}
|
||||
|
||||
if(trade.Sell(InpLotSize, _Symbol, price, sl, tp, "ONNX Sell Signal"))
|
||||
{
|
||||
Print("Sell order opened. Ticket: ", trade.ResultOrder());
|
||||
Print("Sell order opened. Ticket: ", trade.ResultOrder(), " Price: ", price, " SL: ", sl, " TP: ", tp);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+34
-5
@@ -71,16 +71,45 @@ python predict_with_onnx.py --model models/XAUUSD_H1_model.onnx --symbol XAUUSD
|
||||
|
||||
### Step 3: Use in Expert Advisor
|
||||
|
||||
#### For Strategy Tester:
|
||||
1. Copy the ONNX model to Tester Files folder:
|
||||
```
|
||||
<MT5 Data Folder>\Tester\Files\XAUUSD_H1_model.onnx
|
||||
```
|
||||
Or use the full path shown in error messages if file not found.
|
||||
|
||||
2. Compile `ONNX_EA.mq5` in MetaEditor (F7)
|
||||
|
||||
3. Open Strategy Tester (View → Strategy Tester or Ctrl+R)
|
||||
|
||||
4. Configure:
|
||||
- Expert Advisor: `ONNX_EA`
|
||||
- Symbol: `XAUUSD` (or your symbol)
|
||||
- Period: `H1` (or your timeframe)
|
||||
- Inputs:
|
||||
- `InpModelPath`: `XAUUSD_H1_model.onnx` (just filename)
|
||||
- Adjust other parameters as needed
|
||||
|
||||
5. Click Start
|
||||
|
||||
#### For Live/Demo Trading:
|
||||
1. Copy the ONNX model to MT5's Files folder:
|
||||
```
|
||||
<MT5 Data Folder>\MQL5\Files\models\XAUUSD_H1_model.onnx
|
||||
<MT5 Data Folder>\MQL5\Files\XAUUSD_H1_model.onnx
|
||||
```
|
||||
To find your Data Folder: Tools → Options → Expert Advisors → Data Folder
|
||||
|
||||
2. Compile `ONNX_EA.mq5` in MetaEditor
|
||||
2. Compile `ONNX_EA.mq5` in MetaEditor (F7)
|
||||
|
||||
3. Attach the EA to a chart with:
|
||||
- Model path: `models\XAUUSD_H1_model.onnx`
|
||||
- Your trading parameters
|
||||
3. Open a chart (e.g., XAUUSD H1)
|
||||
|
||||
4. Drag `ONNX_EA` from Navigator (Ctrl+N) onto the chart
|
||||
|
||||
5. Configure inputs:
|
||||
- `InpModelPath`: `XAUUSD_H1_model.onnx` (just filename)
|
||||
- Adjust trading parameters
|
||||
|
||||
6. Click OK and enable AutoTrading if needed
|
||||
|
||||
## Detailed Usage
|
||||
|
||||
|
||||
Reference in New Issue
Block a user