This commit is contained in:
zhutoutoutousan
2026-02-13 08:03:25 +01:00
parent 09c2f54c71
commit 98a87a69ca
134 changed files with 20003 additions and 253 deletions
+53
View File
@@ -0,0 +1,53 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Models and Data
models/
*.onnx
*.h5
*.pb
*.pkl
*.csv
*.npy
# TensorFlow
*.ckpt
checkpoints/
logs/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Jupyter
.ipynb_checkpoints/
*.ipynb
+121
View File
@@ -0,0 +1,121 @@
# MetaTrader 5 ONNX EA Setup Guide
## Quick Start
1. **Copy Model Files to MT5**
- Copy `models/XAUUSD_H1_model.onnx` to: `MT5_Data_Folder/MQL5/Files/models/`
- The EA will look for the model at: `models\XAUUSD_H1_model.onnx`
2. **Compile the EA**
- Open `ai/ONNX_EA.mq5` in MetaEditor
- Press F7 to compile
- Check for any errors
3. **Attach to Chart**
- Open XAUUSD H1 chart in MT5
- Drag `ONNX_EA.ex5` from Navigator to chart
- Configure parameters (see below)
## Model Information
- **Model Type**: LSTM Neural Network
- **Input**: 60 bars × 13 features
- **Output**: Price change percentage (e.g., -0.003 = -0.3% decrease)
- **Features**: OHLC, volume, RSI, EMA20, EMA50, ATR, price_change, high_low_ratio, volume_ma, volume_ratio
## EA Parameters
### ONNX Model Settings
- **InpModelPath**: `models\\XAUUSD_H1_model.onnx` (path relative to MQL5/Files/)
- **InpLookback**: `60` (must match training)
- **InpUsePrediction**: `true` (enable/disable predictions)
### Trading Settings
- **InpLotSize**: `0.01` (start small for testing)
- **InpMagicNumber**: `123456` (unique identifier)
- **InpSlippage**: `3` (points)
- **InpStopLoss**: `50` (pips)
- **InpTakeProfit**: `100` (pips)
### Prediction Settings
- **InpPredictionThreshold**: `0.00005` (0.005% as decimal, minimum change to trade)
- **InpUseConfidence**: `true` (enable confidence filter)
- **InpMinConfidence**: `0.1` (10% minimum confidence)
## Important Notes
### Feature Normalization
⚠️ **The EA uses simplified normalization that may not exactly match training.**
For best results:
1. The training script saves a scaler (`XAUUSD_H1_scaler.pkl`)
2. You should implement the same MinMaxScaler logic in MQL5
3. Or export scaler parameters (min/max) from Python and use in MQL5
Current implementation uses:
- OHLC: Raw values (should be normalized by scaler)
- Volume: Divided by 1,000,000
- RSI: Divided by 100
- EMAs/ATR: Normalized differences
- Price change: Percentage
- Volume MA: Divided by 1,000,000
### Prediction Format
The new model predicts **price change percentage** directly:
- Example: `-0.003` = price will decrease by 0.3%
- Old format (absolute price) is also supported for backward compatibility
### Testing Recommendations
1. **Start with Strategy Tester**
- Use Visual Mode to see predictions
- Check Expert tab for prediction logs
- Verify predictions make sense
2. **Monitor Logs**
- Check "Experts" tab for prediction values
- Verify confidence calculations
- Watch for any errors
3. **Adjust Parameters**
- If too many trades: Increase `InpPredictionThreshold` or `InpMinConfidence`
- If no trades: Decrease thresholds
- Adjust stop loss/take profit based on volatility
## Troubleshooting
### "Failed to load ONNX model"
- Check model path is correct
- Ensure model file exists in `MQL5/Files/models/`
- Check file permissions
### "Failed to prepare input data"
- Ensure enough historical data (need 60+ bars)
- Check indicator calculations
- Verify symbol is XAUUSD
### "Empty output from ONNX model"
- Check model input shape matches (1, 60, 13)
- Verify feature preparation matches training
- Check ONNX runtime version compatibility
### Predictions seem wrong
- Feature normalization may not match training
- Implement proper MinMaxScaler from training
- Check feature order matches training (13 features in correct order)
## Model Training Info
- **Training Date**: 2026-01-06
- **Training MAE**: 0.0013 (0.13%)
- **Validation MAE**: 0.0018 (0.18%)
- **Data Period**: Last 2 years
- **Timeframe**: H1
## Next Steps
1. Test in Strategy Tester first
2. Compare predictions with Python backtest
3. Adjust parameters based on results
4. Consider implementing proper scaler normalization
5. Test on demo account before live trading
+472
View File
@@ -0,0 +1,472 @@
//+------------------------------------------------------------------+
//| ONNX_EA.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property description "Expert Advisor using ONNX model for price prediction"
#property description "Based on: https://www.mql5.com/en/docs/onnx/onnx_prepare"
#include <Trade\Trade.mqh>
//--- Input parameters
input group "ONNX Model Settings"
input string InpModelPath = "models\\XAUUSD_H1_model.onnx"; // ONNX Model Path
input int InpLookback = 60; // Lookback Period (bars)
input bool InpUsePrediction = true; // Use Model Prediction
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 group "Prediction Settings"
input double InpPredictionThreshold = 0.00005; // Min Prediction Change (0.005% as decimal, e.g., 0.00005 = 0.005%)
input bool InpUseConfidence = true; // Use Confidence Filter
input double InpMinConfidence = 0.1; // Minimum Confidence (0.1 = 10%)
//--- Global variables
CTrade trade;
long onnx_handle = INVALID_HANDLE;
datetime last_bar_time = 0;
double last_prediction = 0.0;
double last_confidence = 0.0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Set trade parameters
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetDeviationInPoints(InpSlippage);
trade.SetTypeFilling(ORDER_FILLING_FOK);
// Load ONNX model
string model_path = InpModelPath;
// 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);
if(onnx_handle == INVALID_HANDLE)
{
Print("ERROR: Failed to load ONNX model. Error: ", GetLastError());
Print("Make sure the model file exists at: ", model_path);
return(INIT_FAILED);
}
// Get model info
long input_count = OnnxGetInputCount(onnx_handle);
long output_count = OnnxGetOutputCount(onnx_handle);
Print("ONNX Model loaded successfully");
Print(" Inputs: ", input_count);
Print(" Outputs: ", output_count);
if(input_count > 0)
{
string input_name = OnnxGetInputName(onnx_handle, 0);
Print(" Input name: ", input_name);
}
if(output_count > 0)
{
string output_name = OnnxGetOutputName(onnx_handle, 0);
Print(" Output name: ", output_name);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Release ONNX model
if(onnx_handle != INVALID_HANDLE)
{
OnnxRelease(onnx_handle);
Print("ONNX model released");
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if new bar
datetime current_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0);
if(current_bar_time == last_bar_time)
{
return; // Still the same bar
}
last_bar_time = current_bar_time;
// Check if we should use prediction
if(!InpUsePrediction)
{
return;
}
// Prepare input data
float input_data[];
if(!PrepareInputData(input_data))
{
Print("ERROR: Failed to prepare input data");
return;
}
// Run ONNX model
float output_data[];
if(!RunONNXModel(input_data, output_data))
{
Print("ERROR: Failed to run ONNX model");
return;
}
// Get prediction
if(ArraySize(output_data) == 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 current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// Check if prediction is percentage (between -1 and 1) or absolute price (old format)
double price_change_pct;
double predicted_price;
if(MathAbs(predicted_change_pct) < 1.0)
{
// New format: percentage (e.g., -0.003 = -0.3%)
price_change_pct = predicted_change_pct * 100.0; // Convert to percentage
predicted_price = current_price * (1.0 + predicted_change_pct); // Calculate predicted price
}
else
{
// Old format: absolute price
predicted_price = predicted_change_pct;
double price_change = predicted_price - current_price;
price_change_pct = (price_change / current_price) * 100.0;
}
// Calculate confidence (for percentage predictions: 0.001 = 0.1% = 10% confidence)
double confidence;
if(MathAbs(price_change_pct) < 1.0)
{
// It's a decimal percentage (e.g., 0.001 = 0.1%)
confidence = MathMin(MathAbs(predicted_change_pct) / 0.01, 1.0); // 0.01 = 1% = 100% confidence
}
else
{
// It's already in percentage form
confidence = MathMin(MathAbs(price_change_pct) / 1.0, 1.0);
}
last_prediction = predicted_price;
last_confidence = confidence;
// Log prediction
Print("Prediction: Current=", current_price,
" Predicted Change=", price_change_pct, "%",
" Predicted Price=", predicted_price,
" Confidence=", confidence);
// 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)
{
// Check existing position
if(PositionSelect(_Symbol))
{
// Manage existing position
ManagePosition(predicted_price, price_change_pct);
}
else
{
// Open new position based on prediction
// Use raw prediction (decimal format) for threshold comparison
if(predicted_change_pct > InpPredictionThreshold)
{
OpenBuyPosition();
}
else if(predicted_change_pct < -InpPredictionThreshold)
{
OpenSellPosition();
}
}
}
}
}
//+------------------------------------------------------------------+
//| Prepare input data for ONNX model |
//+------------------------------------------------------------------+
bool PrepareInputData(float &input_array[])
{
// We need to prepare data similar to training
// This is a simplified version - you may need to adjust based on your model
int lookback = InpLookback;
int features = 13; // OHLC(4) + volume(1) + RSI(1) + EMA20(1) + EMA50(1) + ATR(1) + price_change(1) + high_low_ratio(1) + volume_ma(1) + volume_ratio(1) = 13
ArrayResize(input_array, lookback * features);
ArrayInitialize(input_array, 0.0);
// Get historical data
double open[], high[], low[], close[];
long volume[]; // CopyTickVolume requires long[] not double[]
ArraySetAsSeries(open, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
ArraySetAsSeries(volume, true);
if(CopyOpen(_Symbol, PERIOD_CURRENT, 0, lookback + 50, open) < lookback)
return false;
if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, lookback + 50, high) < lookback)
return false;
if(CopyLow(_Symbol, PERIOD_CURRENT, 0, lookback + 50, low) < lookback)
return false;
if(CopyClose(_Symbol, PERIOD_CURRENT, 0, lookback + 50, close) < lookback)
return false;
if(CopyTickVolume(_Symbol, PERIOD_CURRENT, 0, lookback + 50, volume) < lookback)
return false;
// Calculate indicators (simplified - you may need to match training exactly)
double rsi[], ema20[], ema50[], atr[];
ArraySetAsSeries(rsi, true);
ArraySetAsSeries(ema20, true);
ArraySetAsSeries(ema50, true);
ArraySetAsSeries(atr, true);
// Calculate RSI
int rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
if(rsi_handle == INVALID_HANDLE) return false;
if(CopyBuffer(rsi_handle, 0, 0, lookback + 50, rsi) < lookback)
{
IndicatorRelease(rsi_handle);
return false;
}
IndicatorRelease(rsi_handle);
// Calculate EMAs
int ema20_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE);
int ema50_handle = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
if(ema20_handle == INVALID_HANDLE || ema50_handle == INVALID_HANDLE) return false;
if(CopyBuffer(ema20_handle, 0, 0, lookback + 50, ema20) < lookback ||
CopyBuffer(ema50_handle, 0, 0, lookback + 50, ema50) < lookback)
{
IndicatorRelease(ema20_handle);
IndicatorRelease(ema50_handle);
return false;
}
IndicatorRelease(ema20_handle);
IndicatorRelease(ema50_handle);
// Calculate ATR
int atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14);
if(atr_handle == INVALID_HANDLE) return false;
if(CopyBuffer(atr_handle, 0, 0, lookback + 50, atr) < lookback)
{
IndicatorRelease(atr_handle);
return false;
}
IndicatorRelease(atr_handle);
// Calculate volume MA for normalization
double volume_ma[];
ArraySetAsSeries(volume_ma, true);
ArrayResize(volume_ma, lookback);
ArrayInitialize(volume_ma, 0.0);
// Calculate volume MA (20-period rolling average)
for(int j = 0; j < lookback; j++)
{
double sum = 0.0;
int count = 0;
for(int k = j; k < j + 20 && k < ArraySize(volume); k++)
{
sum += (double)volume[k]; // Convert long to double
count++;
}
volume_ma[j] = count > 0 ? sum / count : (double)volume[j]; // Convert long to double
}
// Prepare features - MUST match Python training exactly (13 features)
// IMPORTANT: This uses simplified normalization. For best results, implement MinMaxScaler from training.
// The scaler is saved as models/XAUUSD_H1_scaler.pkl - you may need to export scaler parameters to MQL5
int idx = 0;
for(int i = 0; i < lookback; i++)
{
// Feature 1-4: OHLC (raw values, will be normalized by scaler)
input_array[idx++] = (float)open[i];
input_array[idx++] = (float)high[i];
input_array[idx++] = (float)low[i];
input_array[idx++] = (float)close[i];
// Feature 5: Volume (normalized by 1,000,000)
input_array[idx++] = (float)((double)volume[i] / 1000000.0); // Convert long to double first
// Feature 6: RSI (normalized by 100)
input_array[idx++] = (float)(rsi[i] / 100.0);
// Feature 7: EMA20 normalized difference
input_array[idx++] = (float)((ema20[i] - close[i]) / close[i]);
// Feature 8: EMA50 normalized difference
input_array[idx++] = (float)((ema50[i] - close[i]) / close[i]);
// Feature 9: ATR normalized
input_array[idx++] = (float)(atr[i] / close[i]);
// Feature 10: Price change (percentage)
double price_change = i > 0 ? (close[i] - close[i+1]) / close[i+1] : 0.0;
input_array[idx++] = (float)price_change;
// Feature 11: High/Low ratio
input_array[idx++] = (float)(high[i] / low[i]);
// Feature 12: Volume MA (normalized by 1,000,000)
input_array[idx++] = (float)(volume_ma[i] / 1000000.0);
// Feature 13: Volume ratio
double vol_ratio = volume_ma[i] > 0 ? (double)volume[i] / volume_ma[i] : 1.0; // Convert long to double
input_array[idx++] = (float)vol_ratio;
}
// Reshape for model: (1, lookback, features)
// ONNX expects shape [1, lookback, features]
float reshaped[];
ArrayResize(reshaped, 1 * lookback * features);
ArrayCopy(reshaped, input_array);
ArrayCopy(input_array, reshaped);
return true;
}
//+------------------------------------------------------------------+
//| Run ONNX model |
//+------------------------------------------------------------------+
bool RunONNXModel(float &input_data[], float &output_data[])
{
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))
{
Print("ERROR: Failed to run ONNX model. Error: ", GetLastError());
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Open buy position |
//+------------------------------------------------------------------+
void OpenBuyPosition()
{
double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = InpStopLoss > 0 ? price - InpStopLoss * _Point * 10 : 0;
double 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());
}
else
{
Print("Failed to open buy order. Error: ", trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| Open sell position |
//+------------------------------------------------------------------+
void OpenSellPosition()
{
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double sl = InpStopLoss > 0 ? price + InpStopLoss * _Point * 10 : 0;
double 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());
}
else
{
Print("Failed to open sell order. Error: ", trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| Manage existing position |
//+------------------------------------------------------------------+
void ManagePosition(double predicted_price, double price_change_pct)
{
if(!PositionSelect(_Symbol))
return;
long position_type = PositionGetInteger(POSITION_TYPE);
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_profit = PositionGetDouble(POSITION_PROFIT);
// Simple management: close if prediction reverses
if(position_type == POSITION_TYPE_BUY && price_change_pct < -InpPredictionThreshold)
{
// Prediction turned bearish, close long
if(trade.PositionClose(_Symbol))
{
Print("Closed long position due to bearish prediction");
}
}
else if(position_type == POSITION_TYPE_SELL && price_change_pct > InpPredictionThreshold)
{
// Prediction turned bullish, close short
if(trade.PositionClose(_Symbol))
{
Print("Closed short position due to bullish prediction");
}
}
}
+197
View File
@@ -0,0 +1,197 @@
# Quick Start Guide - ONNX with MetaTrader 5
Get started with ONNX machine learning models in MT5 in 5 minutes!
## Prerequisites
1. **MetaTrader 5** installed and running
2. **Python 3.8+** installed
3. **MT5 account** (demo or live) with access to historical data
## Step 1: Install Dependencies
```bash
cd ai
pip install -r requirements.txt
```
This installs:
- TensorFlow/Keras for model training
- ONNX runtime for model inference
- MetaTrader5 Python module
- Other required libraries
## Step 2: Verify MT5 Connection
Make sure MT5 is running and you're logged in. The scripts will automatically connect to MT5.
## Step 3: Train Your First Model
Train a price prediction model for Gold (XAUUSD):
```bash
python train_onnx_model.py --symbol XAUUSD --timeframe H1 --epochs 30
```
This will:
- Download 2 years of historical data
- Train an LSTM neural network
- Save the model as `models/XAUUSD_H1_model.onnx`
**Expected time**: 5-15 minutes depending on your hardware.
## Step 4: Test the Model
Make a prediction with your trained model:
```bash
python predict_with_onnx.py --model models/XAUUSD_H1_model.onnx --symbol XAUUSD
```
You should see output like:
```
Current XAUUSD price: 2650.12345
Making 1 prediction(s)...
Predicted next price: 2652.54321
Expected change: 2.41976 (0.09%)
```
## Step 5: Use in MetaTrader 5
### Option A: Copy Model to MT5
1. Copy your ONNX model to MT5's Files folder:
```
<MT5 Data Folder>\MQL5\Files\models\XAUUSD_H1_model.onnx
```
Default locations:
- Windows: `C:\Users\<YourName>\AppData\Roaming\MetaQuotes\Terminal\<TerminalID>\MQL5\Files\`
- Or find it: MT5 → File → Open Data Folder → MQL5 → Files
2. Open `ONNX_EA.mq5` in MetaEditor
3. Compile (F7)
4. Attach to chart:
- Model path: `models\XAUUSD_H1_model.onnx`
- Lookback: `60` (must match training)
- Set your trading parameters
### Option B: Run from MetaEditor
If you have Python integration enabled in MetaEditor:
1. Open `train_onnx_model.py` in MetaEditor
2. Press F7 (Compile) to run
3. The model will be saved to the project folder
## Common Commands
### Train for Different Symbols
```bash
# EUR/USD on 15-minute charts
python train_onnx_model.py --symbol EURUSD --timeframe M15
# Bitcoin on 4-hour charts
python train_onnx_model.py --symbol BTCUSD --timeframe H4
```
### Custom Training Parameters
```bash
python train_onnx_model.py \
--symbol XAUUSD \
--timeframe H1 \
--lookback 100 \
--epochs 100 \
--batch-size 64
```
### Multiple Predictions
```bash
python predict_with_onnx.py \
--model models/XAUUSD_H1_model.onnx \
--symbol XAUUSD \
--predictions 5
```
## Troubleshooting
### "MT5 initialization failed"
- ✅ Make sure MT5 is running
- ✅ Log into your account in MT5
- ✅ Check that the symbol exists (e.g., XAUUSD, not GOLD)
### "No data available"
- ✅ Ensure you have historical data downloaded in MT5
- ✅ Check the date range (script uses last 2 years)
- ✅ Verify symbol name is correct
### "Failed to load ONNX model" in EA
- ✅ Check the file path is correct
- ✅ Ensure model is in MT5's Files folder
- ✅ Verify the model file exists and is not corrupted
### Model predictions seem wrong
- ✅ Ensure `InpLookback` in EA matches training `--lookback`
- ✅ Check that you're using the same symbol/timeframe
- ✅ Verify feature normalization matches training
## Next Steps
1. **Experiment with different models**:
- Try different lookback periods
- Adjust network architecture
- Add more features
2. **Optimize trading parameters**:
- Test different prediction thresholds
- Tune stop loss/take profit
- Adjust confidence levels
3. **Backtest thoroughly**:
- Use MT5 Strategy Tester
- Test on different time periods
- Analyze performance metrics
4. **Monitor and improve**:
- Track prediction accuracy
- Retrain models periodically
- Adjust based on market conditions
## Example Workflow
```bash
# 1. Train model
python train_onnx_model.py --symbol XAUUSD --timeframe H1 --epochs 50
# 2. Test predictions
python predict_with_onnx.py --model models/XAUUSD_H1_model.onnx --symbol XAUUSD
# 3. Copy model to MT5 Files folder
# (Manual step)
# 4. Compile and attach ONNX_EA.mq5 to chart
# 5. Monitor and adjust parameters
```
## Tips
- 🎯 Start with longer timeframes (H1, H4) for more stable predictions
- 📊 Use multiple models for different market conditions
- 🔄 Retrain models periodically (weekly/monthly)
- ⚠️ Always test on demo account first
- 📈 Monitor model performance and adjust parameters
## Need Help?
- Check the main [README.md](README.md) for detailed documentation
- Review the MQL5 ONNX documentation: https://www.mql5.com/en/docs/onnx/onnx_prepare
- Examine the code comments for implementation details
Happy trading! 🚀
+310
View File
@@ -0,0 +1,310 @@
# ONNX Models with MetaTrader 5
A complete framework for training and using ONNX machine learning models in MetaTrader 5 for algorithmic trading.
Based on the [MQL5 ONNX documentation](https://www.mql5.com/en/docs/onnx/onnx_prepare).
## Overview
This framework allows you to:
1. **Train neural network models** in Python using MetaTrader 5 historical data
2. **Export models to ONNX format** for use in MQL5
3. **Use ONNX models in Expert Advisors** for real-time trading predictions
4. **Test predictions** using Python scripts
## Features
- 🧠 **LSTM Neural Networks** for price prediction
- 📊 **Technical Indicators** as features (RSI, EMA, ATR, etc.)
- 🔄 **ONNX Export** for MQL5 integration
- 📈 **Real-time Prediction** in Expert Advisors
- 🎯 **Flexible Configuration** for different symbols and timeframes
## Installation
### 1. Install Python Dependencies
```bash
cd ai
pip install -r requirements.txt
```
### 2. Install MetaTrader 5
- Download and install [MetaTrader 5](https://www.metatrader5.com/en/download)
- Create a demo or live account
- Enable Python integration in MT5 settings:
- Tools → Options → Expert Advisors
- Check "Allow DLL imports"
- Check "Integration with Python" (if available)
### 3. Configure MetaEditor (Optional)
If you want to run Python scripts from MetaEditor:
- MetaEditor → Tools → Options → Compiler
- Set Python executable path
- Or click "Install" to download Python
## Quick Start
### Step 1: Train an ONNX Model
Train a model for price prediction:
```bash
python train_onnx_model.py --symbol XAUUSD --timeframe H1 --lookback 60 --epochs 50
```
This will:
- Fetch 2 years of historical data from MT5
- Prepare features (OHLCV + technical indicators)
- Train an LSTM neural network
- Export the model to `models/XAUUSD_H1_model.onnx`
### Step 2: Test the Model
Make predictions using the trained model:
```bash
python predict_with_onnx.py --model models/XAUUSD_H1_model.onnx --symbol XAUUSD --timeframe H1
```
### Step 3: Use in Expert Advisor
1. Copy the ONNX model to MT5's Files folder:
```
<MT5 Data Folder>\MQL5\Files\models\XAUUSD_H1_model.onnx
```
2. Compile `ONNX_EA.mq5` in MetaEditor
3. Attach the EA to a chart with:
- Model path: `models\XAUUSD_H1_model.onnx`
- Your trading parameters
## Detailed Usage
### Training Models
#### Basic Training
```bash
python train_onnx_model.py \
--symbol XAUUSD \
--timeframe H1 \
--lookback 60 \
--epochs 50 \
--batch-size 32
```
#### Advanced Options
```bash
python train_onnx_model.py \
--symbol EURUSD \
--timeframe M15 \
--lookback 100 \
--epochs 100 \
--batch-size 64 \
--output custom_models
```
**Parameters:**
- `--symbol`: Trading symbol (XAUUSD, EURUSD, BTCUSD, etc.)
- `--timeframe`: M1, M5, M15, M30, H1, H4, D1
- `--lookback`: Number of bars to use for prediction (default: 60)
- `--epochs`: Training epochs (default: 50)
- `--batch-size`: Batch size (default: 32)
- `--output`: Output directory (default: models)
### Making Predictions
#### Single Prediction
```bash
python predict_with_onnx.py \
--model models/XAUUSD_H1_model.onnx \
--symbol XAUUSD \
--timeframe H1
```
#### Multiple Predictions
```bash
python predict_with_onnx.py \
--model models/XAUUSD_H1_model.onnx \
--symbol XAUUSD \
--timeframe H1 \
--predictions 5
```
### Expert Advisor Configuration
The `ONNX_EA.mq5` Expert Advisor includes:
**ONNX Model Settings:**
- `InpModelPath`: Path to ONNX model file
- `InpLookback`: Lookback period (must match training)
- `InpUsePrediction`: Enable/disable model predictions
**Trading Settings:**
- `InpLotSize`: Position size
- `InpMagicNumber`: Magic number for trades
- `InpStopLoss`: Stop loss in pips
- `InpTakeProfit`: Take profit in pips
**Prediction Settings:**
- `InpPredictionThreshold`: Minimum prediction change to trade (0.01% = 0.0001)
- `InpUseConfidence`: Enable confidence filtering
- `InpMinConfidence`: Minimum confidence level (0.0-1.0)
## Model Architecture
The default model uses:
- **Input**: 60 bars × 12 features
- **Architecture**:
- LSTM(128) → Dropout(0.2)
- LSTM(64) → Dropout(0.2)
- LSTM(32) → Dropout(0.2)
- Dense(16, ReLU)
- Dense(1) - Price prediction
- **Features**:
- OHLC prices
- Tick volume
- RSI (14)
- EMA(20), EMA(50)
- ATR(14)
- Price changes
- High/Low ratio
- Volume ratios
## Customization
### Modify Features
Edit `train_onnx_model.py` to add/remove features:
```python
def prepare_features(self, df: pd.DataFrame) -> pd.DataFrame:
feature_df = df[['open', 'high', 'low', 'close', 'tick_volume']].copy()
# Add your custom indicators
feature_df['custom_indicator'] = your_calculation(df)
return feature_df
```
### Change Model Architecture
Modify `build_model()` in `train_onnx_model.py`:
```python
def build_model(self, input_shape: tuple) -> keras.Model:
model = keras.Sequential([
layers.LSTM(256, return_sequences=True, input_shape=input_shape),
# Add your layers here
layers.Dense(1)
])
return model
```
### Adjust Expert Advisor Logic
Edit `ONNX_EA.mq5` to customize trading logic:
- Entry conditions
- Exit conditions
- Position management
- Risk management
## File Structure
```
ai/
├── requirements.txt # Python dependencies
├── train_onnx_model.py # Model training script
├── predict_with_onnx.py # Prediction testing script
├── ONNX_EA.mq5 # MQL5 Expert Advisor
├── README.md # This file
└── models/ # Trained ONNX models (created after training)
```
## Troubleshooting
### MT5 Connection Issues
**Error**: "MT5 initialization failed"
- Ensure MetaTrader 5 is installed and running
- Log into a demo or live account
- Check that the symbol exists in MT5
### Model Loading Issues
**Error**: "Failed to load ONNX model"
- Verify the model file path is correct
- Ensure the model file is in MT5's Files folder
- Check that the model was exported correctly
### Prediction Issues
**Error**: "Failed to prepare input data"
- Ensure enough historical data is available
- Check that lookback period matches training
- Verify indicators can be calculated
### Shape Mismatch Errors
If you get shape mismatch errors:
1. Check that `InpLookback` in EA matches training `--lookback`
2. Verify feature count matches (default: 12 features)
3. Ensure input normalization matches training
## Best Practices
1. **Data Quality**: Use high-quality historical data
2. **Feature Engineering**: Experiment with different indicators
3. **Model Validation**: Always validate on out-of-sample data
4. **Risk Management**: Use stop loss and position sizing
5. **Backtesting**: Test thoroughly before live trading
6. **Monitoring**: Monitor model performance regularly
## Example Workflow
1. **Train Model**:
```bash
python train_onnx_model.py --symbol XAUUSD --timeframe H1
```
2. **Test Predictions**:
```bash
python predict_with_onnx.py --model models/XAUUSD_H1_model.onnx --symbol XAUUSD
```
3. **Backtest in MT5**:
- Use Strategy Tester with `ONNX_EA.mq5`
- Test on historical data
- Analyze results
4. **Optimize Parameters**:
- Adjust prediction threshold
- Tune confidence levels
- Optimize stop loss/take profit
5. **Deploy**:
- Start with small position sizes
- Monitor performance
- Adjust as needed
## References
- [MQL5 ONNX Documentation](https://www.mql5.com/en/docs/onnx/onnx_prepare)
- [ONNX Model Zoo](https://github.com/onnx/models)
- [MetaTrader 5 Python Module](https://pypi.org/project/MetaTrader5/)
- [TensorFlow to ONNX](https://github.com/onnx/tensorflow-onnx)
## Disclaimer
Trading involves substantial risk of loss. This framework is provided for educational purposes only. Always test thoroughly on a demo account before using with real money. Past performance does not guarantee future results.
## License
This framework is provided for educational and research purposes.
+94
View File
@@ -0,0 +1,94 @@
# XAUUSD ONNX Model Training and Backtesting Summary
## Status: ✅ Model Trained, ⚠️ Predictions Need Investigation
### Completed
1.**Model Training**: Successfully trained XAUUSD H1 ONNX model
- Model: `models/XAUUSD_H1_model.onnx`
- Scaler: `models/XAUUSD_H1_scaler.pkl`
- Training Loss: 177939.59, MAE: 349.43
- Validation Loss: 1749893.25, MAE: 1280.39
2.**Backtest Framework**: Working correctly
- Processes 2888 bars successfully
- No errors in execution
3.**Parameter Optimization Tools**: Created
- Grid search and random search support
- Can test multiple parameter combinations
### Issue Identified
⚠️ **Model Predictions Are Unrealistic**
- Model predicts prices around **2669** when current price is **3800+**
- This suggests a **-31% price change**, which is unrealistic
- All parameter combinations result in **0 trades**
### Possible Causes
1. **Model Training Issue**:
- High validation MAE (1280) suggests model may not be learning well
- Model might be predicting from wrong data range
2. **Feature Mismatch**:
- Features used in backtesting might not match training features exactly
- Normalization might be inconsistent
3. **Model Architecture**:
- LSTM might need more training or different architecture
- Current model might be underfitting
### Recommendations
#### Immediate Actions
1. **Check Model Predictions**:
```bash
python inspect_predictions.py
```
This shows actual prediction values and statistics
2. **Retrain with Better Settings**:
- Increase training epochs (try 50-100)
- Use more recent data
- Consider predicting price changes instead of absolute prices
- Add more regularization to prevent overfitting
3. **Alternative Approach**:
- Train model to predict **price change percentage** instead of absolute price
- This would be more stable and easier to interpret
#### Next Steps
1. Investigate why predictions are so far off
2. Consider retraining with:
- Price change prediction instead of absolute price
- Better feature engineering
- More training data
- Different model architecture
### Files Created
- `ai/train_onnx_model.py` - Model training script
- `ai/quick_backtest.py` - Quick backtest script
- `ai/optimize_onnx_params.py` - Parameter optimization
- `ai/debug_onnx_predictions.py` - Debug predictions
- `ai/inspect_predictions.py` - Detailed prediction inspection
- `ai/test_very_low_threshold.py` - Test with very low thresholds
- `backtesting/MT5/onnx_backtest_strategy.py` - ONNX strategy class
- `backtesting/MT5/indicator_utils.py` - Indicator calculation utilities
### Usage
```bash
# Quick backtest
cd ai
python quick_backtest.py
# Optimize parameters
python optimize_onnx_params.py 2 30
# Inspect predictions
python inspect_predictions.py
```
### Model Details
- **Symbol**: XAUUSD
- **Timeframe**: H1
- **Lookback**: 60 bars
- **Features**: 13 (OHLC + volume + RSI + EMA20 + EMA50 + ATR + price_change + high_low_ratio + volume_ma + volume_ratio)
- **Architecture**: LSTM(128) → LSTM(64) → LSTM(32) → Dense(16) → Dense(1)
+112
View File
@@ -0,0 +1,112 @@
# XAUUSD ONNX Model Training and Backtesting Guide
This guide will help you train an ONNX model for XAUUSD and backtest it.
## Step 1: Install Dependencies
Make sure you have all required packages installed:
```bash
cd ai
pip install -r requirements.txt
```
If you encounter issues, install individually:
```bash
pip install tensorflow onnx onnxruntime tf2onnx scikit-learn pandas numpy MetaTrader5
```
## Step 2: Train the Model
Train an ONNX model for XAUUSD:
```bash
python train_onnx_model.py --symbol XAUUSD --timeframe H1 --lookback 60 --epochs 30
```
**Parameters:**
- `--symbol XAUUSD`: Trading symbol (Gold)
- `--timeframe H1`: 1-hour timeframe
- `--lookback 60`: Use 60 bars for prediction
- `--epochs 30`: Training epochs (adjust based on your needs)
**Expected Output:**
- Model: `models/XAUUSD_H1_model.onnx`
- Scaler: `models/XAUUSD_H1_scaler.pkl`
**Training Time:** 5-15 minutes depending on your hardware and data availability.
## Step 3: Run Backtest
After training, backtest the model:
```bash
python run_xauusd_backtest.py
```
Or use the combined script:
```bash
python train_and_backtest_xauusd.py
```
## Step 4: Review Results
The backtest will generate:
- Performance summary in console
- Equity curve chart
- Drawdown chart
- Monthly returns chart
- Trades CSV file
All files are saved in `onnx_xauusd_backtest/` directory.
## Model Configuration
The trained model uses:
- **Input**: 60 bars × 12 features
- **Features**: OHLC, volume, RSI, EMA, ATR, price changes, ratios
- **Output**: Predicted next close price
- **Architecture**: LSTM(128) → LSTM(64) → LSTM(32) → Dense layers
## Backtest Strategy Parameters
Default backtest parameters:
- **Prediction Threshold**: 0.01% (minimum price change to trade)
- **Min Confidence**: 30%
- **Lot Size**: 0.1
- **Stop Loss**: 50 pips
- **Take Profit**: 100 pips
You can adjust these in `run_xauusd_backtest.py`.
## Troubleshooting
### "Model not found"
- Make sure you've trained the model first
- Check that the model file exists in `models/` directory
### "MT5 initialization failed"
- Ensure MetaTrader 5 is running
- Log into your account
- Check that XAUUSD symbol is available
### "Insufficient data"
- Make sure you have historical data downloaded in MT5
- Check the date range in the backtest script
- Verify symbol name is correct
## Next Steps
After successful backtesting:
1. Review performance metrics
2. Optimize prediction threshold and confidence levels
3. Adjust stop loss/take profit if needed
4. Test on demo account before live trading
5. Consider using the model in `ONNX_EA.mq5` for live trading
## Files Created
- `models/XAUUSD_H1_model.onnx`: Trained ONNX model
- `models/XAUUSD_H1_scaler.pkl`: Feature scaler for normalization
- `onnx_xauusd_backtest/`: Backtest results directory
+176
View File
@@ -0,0 +1,176 @@
"""
Debug ONNX Model Predictions
This script helps debug why the model isn't generating trades.
It shows actual predictions and checks if they meet trading criteria.
"""
import os
import sys
from datetime import datetime, timedelta
import MetaTrader5 as mt5
import numpy as np
import pandas as pd
import onnxruntime as ort
import pickle
# Add paths
current_dir = os.path.dirname(os.path.abspath(__file__))
backtest_dir = os.path.join(os.path.dirname(current_dir), 'backtesting', 'MT5')
sys.path.insert(0, backtest_dir)
from backtest_engine import BacktestEngine
from onnx_backtest_strategy import ONNXBacktestStrategy
def main():
"""Debug ONNX predictions."""
print("="*60)
print("ONNX Model Prediction Debug")
print("="*60)
# Configuration
symbol = 'XAUUSD'
timeframe = mt5.TIMEFRAME_H1
model_path = 'models/XAUUSD_H1_model.onnx'
scaler_path = 'models/XAUUSD_H1_scaler.pkl'
if not os.path.exists(model_path):
print(f"ERROR: Model not found: {model_path}")
return
# Initialize MT5
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
return
try:
# Load model and scaler
session = ort.InferenceSession(model_path)
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name
with open(scaler_path, 'rb') as f:
scaler = pickle.load(f)
# Get recent data
end_date = datetime.now()
start_date = end_date - timedelta(days=10)
rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
if rates is None or len(rates) == 0:
print("ERROR: No data available")
return
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
print(f"\nLoaded {len(df)} bars")
print(f"Date range: {df['time'].min()} to {df['time'].max()}")
# Create strategy to get features
strategy = ONNXBacktestStrategy(
symbol=symbol,
timeframe=timeframe,
model_path=model_path,
scaler_path=scaler_path,
initial_balance=10000.0,
prediction_threshold=0.0001,
min_confidence=0.3,
lot_size=0.1,
stop_loss_pips=50,
take_profit_pips=100
)
# Simulate a few bars
print("\n" + "="*60)
print("Predictions Analysis")
print("="*60)
predictions_data = []
for i in range(60, min(100, len(df))): # Start from bar 60 (need lookback)
bar = df.iloc[i]
bar_time = bar['time'] if isinstance(bar['time'], datetime) else datetime.fromtimestamp(bar['time'])
current_price = bar['close']
# Build historical buffer
bar_data = {
'time': bar_time,
'open': float(bar['open']),
'high': float(bar['high']),
'low': float(bar['low']),
'close': float(bar['close']),
'tick_volume': int(bar['tick_volume']),
'rsi': 50.0, # Simplified
'ema': current_price, # Simplified
'atr': 0.0 # Simplified
}
strategy.historical_bars.append(bar_data)
if len(strategy.historical_bars) >= strategy.lookback:
# Get prediction
features = strategy.prepare_features()
if features is not None:
input_data = features.astype(np.float32)
outputs = session.run([output_name], {input_name: input_data})
predicted_price = float(outputs[0][0][0])
# Calculate metrics
price_change = predicted_price - current_price
price_change_pct = (price_change / current_price) if current_price > 0 else 0.0
confidence = min(abs(price_change_pct) / 0.01, 1.0)
predictions_data.append({
'time': bar_time,
'current_price': current_price,
'predicted_price': predicted_price,
'price_change': price_change,
'price_change_pct': price_change_pct * 100,
'confidence': confidence,
'meets_threshold': abs(price_change_pct) >= 0.0001,
'meets_confidence': confidence >= 0.3,
'would_trade': abs(price_change_pct) >= 0.0001 and confidence >= 0.3
})
# Display results
if predictions_data:
pred_df = pd.DataFrame(predictions_data)
print(f"\nAnalyzed {len(pred_df)} predictions")
print(f"\nPrediction Statistics:")
print(f" Mean price change: {pred_df['price_change_pct'].mean():.4f}%")
print(f" Std price change: {pred_df['price_change_pct'].std():.4f}%")
print(f" Min price change: {pred_df['price_change_pct'].min():.4f}%")
print(f" Max price change: {pred_df['price_change_pct'].max():.4f}%")
print(f"\n Mean confidence: {pred_df['confidence'].mean():.4f}")
print(f" Predictions meeting threshold: {pred_df['meets_threshold'].sum()}/{len(pred_df)}")
print(f" Predictions meeting confidence: {pred_df['meets_confidence'].sum()}/{len(pred_df)}")
print(f" Predictions that would trade: {pred_df['would_trade'].sum()}/{len(pred_df)}")
print(f"\nSample predictions (first 10):")
print(pred_df[['time', 'current_price', 'predicted_price', 'price_change_pct', 'confidence', 'would_trade']].head(10).to_string(index=False))
if pred_df['would_trade'].sum() == 0:
print("\n" + "="*60)
print("RECOMMENDATIONS:")
print("="*60)
print("No trades would be generated. Try:")
print(f" 1. Lower prediction_threshold (current: 0.0001)")
print(f" Suggested: {pred_df['price_change_pct'].abs().quantile(0.1):.6f}")
print(f" 2. Lower min_confidence (current: 0.3)")
print(f" Suggested: {pred_df['confidence'].quantile(0.1):.2f}")
print(f" 3. Check if model predictions are reasonable")
else:
print("No predictions generated (need more historical data)")
except Exception as e:
print(f"\nERROR: {e}")
import traceback
traceback.print_exc()
finally:
mt5.shutdown()
if __name__ == '__main__':
main()
+219
View File
@@ -0,0 +1,219 @@
"""
Debug ONNX Strategy - Find out why no trades are generated
"""
import os
import sys
from datetime import datetime, timedelta
import MetaTrader5 as mt5
# Add paths
current_dir = os.path.dirname(os.path.abspath(__file__))
backtest_dir = os.path.join(os.path.dirname(current_dir), 'backtesting', 'MT5')
sys.path.insert(0, backtest_dir)
from backtest_engine import BacktestEngine
from onnx_backtest_strategy import ONNXBacktestStrategy
def main():
"""Debug strategy to find why no trades."""
print("="*60)
print("Debugging ONNX Strategy - Why No Trades?")
print("="*60)
symbol = 'XAUUSD'
timeframe = mt5.TIMEFRAME_H1
model_path = 'models/XAUUSD_H1_model.onnx'
scaler_path = 'models/XAUUSD_H1_scaler.pkl'
initial_balance = 10000.0
if not os.path.exists(model_path):
print(f"ERROR: Model not found: {model_path}")
return
end_date = datetime.now()
start_date = end_date - timedelta(days=30) # Shorter period for debugging
print(f"\nModel: {model_path}")
print(f"Date Range: {start_date.date()} to {end_date.date()}")
print(f"Parameters:")
print(f" Prediction Threshold: 0.00005 (0.005%)")
print(f" Min Confidence: 0.1 (10%)")
print("\n")
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
return
try:
# Create strategy with debug enabled
strategy = ONNXBacktestStrategy(
symbol=symbol,
timeframe=timeframe,
model_path=model_path,
scaler_path=scaler_path,
initial_balance=initial_balance,
prediction_threshold=0.00005,
min_confidence=0.1,
lot_size=0.1,
stop_loss_pips=50,
take_profit_pips=100
)
# Override on_bar to add detailed debugging
original_on_bar = strategy.on_bar
def debug_on_bar(bar_data):
"""Debug version of on_bar."""
# Add current bar to historical buffer
strategy.historical_bars.append(bar_data.copy())
# Keep only necessary history
if len(strategy.historical_bars) > strategy.lookback + 50:
strategy.historical_bars = strategy.historical_bars[-(strategy.lookback + 50):]
# Check if we have enough data
if len(strategy.historical_bars) < strategy.lookback:
if len(strategy.historical_bars) % 20 == 0:
print(f" [Bar {len(strategy.historical_bars)}] Not enough data yet (need {strategy.lookback})")
return
current_price = bar_data['close']
# Check existing position
if strategy.position is not None:
strategy.check_stop_loss_take_profit(current_price)
return
# Make prediction
try:
predicted_change_pct = strategy.predict_price()
if predicted_change_pct is None:
if len(strategy.historical_bars) % 10 == 0:
print(f" [Bar {len(strategy.historical_bars)}] Prediction returned None - checking why...")
# Try to debug why prediction is None
features = strategy.prepare_features()
if features is None:
print(f" -> Features preparation returned None")
else:
print(f" -> Features shape: {features.shape}")
return
except Exception as e:
print(f" [Bar {len(strategy.historical_bars)}] Prediction exception: {e}")
import traceback
traceback.print_exc()
return
# Process prediction
if abs(predicted_change_pct) < 1.0:
price_change_pct = predicted_change_pct
else:
predicted_price = predicted_change_pct
if predicted_price <= 0 or predicted_price > 10000:
if len(strategy.historical_bars) % 50 == 0:
print(f" [Bar {len(strategy.historical_bars)}] Invalid prediction: {predicted_price}")
return
price_change = predicted_price - current_price
price_change_pct = (price_change / current_price) if current_price > 0 else 0.0
# Calculate confidence
if abs(price_change_pct) < 1.0:
confidence = min(abs(price_change_pct) / 0.01, 1.0)
else:
confidence = min(abs(price_change_pct) / 1.0, 1.0)
# Debug output for every 10th bar
if len(strategy.historical_bars) % 10 == 0:
print(f"\n [Bar {len(strategy.historical_bars)}]")
print(f" Current Price: {current_price:.2f}")
print(f" Raw Prediction: {predicted_change_pct:.6f}")
print(f" Price Change %: {price_change_pct*100:.4f}%")
print(f" Abs Change: {abs(price_change_pct):.6f}")
print(f" Threshold: {strategy.prediction_threshold:.6f}")
print(f" Confidence: {confidence:.3f}")
print(f" Min Confidence: {strategy.min_confidence:.2f}")
print(f" Threshold Check: {abs(price_change_pct) >= strategy.prediction_threshold} (need True)")
print(f" Confidence Check: {confidence >= strategy.min_confidence} (need True)")
if abs(price_change_pct) >= strategy.prediction_threshold and confidence >= strategy.min_confidence:
print(f" -> WOULD TRADE! Direction: {'BUY' if price_change_pct > 0 else 'SELL'}")
else:
if abs(price_change_pct) < strategy.prediction_threshold:
print(f" -> BLOCKED: Abs change {abs(price_change_pct):.6f} < threshold {strategy.prediction_threshold:.6f}")
if confidence < strategy.min_confidence:
print(f" -> BLOCKED: Confidence {confidence:.3f} < min {strategy.min_confidence:.2f}")
# Check if we should trade
if confidence < strategy.min_confidence:
return
if abs(price_change_pct) < strategy.prediction_threshold:
return
# Open position based on prediction
if price_change_pct > strategy.prediction_threshold:
# Bullish prediction
sl = current_price - (strategy.stop_loss_pips / 10000) if strategy.stop_loss_pips > 0 else None
tp = current_price + (strategy.take_profit_pips / 10000) if strategy.take_profit_pips > 0 else None
print(f"\n *** ATTEMPTING BUY POSITION at bar {len(strategy.historical_bars)} ***")
print(f" Price: {current_price:.2f}, Predicted Change: {price_change_pct*100:.4f}%")
print(f" SL: {sl:.2f}, TP: {tp:.2f}, Volume: {strategy.lot_size}")
print(f" Equity: {strategy.equity:.2f}, Current Position: {strategy.position}")
# Check margin requirement manually
contract_size = 100000
margin_required = strategy.lot_size * contract_size * current_price * 0.01
print(f" Margin Required: {margin_required:.2f}, Available: {strategy.equity * 0.9:.2f}")
result = strategy.open_position('BUY', strategy.lot_size, current_price, sl, tp, 'ONNX Buy')
print(f" Open Position Result: {result}")
if result:
print(f" -> Position opened! New position: {strategy.position}")
else:
if strategy.position is not None:
print(f" -> Position NOT opened! Reason: Already have position")
else:
print(f" -> Position NOT opened! Reason: Margin insufficient or other validation failed")
elif price_change_pct < -strategy.prediction_threshold:
# Bearish prediction
sl = current_price + (strategy.stop_loss_pips / 10000) if strategy.stop_loss_pips > 0 else None
tp = current_price - (strategy.take_profit_pips / 10000) if strategy.take_profit_pips > 0 else None
print(f"\n *** ATTEMPTING SELL POSITION at bar {len(strategy.historical_bars)} ***")
print(f" Price: {current_price:.2f}, Predicted Change: {price_change_pct*100:.4f}%")
print(f" SL: {sl:.2f}, TP: {tp:.2f}, Volume: {strategy.lot_size}")
print(f" Equity: {strategy.equity:.2f}, Current Position: {strategy.position}")
# Check margin requirement manually
contract_size = 100000
margin_required = strategy.lot_size * contract_size * current_price * 0.01
print(f" Margin Required: {margin_required:.2f}, Available: {strategy.equity * 0.9:.2f}")
result = strategy.open_position('SELL', strategy.lot_size, current_price, sl, tp, 'ONNX Sell')
print(f" Open Position Result: {result}")
if result:
print(f" -> Position opened! New position: {strategy.position}")
else:
if strategy.position is not None:
print(f" -> Position NOT opened! Reason: Already have position")
else:
print(f" -> Position NOT opened! Reason: Margin insufficient or other validation failed")
strategy.on_bar = debug_on_bar
print("Running backtest with detailed debugging...\n")
engine = BacktestEngine(strategy, start_date, end_date)
results = engine.run()
print("\n" + "="*60)
print("Backtest Complete")
print("="*60)
print(f"Total Trades: {len(strategy.closed_trades)}")
print(f"Open Positions: {1 if strategy.position else 0}")
except Exception as e:
print(f"\nERROR: {e}")
import traceback
traceback.print_exc()
finally:
mt5.shutdown()
if __name__ == '__main__':
main()
+159
View File
@@ -0,0 +1,159 @@
"""
Complete Example Workflow for ONNX + MT5
This script demonstrates the complete workflow:
1. Train an ONNX model
2. Test predictions
3. Show how to use in MT5
Run this to see the full process in action.
"""
import os
import sys
from datetime import datetime
import MetaTrader5 as mt5
# Import our modules
from train_onnx_model import ONNXModelTrainer
from predict_with_onnx import ONNXPredictor
def main():
"""Complete workflow example."""
print("="*60)
print("ONNX + MetaTrader 5 - Complete Workflow Example")
print("="*60)
# Configuration
symbol = 'XAUUSD'
timeframe_str = 'H1'
lookback = 60
epochs = 20 # Reduced for quick demo
# Convert timeframe
timeframe_map = {
'M1': mt5.TIMEFRAME_M1,
'M5': mt5.TIMEFRAME_M5,
'M15': mt5.TIMEFRAME_M15,
'M30': mt5.TIMEFRAME_M30,
'H1': mt5.TIMEFRAME_H1,
'H4': mt5.TIMEFRAME_H4,
'D1': mt5.TIMEFRAME_D1
}
timeframe = timeframe_map[timeframe_str]
# Create output directory
models_dir = 'models'
os.makedirs(models_dir, exist_ok=True)
# Step 1: Train Model
print("\n" + "="*60)
print("STEP 1: Training ONNX Model")
print("="*60)
trainer = ONNXModelTrainer(
symbol=symbol,
timeframe=timeframe,
lookback=lookback
)
try:
print(f"\nTraining model for {symbol} on {timeframe_str} timeframe...")
print(f"Lookback: {lookback} bars")
print(f"Epochs: {epochs}")
print("\nThis may take several minutes...\n")
trainer.train(epochs=epochs, batch_size=32, verbose=1)
# Export model
model_name = f"{symbol}_{timeframe_str}_model.onnx"
model_path = os.path.join(models_dir, model_name)
print(f"\nExporting model to ONNX format...")
trainer.export_to_onnx(model_path)
print(f"\n✓ Model saved to: {model_path}")
except Exception as e:
print(f"\n✗ Training failed: {e}")
import traceback
traceback.print_exc()
trainer.cleanup()
return
finally:
trainer.cleanup()
# Step 2: Test Predictions
print("\n" + "="*60)
print("STEP 2: Testing Predictions")
print("="*60)
predictor = ONNXPredictor(model_path)
try:
# Get current price
symbol_info = mt5.symbol_info(symbol)
if symbol_info:
current_price = symbol_info.bid
print(f"\nCurrent {symbol} price: {current_price:.5f}")
else:
print(f"\nWarning: Could not get current price for {symbol}")
current_price = 0
# Make predictions
print(f"\nMaking predictions...")
predictions = predictor.predict_batch(symbol, timeframe, n_predictions=3)
print("\nPredictions:")
for i, pred in enumerate(predictions, 1):
if current_price > 0:
change = pred - current_price
change_pct = (change / current_price) * 100
print(f" {i}. {pred:.5f} (change: {change:+.5f}, {change_pct:+.2f}%)")
else:
print(f" {i}. {pred:.5f}")
except Exception as e:
print(f"\n✗ Prediction failed: {e}")
import traceback
traceback.print_exc()
finally:
predictor.cleanup()
# Step 3: Instructions for MT5
print("\n" + "="*60)
print("STEP 3: Using in MetaTrader 5")
print("="*60)
print(f"\nTo use this model in MetaTrader 5:")
print(f"\n1. Copy the model file to MT5's Files folder:")
print(f" {model_path}")
print(f" → <MT5 Data Folder>\\MQL5\\Files\\models\\{os.path.basename(model_path)}")
print(f"\n2. Open ONNX_EA.mq5 in MetaEditor")
print(f"\n3. Set EA parameters:")
print(f" - Model Path: models\\{os.path.basename(model_path)}")
print(f" - Lookback: {lookback}")
print(f" - Your trading parameters")
print(f"\n4. Compile and attach to chart")
print(f"\n5. Monitor performance")
print("\n" + "="*60)
print("Workflow completed!")
print("="*60 + "\n")
if __name__ == '__main__':
# Check MT5 connection first
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
print("Make sure MetaTrader 5 is running and you're logged in.")
sys.exit(1)
try:
main()
except KeyboardInterrupt:
print("\n\nInterrupted by user")
finally:
mt5.shutdown()
+212
View File
@@ -0,0 +1,212 @@
"""
Inspect ONNX Model Predictions in Detail
This script directly tests the model and shows what it's predicting.
"""
import os
import sys
from datetime import datetime, timedelta
import MetaTrader5 as mt5
import numpy as np
import pandas as pd
import onnxruntime as ort
import pickle
# Add paths
current_dir = os.path.dirname(os.path.abspath(__file__))
backtest_dir = os.path.join(os.path.dirname(current_dir), 'backtesting', 'MT5')
sys.path.insert(0, backtest_dir)
from indicator_utils import calculate_rsi, calculate_ema, calculate_atr
def main():
"""Inspect model predictions."""
print("="*60)
print("ONNX Model Prediction Inspection")
print("="*60)
model_path = 'models/XAUUSD_H1_model.onnx'
scaler_path = 'models/XAUUSD_H1_scaler.pkl'
if not os.path.exists(model_path):
print(f"ERROR: Model not found: {model_path}")
return
# Load model
session = ort.InferenceSession(model_path)
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name
input_shape = session.get_inputs()[0].shape
lookback = int(input_shape[1]) if input_shape[1] else 60
print(f"Model Input Shape: {input_shape}")
print(f"Lookback: {lookback}")
# Load scaler
with open(scaler_path, 'rb') as f:
scaler = pickle.load(f)
print(f"Scaler Feature Count: {scaler.n_features_in_}")
# Initialize MT5
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
return
try:
# Get data
symbol = 'XAUUSD'
timeframe = mt5.TIMEFRAME_H1
end_date = datetime.now()
start_date = end_date - timedelta(days=100) # Get more data
rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
if rates is None or len(rates) == 0:
print("ERROR: No data")
return
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
df.set_index('time', inplace=True)
print(f"\nLoaded {len(df)} bars")
# Calculate indicators (same as training)
df['rsi'] = calculate_rsi(df['close'], period=14)
df['ema_20'] = calculate_ema(df['close'], period=20)
df['ema_50'] = calculate_ema(df['close'], period=50)
df['atr'] = calculate_atr(df, period=14)
df['price_change'] = df['close'].pct_change()
df['high_low_ratio'] = df['high'] / df['low']
df['volume_ma'] = df['tick_volume'].rolling(window=20).mean()
df['volume_ratio'] = df['tick_volume'] / df['volume_ma']
# Drop NaN
df = df.dropna()
print(f"After indicator calculation: {len(df)} bars")
print(f"Features: {len(['open', 'high', 'low', 'close', 'tick_volume', 'rsi', 'ema_20', 'ema_50', 'atr', 'price_change', 'high_low_ratio', 'volume_ma', 'volume_ratio'])}")
# Test predictions
print("\n" + "="*60)
print("Testing Predictions")
print("="*60)
predictions = []
for i in range(lookback, min(lookback + 50, len(df))):
# Prepare features (same as training)
feature_rows = []
for j in range(i - lookback, i):
bar = df.iloc[j]
feature_row = [
bar['open'],
bar['high'],
bar['low'],
bar['close'],
bar['tick_volume'] / 1000000.0,
bar['rsi'] / 100.0,
(bar['ema_20'] - bar['close']) / bar['close'] if bar['close'] > 0 else 0.0,
(bar['ema_50'] - bar['close']) / bar['close'] if bar['close'] > 0 else 0.0,
bar['atr'] / bar['close'] if bar['close'] > 0 else 0.0,
bar['price_change'],
bar['high_low_ratio'],
bar['volume_ma'] / 1000000.0,
bar['volume_ratio']
]
feature_rows.append(feature_row)
features = np.array(feature_rows, dtype=np.float32)
# Scale
original_shape = features.shape
features_flat = features.reshape(-1, features.shape[-1])
features_scaled = scaler.transform(features_flat)
features = features_scaled.reshape(original_shape)
# Reshape for model
input_data = features.reshape(1, lookback, -1)
# Predict
outputs = session.run([output_name], {input_name: input_data})
predicted_change_pct = float(outputs[0][0][0])
current_price = df.iloc[i]['close']
# Model predicts price change percentage directly
# If it's between -1 and 1, it's already a percentage
if abs(predicted_change_pct) < 1.0:
price_change_pct = predicted_change_pct * 100 # Convert to percentage (e.g., 0.001 -> 0.1%)
predicted_price = current_price * (1 + predicted_change_pct / 100) # Calculate predicted price
price_change = predicted_price - current_price
else:
# Old format: absolute price
predicted_price = predicted_change_pct
price_change = predicted_price - current_price
price_change_pct = (price_change / current_price) * 100 if current_price > 0 else 0.0
predictions.append({
'time': df.index[i],
'current_price': current_price,
'predicted_price': predicted_price,
'price_change': price_change,
'price_change_pct': price_change_pct,
'abs_change_pct': abs(price_change_pct)
})
if predictions:
pred_df = pd.DataFrame(predictions)
print(f"\nAnalyzed {len(pred_df)} predictions:")
print(f"\nPrice Change Statistics:")
print(f" Mean: {pred_df['price_change_pct'].mean():.6f}%")
print(f" Std: {pred_df['price_change_pct'].std():.6f}%")
print(f" Min: {pred_df['price_change_pct'].min():.6f}%")
print(f" Max: {pred_df['price_change_pct'].max():.6f}%")
print(f" Median: {pred_df['price_change_pct'].median():.6f}%")
print(f"\nAbsolute Price Change Statistics:")
print(f" Mean: {pred_df['abs_change_pct'].mean():.6f}%")
print(f" Min: {pred_df['abs_change_pct'].min():.6f}%")
print(f" Max: {pred_df['abs_change_pct'].max():.6f}%")
print(f" Median: {pred_df['abs_change_pct'].median():.6f}%")
print(f"\nSample Predictions (first 10):")
print(pred_df[['time', 'current_price', 'predicted_price', 'price_change_pct']].head(10).to_string(index=False))
# Check thresholds
threshold_0001 = (pred_df['abs_change_pct'] >= 0.01).sum()
threshold_00005 = (pred_df['abs_change_pct'] >= 0.005).sum()
threshold_00001 = (pred_df['abs_change_pct'] >= 0.001).sum()
print(f"\nPredictions meeting thresholds:")
print(f" >= 0.01% (0.0001): {threshold_0001}/{len(pred_df)}")
print(f" >= 0.005% (0.00005): {threshold_00005}/{len(pred_df)}")
print(f" >= 0.001% (0.00001): {threshold_00001}/{len(pred_df)}")
if threshold_00001 == 0:
print("\n" + "="*60)
print("ISSUE DETECTED!")
print("="*60)
print("Even with 0.001% threshold, no predictions qualify.")
print("The model may be predicting prices that are too close to current prices.")
print("\nPossible solutions:")
print(" 1. Retrain model to predict price changes instead of absolute prices")
print(" 2. Use a different prediction target (e.g., next bar high/low)")
print(" 3. Adjust the model architecture")
else:
print("No predictions generated")
except Exception as e:
print(f"\nERROR: {e}")
import traceback
traceback.print_exc()
finally:
mt5.shutdown()
if __name__ == '__main__':
main()
@@ -0,0 +1,142 @@
[
{
"prediction_threshold": 0.0007803532317523569,
"min_confidence": 0.4778214378844623,
"stop_loss_pips": 126,
"take_profit_pips": 111,
"lot_size": 0.19966462104925914,
"total_return": 0.0,
"max_drawdown": 0.0,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 0,
"final_balance": 10000.0
},
{
"prediction_threshold": 0.00035423634886275126,
"min_confidence": 0.1201975341512912,
"stop_loss_pips": 94,
"take_profit_pips": 127,
"lot_size": 0.13342715278475548,
"total_return": -6.90000000000009,
"max_drawdown": 6.90000000000009,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 1,
"final_balance": -59000.0000000009
},
{
"prediction_threshold": 0.001265431347313738,
"min_confidence": 0.19890411118369217,
"stop_loss_pips": 67,
"take_profit_pips": 101,
"lot_size": 0.13129583050668675,
"total_return": -6.90000000000009,
"max_drawdown": 6.90000000000009,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 1,
"final_balance": -59000.0000000009
},
{
"prediction_threshold": 0.0012316219508229722,
"min_confidence": 0.46683539533100704,
"stop_loss_pips": 60,
"take_profit_pips": 67,
"lot_size": 0.2657758564688984,
"total_return": 0.0,
"max_drawdown": 0.0,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 0,
"final_balance": 10000.0
},
{
"prediction_threshold": 6.0768128391024685e-05,
"min_confidence": 0.41695764280467534,
"stop_loss_pips": 100,
"take_profit_pips": 202,
"lot_size": 0.2475438851328014,
"total_return": 0.0,
"max_drawdown": 0.0,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 0,
"final_balance": 10000.0
},
{
"prediction_threshold": 0.0001953737551755531,
"min_confidence": 0.49409912147023277,
"stop_loss_pips": 107,
"take_profit_pips": 168,
"lot_size": 0.0996789203835431,
"total_return": 0.0,
"max_drawdown": 0.0,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 0,
"final_balance": 10000.0
},
{
"prediction_threshold": 0.0019322478491650692,
"min_confidence": 0.3231654114590081,
"stop_loss_pips": 60,
"take_profit_pips": 196,
"lot_size": 0.2505492451885099,
"total_return": -6.90000000000009,
"max_drawdown": 6.90000000000009,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 1,
"final_balance": -59000.0000000009
},
{
"prediction_threshold": 0.0019242854474812309,
"min_confidence": 0.4300402319051681,
"stop_loss_pips": 101,
"take_profit_pips": 92,
"lot_size": 0.19668779141596204,
"total_return": 0.0,
"max_drawdown": 0.0,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 0,
"final_balance": 10000.0
},
{
"prediction_threshold": 0.0018569847882978986,
"min_confidence": 0.3772723981353894,
"stop_loss_pips": 34,
"take_profit_pips": 254,
"lot_size": 0.18020856500645593,
"total_return": -2.349999999999909,
"max_drawdown": 2.349999999999909,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 1,
"final_balance": -13499.99999999909
},
{
"prediction_threshold": 0.00011106092028833925,
"min_confidence": 0.42902814856774935,
"stop_loss_pips": 63,
"take_profit_pips": 201,
"lot_size": 0.1487875590004536,
"total_return": 0.0,
"max_drawdown": 0.0,
"sharpe_ratio": 0.0,
"profit_factor": 0.0,
"win_rate": 0.0,
"total_trades": 0,
"final_balance": 10000.0
}
]
+359
View File
@@ -0,0 +1,359 @@
"""
Parameter Optimization for ONNX Strategy
This script optimizes strategy parameters (prediction_threshold, min_confidence,
stop_loss_pips, take_profit_pips, lot_size) using grid search or random search.
"""
import os
import sys
from datetime import datetime, timedelta
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from itertools import product
import json
# Add paths
current_dir = os.path.dirname(os.path.abspath(__file__))
backtest_dir = os.path.join(os.path.dirname(current_dir), 'backtesting', 'MT5')
sys.path.insert(0, backtest_dir)
from backtest_engine import BacktestEngine
from onnx_backtest_strategy import ONNXBacktestStrategy
from performance_analyzer import PerformanceAnalyzer
class ONNXParameterOptimizer:
"""Optimize ONNX strategy parameters."""
def __init__(self, symbol: str, timeframe: int, model_path: str, scaler_path: str,
start_date: datetime, end_date: datetime, initial_balance: float = 10000.0):
"""
Initialize optimizer.
Args:
symbol: Trading symbol
timeframe: MT5 timeframe
model_path: Path to ONNX model
scaler_path: Path to scaler
start_date: Backtest start date
end_date: Backtest end date
initial_balance: Starting balance
"""
self.symbol = symbol
self.timeframe = timeframe
self.model_path = model_path
self.scaler_path = scaler_path
self.start_date = start_date
self.end_date = end_date
self.initial_balance = initial_balance
def grid_search(self, param_grid: dict, metric: str = 'sharpe_ratio') -> pd.DataFrame:
"""
Perform grid search optimization.
Args:
param_grid: Dictionary of parameter ranges
Example: {
'prediction_threshold': [0.0001, 0.0002, 0.0005],
'min_confidence': [0.2, 0.3, 0.4],
'stop_loss_pips': [30, 50, 70],
'take_profit_pips': [60, 100, 150],
'lot_size': [0.1, 0.2]
}
metric: Metric to optimize ('sharpe_ratio', 'total_return', 'max_drawdown', 'profit_factor')
Returns:
DataFrame with results sorted by metric
"""
print("="*60)
print("Grid Search Parameter Optimization")
print("="*60)
# Generate all parameter combinations
param_names = list(param_grid.keys())
param_values = list(param_grid.values())
combinations = list(product(*param_values))
total_combinations = len(combinations)
print(f"\nTotal parameter combinations: {total_combinations}")
print(f"Optimizing for: {metric}\n")
results = []
for i, combo in enumerate(combinations, 1):
params = dict(zip(param_names, combo))
print(f"[{i}/{total_combinations}] Testing: {params}")
try:
# Create strategy with these parameters
strategy = ONNXBacktestStrategy(
symbol=self.symbol,
timeframe=self.timeframe,
model_path=self.model_path,
scaler_path=self.scaler_path,
initial_balance=self.initial_balance,
**params
)
# Run backtest
engine = BacktestEngine(strategy, self.start_date, self.end_date)
backtest_results = engine.run()
# Calculate metrics
analyzer = PerformanceAnalyzer(backtest_results)
metrics = analyzer.metrics
# Store results
result = params.copy()
# Map metric names to match what we're looking for
result['total_return'] = metrics.get('total_return_pct', 0) / 100.0
result['max_drawdown'] = metrics.get('max_drawdown_pct', 0) / 100.0
result['sharpe_ratio'] = metrics.get('sharpe_ratio', 0.0) if 'sharpe_ratio' in metrics else 0.0
result['profit_factor'] = metrics.get('profit_factor', 0.0)
result['win_rate'] = metrics.get('win_rate_pct', 0) / 100.0
result['total_trades'] = metrics.get('total_trades', 0)
result['final_balance'] = metrics.get('final_balance', self.initial_balance)
results.append(result)
metric_value = result.get(metric, 0)
print(f" -> {metric}: {metric_value:.4f} | Trades: {result['total_trades']}")
except Exception as e:
print(f" X Error: {e}")
continue
# Convert to DataFrame
df_results = pd.DataFrame(results)
if len(df_results) == 0:
raise ValueError("No successful backtests!")
# Sort by metric (descending for most metrics, ascending for max_drawdown)
if metric == 'max_drawdown':
df_results = df_results.sort_values(metric, ascending=True)
else:
df_results = df_results.sort_values(metric, ascending=False)
return df_results
def random_search(self, param_ranges: dict, n_iter: int = 50,
metric: str = 'sharpe_ratio') -> pd.DataFrame:
"""
Perform random search optimization.
Args:
param_ranges: Dictionary of parameter ranges
Example: {
'prediction_threshold': (0.0001, 0.001),
'min_confidence': (0.1, 0.5),
'stop_loss_pips': (20, 100),
'take_profit_pips': (40, 200),
'lot_size': (0.1, 0.5)
}
n_iter: Number of random combinations to test
metric: Metric to optimize
Returns:
DataFrame with results sorted by metric
"""
print("="*60)
print("Random Search Parameter Optimization")
print("="*60)
print(f"\nTesting {n_iter} random parameter combinations")
print(f"Optimizing for: {metric}\n")
results = []
np.random.seed(42) # For reproducibility
for i in range(1, n_iter + 1):
# Generate random parameters
params = {}
for param_name, (min_val, max_val) in param_ranges.items():
if isinstance(min_val, int) and isinstance(max_val, int):
params[param_name] = np.random.randint(min_val, max_val + 1)
else:
params[param_name] = np.random.uniform(min_val, max_val)
print(f"[{i}/{n_iter}] Testing: {params}")
try:
# Create strategy
strategy = ONNXBacktestStrategy(
symbol=self.symbol,
timeframe=self.timeframe,
model_path=self.model_path,
scaler_path=self.scaler_path,
initial_balance=self.initial_balance,
**params
)
# Run backtest
engine = BacktestEngine(strategy, self.start_date, self.end_date)
backtest_results = engine.run()
# Calculate metrics
analyzer = PerformanceAnalyzer(backtest_results)
metrics = analyzer.metrics
# Store results
result = params.copy()
# Map metric names to match what we're looking for
result['total_return'] = metrics.get('total_return_pct', 0) / 100.0
result['max_drawdown'] = metrics.get('max_drawdown_pct', 0) / 100.0
result['sharpe_ratio'] = metrics.get('sharpe_ratio', 0.0) if 'sharpe_ratio' in metrics else 0.0
result['profit_factor'] = metrics.get('profit_factor', 0.0)
result['win_rate'] = metrics.get('win_rate_pct', 0) / 100.0
result['total_trades'] = metrics.get('total_trades', 0)
result['final_balance'] = metrics.get('final_balance', self.initial_balance)
results.append(result)
metric_value = result.get(metric, 0)
print(f" -> {metric}: {metric_value:.4f} | Trades: {result['total_trades']}")
except Exception as e:
print(f" X Error: {e}")
continue
# Convert to DataFrame
df_results = pd.DataFrame(results)
if len(df_results) == 0:
raise ValueError("No successful backtests!")
# Sort by metric
if metric == 'max_drawdown':
df_results = df_results.sort_values(metric, ascending=True)
else:
df_results = df_results.sort_values(metric, ascending=False)
return df_results
def save_results(self, df_results: pd.DataFrame, output_file: str = 'optimization_results.csv'):
"""Save optimization results to CSV."""
df_results.to_csv(output_file, index=False)
print(f"\nResults saved to: {output_file}")
# Also save top 10 as JSON
top_10 = df_results.head(10).to_dict('records')
json_file = output_file.replace('.csv', '_top10.json')
with open(json_file, 'w') as f:
json.dump(top_10, f, indent=2, default=str)
print(f"Top 10 results saved to: {json_file}")
def main():
"""Main optimization function."""
# Configuration
symbol = 'XAUUSD'
timeframe = mt5.TIMEFRAME_H1
model_path = 'models/XAUUSD_H1_model.onnx'
scaler_path = 'models/XAUUSD_H1_scaler.pkl'
initial_balance = 10000.0
# Backtest date range (use last 6 months for optimization)
end_date = datetime.now()
start_date = end_date - timedelta(days=180)
# Check if model exists
if not os.path.exists(model_path):
print(f"ERROR: Model not found: {model_path}")
print("Please train the model first using train_onnx_model.py")
return
# Initialize MT5
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
return
try:
# Create optimizer
optimizer = ONNXParameterOptimizer(
symbol=symbol,
timeframe=timeframe,
model_path=model_path,
scaler_path=scaler_path,
start_date=start_date,
end_date=end_date,
initial_balance=initial_balance
)
# Choose optimization method (use command line args or defaults)
import sys
choice = "2" # Default to random search
n_iter = 30 # Default iterations
if len(sys.argv) > 1:
choice = sys.argv[1]
if len(sys.argv) > 2:
n_iter = int(sys.argv[2])
print("\nOptimization Configuration:")
print(f"Method: {'Grid Search' if choice == '1' else 'Random Search'}")
if choice != "1":
print(f"Iterations: {n_iter}")
print()
if choice == "1":
# Grid search parameters
param_grid = {
'prediction_threshold': [0.0001, 0.0002, 0.0005, 0.001],
'min_confidence': [0.1, 0.2, 0.3, 0.4],
'stop_loss_pips': [30, 50, 70, 100],
'take_profit_pips': [60, 100, 150, 200],
'lot_size': [0.1, 0.2]
}
results = optimizer.grid_search(param_grid, metric='sharpe_ratio')
else:
# Random search parameters
param_ranges = {
'prediction_threshold': (0.00005, 0.002), # Lower threshold to get more trades
'min_confidence': (0.05, 0.5), # Lower confidence requirement
'stop_loss_pips': (20, 150),
'take_profit_pips': (40, 300),
'lot_size': (0.05, 0.3)
}
results = optimizer.random_search(param_ranges, n_iter=n_iter, metric='sharpe_ratio')
# Display top results
print("\n" + "="*60)
print("Top 10 Results")
print("="*60)
print(results.head(10).to_string(index=False))
# Save results
optimizer.save_results(results, 'onnx_optimization_results.csv')
# Display best parameters
best = results.iloc[0]
print("\n" + "="*60)
print("Best Parameters")
print("="*60)
print(f"Prediction Threshold: {best['prediction_threshold']:.6f}")
print(f"Min Confidence: {best['min_confidence']:.2f}")
print(f"Stop Loss (pips): {best['stop_loss_pips']}")
print(f"Take Profit (pips): {best['take_profit_pips']}")
print(f"Lot Size: {best['lot_size']:.2f}")
print(f"\nPerformance Metrics:")
print(f" Sharpe Ratio: {best.get('sharpe_ratio', 'N/A'):.4f}")
print(f" Total Return: {best.get('total_return', 'N/A'):.2%}")
print(f" Max Drawdown: {best.get('max_drawdown', 'N/A'):.2%}")
print(f" Profit Factor: {best.get('profit_factor', 'N/A'):.2f}")
except KeyboardInterrupt:
print("\n\nOptimization interrupted by user")
except Exception as e:
print(f"\nERROR: {e}")
import traceback
traceback.print_exc()
finally:
mt5.shutdown()
if __name__ == '__main__':
main()
+261
View File
@@ -0,0 +1,261 @@
"""
ONNX Model Prediction Script
This script loads a trained ONNX model and makes predictions using MT5 data.
Can be run from MetaEditor or directly in Python.
Usage:
python predict_with_onnx.py --model models/XAUUSD_H1_model.onnx --symbol XAUUSD
"""
import argparse
import numpy as np
import pandas as pd
import MetaTrader5 as mt5
import onnxruntime as ort
from datetime import datetime
from sklearn.preprocessing import MinMaxScaler
import pickle
import os
class ONNXPredictor:
"""
Predictor class for using ONNX models with MT5 data.
"""
def __init__(self, model_path: str, scaler_path: str = None):
"""
Initialize the predictor.
Args:
model_path: Path to ONNX model file
scaler_path: Path to saved scaler (optional, will create if not provided)
"""
self.model_path = model_path
self.scaler_path = scaler_path
# Load ONNX model
if not os.path.exists(model_path):
raise FileNotFoundError(f"ONNX model not found: {model_path}")
self.session = ort.InferenceSession(model_path)
# Get input/output info
self.input_name = self.session.get_inputs()[0].name
self.output_name = self.session.get_outputs()[0].name
self.input_shape = self.session.get_inputs()[0].shape
print(f"Loaded ONNX model: {model_path}")
print(f"Input shape: {self.input_shape}")
print(f"Input name: {self.input_name}")
print(f"Output name: {self.output_name}")
# Load or create scaler
if scaler_path and os.path.exists(scaler_path):
with open(scaler_path, 'rb') as f:
self.scaler = pickle.load(f)
print(f"Loaded scaler from: {scaler_path}")
else:
self.scaler = MinMaxScaler()
print("Using default scaler (will need to fit)")
# Initialize MT5
if not mt5.initialize():
raise RuntimeError(f"MT5 initialization failed: {mt5.last_error()}")
def prepare_features(self, df: pd.DataFrame) -> pd.DataFrame:
"""
Prepare features from raw OHLCV data (same as training).
Args:
df: Raw OHLCV data
Returns:
DataFrame with features
"""
features = ['open', 'high', 'low', 'close', 'tick_volume']
feature_df = df[features].copy()
# Add technical indicators
feature_df['rsi'] = self._calculate_rsi(df['close'], period=14)
feature_df['ema_20'] = df['close'].ewm(span=20).mean()
feature_df['ema_50'] = df['close'].ewm(span=50).mean()
feature_df['atr'] = self._calculate_atr(df, period=14)
feature_df['price_change'] = df['close'].pct_change()
feature_df['high_low_ratio'] = df['high'] / df['low']
feature_df['volume_ma'] = df['tick_volume'].rolling(window=20).mean()
feature_df['volume_ratio'] = df['tick_volume'] / feature_df['volume_ma']
feature_df = feature_df.dropna()
return feature_df
def _calculate_rsi(self, prices: pd.Series, period: int = 14) -> pd.Series:
"""Calculate RSI indicator."""
delta = prices.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
def _calculate_atr(self, df: pd.DataFrame, period: int = 14) -> pd.Series:
"""Calculate ATR indicator."""
high_low = df['high'] - df['low']
high_close = np.abs(df['high'] - df['close'].shift())
low_close = np.abs(df['low'] - df['close'].shift())
tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
atr = tr.rolling(window=period).mean()
return atr
def get_latest_data(self, symbol: str, timeframe: int, lookback: int) -> np.ndarray:
"""
Get latest data from MT5 and prepare for prediction.
Args:
symbol: Trading symbol
timeframe: MT5 timeframe
lookback: Number of bars needed
Returns:
Prepared feature array ready for model input
"""
# Fetch data
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, lookback + 50)
if rates is None or len(rates) < lookback:
raise ValueError(f"Insufficient data for {symbol}")
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
# Prepare features
feature_df = self.prepare_features(df)
# Get last lookback bars
feature_data = feature_df.values[-lookback:]
# Scale features
if hasattr(self.scaler, 'scale_'):
feature_data_scaled = self.scaler.transform(feature_data)
else:
# Fit scaler if not already fitted
print("Warning: Scaler not fitted, fitting on current data...")
feature_data_scaled = self.scaler.fit_transform(feature_data)
# Reshape for model input: (1, lookback, features)
feature_data_scaled = feature_data_scaled.reshape(1, lookback, -1)
return feature_data_scaled.astype(np.float32)
def predict(self, symbol: str, timeframe: int, lookback: int = None) -> float:
"""
Make a prediction for the next price.
Args:
symbol: Trading symbol
timeframe: MT5 timeframe
lookback: Number of bars to use (default: from model input shape)
Returns:
Predicted price
"""
if lookback is None:
lookback = self.input_shape[1] if self.input_shape[1] else 60
# Get and prepare data
input_data = self.get_latest_data(symbol, timeframe, lookback)
# Make prediction
outputs = self.session.run([self.output_name], {self.input_name: input_data})
prediction = outputs[0][0][0]
return float(prediction)
def predict_batch(self, symbol: str, timeframe: int, n_predictions: int = 5) -> list:
"""
Make multiple predictions.
Args:
symbol: Trading symbol
timeframe: MT5 timeframe
n_predictions: Number of predictions to make
Returns:
List of predictions
"""
predictions = []
for _ in range(n_predictions):
pred = self.predict(symbol, timeframe)
predictions.append(pred)
return predictions
def cleanup(self):
"""Clean up MT5 connection."""
mt5.shutdown()
def main():
"""Main function."""
parser = argparse.ArgumentParser(description='Make predictions using ONNX model')
parser.add_argument('--model', type=str, required=True,
help='Path to ONNX model file')
parser.add_argument('--symbol', type=str, default='XAUUSD',
help='Trading symbol')
parser.add_argument('--timeframe', type=str, default='H1',
choices=['M1', 'M5', 'M15', 'M30', 'H1', 'H4', 'D1'],
help='Timeframe')
parser.add_argument('--scaler', type=str, default=None,
help='Path to saved scaler (optional)')
parser.add_argument('--predictions', type=int, default=1,
help='Number of predictions to make')
args = parser.parse_args()
# Convert timeframe
timeframe_map = {
'M1': mt5.TIMEFRAME_M1,
'M5': mt5.TIMEFRAME_M5,
'M15': mt5.TIMEFRAME_M15,
'M30': mt5.TIMEFRAME_M30,
'H1': mt5.TIMEFRAME_H1,
'H4': mt5.TIMEFRAME_H4,
'D1': mt5.TIMEFRAME_D1
}
timeframe = timeframe_map[args.timeframe]
# Create predictor
predictor = ONNXPredictor(args.model, args.scaler)
try:
# Get current price
symbol_info = mt5.symbol_info(args.symbol)
current_price = symbol_info.bid if symbol_info else 0
print(f"\nCurrent {args.symbol} price: {current_price:.5f}")
print(f"Making {args.predictions} prediction(s)...\n")
# Make predictions
if args.predictions == 1:
prediction = predictor.predict(args.symbol, timeframe)
print(f"Predicted next price: {prediction:.5f}")
print(f"Expected change: {(prediction - current_price):.5f} "
f"({((prediction - current_price) / current_price * 100):.2f}%)")
else:
predictions = predictor.predict_batch(args.symbol, timeframe, args.predictions)
print("Predictions:")
for i, pred in enumerate(predictions, 1):
change = pred - current_price
change_pct = (change / current_price * 100) if current_price > 0 else 0
print(f" {i}. {pred:.5f} (change: {change:+.5f}, {change_pct:+.2f}%)")
except Exception as e:
print(f"\nError: {e}")
import traceback
traceback.print_exc()
finally:
predictor.cleanup()
if __name__ == '__main__':
main()
+120
View File
@@ -0,0 +1,120 @@
"""
Quick Backtest Script for ONNX Model
Simple script to quickly backtest the trained ONNX model with default parameters.
"""
import os
import sys
from datetime import datetime, timedelta
import MetaTrader5 as mt5
# Add paths
current_dir = os.path.dirname(os.path.abspath(__file__))
backtest_dir = os.path.join(os.path.dirname(current_dir), 'backtesting', 'MT5')
sys.path.insert(0, backtest_dir)
from backtest_engine import BacktestEngine
from onnx_backtest_strategy import ONNXBacktestStrategy
from performance_analyzer import PerformanceAnalyzer
def main():
"""Run quick backtest."""
print("="*60)
print("XAUUSD ONNX Model Quick Backtest")
print("="*60)
# Configuration
symbol = 'XAUUSD'
timeframe = mt5.TIMEFRAME_H1
model_path = 'models/XAUUSD_H1_model.onnx'
scaler_path = 'models/XAUUSD_H1_scaler.pkl'
initial_balance = 10000.0
# Check if model exists
if not os.path.exists(model_path):
print(f"\nERROR: Model not found: {model_path}")
print("Please train the model first using:")
print(" python train_onnx_model.py --symbol XAUUSD --timeframe H1")
return
# Backtest date range
end_date = datetime.now()
start_date = end_date - timedelta(days=180) # Last 6 months
print(f"\nModel: {model_path}")
print(f"Symbol: {symbol}")
print(f"Timeframe: H1")
print(f"Date Range: {start_date.date()} to {end_date.date()}")
print(f"Initial Balance: ${initial_balance:,.2f}\n")
# Adjusted parameters (more relaxed to generate trades)
print("Strategy Parameters (Adjusted for Testing):")
print(" Prediction Threshold: 0.00005 (0.005%) - LOWERED")
print(" Min Confidence: 0.1 (10%) - LOWERED")
print(" Stop Loss: 50 pips")
print(" Take Profit: 100 pips")
print(" Lot Size: 0.1\n")
# Initialize MT5
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
print("Make sure MetaTrader 5 is running and you're logged in.")
return
try:
# Create strategy with relaxed parameters
strategy = ONNXBacktestStrategy(
symbol=symbol,
timeframe=timeframe,
model_path=model_path,
scaler_path=scaler_path,
initial_balance=initial_balance,
prediction_threshold=0.00005, # Lowered from 0.0001
min_confidence=0.1, # Lowered from 0.3
lot_size=0.1,
stop_loss_pips=50,
take_profit_pips=100
)
# Run backtest
print("Running backtest...\n")
engine = BacktestEngine(strategy, start_date, end_date)
results = engine.run()
# Analyze results
print("\n" + "="*60)
print("Performance Summary")
print("="*60)
analyzer = PerformanceAnalyzer(results)
metrics = analyzer.metrics
print(f"\nTotal Return: {metrics.get('total_return_pct', 0):.2f}%")
print(f"Max Drawdown: {metrics.get('max_drawdown_pct', 0):.2f}%")
print(f"Profit Factor: {metrics.get('profit_factor', 0):.2f}")
print(f"Win Rate: {metrics.get('win_rate_pct', 0):.2f}%")
print(f"Total Trades: {metrics.get('total_trades', 0)}")
print(f"Final Balance: ${metrics.get('final_balance', initial_balance):,.2f}")
# Generate report
analyzer.generate_report('onnx_xauusd_quick_backtest')
print("\n" + "="*60)
print("Backtest Completed!")
print("="*60)
print(f"\nResults saved to: onnx_xauusd_quick_backtest/")
print("\nTo optimize parameters, run:")
print(" python optimize_onnx_params.py")
except Exception as e:
print(f"\nERROR: Backtest failed: {e}")
import traceback
traceback.print_exc()
finally:
mt5.shutdown()
if __name__ == '__main__':
main()
+17
View File
@@ -0,0 +1,17 @@
# ONNX and Machine Learning
onnx>=1.12.0
onnxruntime>=1.12.0
tensorflow>=2.10.0
tf2onnx>=1.13.0
# Data Processing
pandas>=1.3.0
numpy>=1.21.0
scikit-learn>=1.0.0
# MetaTrader 5 Integration
MetaTrader5>=5.0.45
# Visualization and Utilities
matplotlib>=3.4.0
tqdm>=4.64.0
+115
View File
@@ -0,0 +1,115 @@
"""
Retrain XAUUSD ONNX Model with Improved Settings
This script retrains the model with:
- Price change percentage prediction (instead of absolute price)
- More training epochs
- Better model architecture
- Improved data preprocessing
"""
import os
import sys
from datetime import datetime
import MetaTrader5 as mt5
# Add paths
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, current_dir)
from train_onnx_model import ONNXModelTrainer
def main():
"""Retrain XAUUSD model with improved settings."""
print("="*60)
print("Retraining XAUUSD ONNX Model (Improved)")
print("="*60)
symbol = 'XAUUSD'
timeframe_str = 'H1'
lookback = 60
epochs = 50 # More epochs for better training
# Convert timeframe
timeframe_map = {
'M1': mt5.TIMEFRAME_M1,
'M5': mt5.TIMEFRAME_M5,
'M15': mt5.TIMEFRAME_M15,
'M30': mt5.TIMEFRAME_M30,
'H1': mt5.TIMEFRAME_H1,
'H4': mt5.TIMEFRAME_H4,
'D1': mt5.TIMEFRAME_D1
}
timeframe = timeframe_map[timeframe_str]
# Create models directory
models_dir = 'models'
os.makedirs(models_dir, exist_ok=True)
print(f"\nConfiguration:")
print(f" Symbol: {symbol}")
print(f" Timeframe: {timeframe_str}")
print(f" Lookback: {lookback} bars")
print(f" Epochs: {epochs}")
print(f" Prediction: Price change percentage (improved)")
print("\nThis will take 10-20 minutes...\n")
trainer = ONNXModelTrainer(
symbol=symbol,
timeframe=timeframe,
lookback=lookback
)
try:
# Train model
trainer.train(epochs=epochs, batch_size=32, verbose=1)
# Export model
model_name = f"{symbol}_{timeframe_str}_model.onnx"
model_path = os.path.join(models_dir, model_name)
print(f"\nExporting model to ONNX format...")
trainer.export_to_onnx(model_path)
# Save scaler
scaler_name = f"{symbol}_{timeframe_str}_scaler.pkl"
scaler_path = os.path.join(models_dir, scaler_name)
import pickle
with open(scaler_path, 'wb') as f:
pickle.dump(trainer.scaler, f)
print(f"Scaler saved to: {scaler_path}")
print(f"\n{'='*60}")
print("Retraining Completed Successfully!")
print(f"{'='*60}")
print(f"\nModel: {model_path}")
print(f"Scaler: {scaler_path}")
print("\nNext steps:")
print(" 1. Run: python quick_backtest.py")
print(" 2. Or: python optimize_onnx_params.py 2 30")
except Exception as e:
print(f"\nERROR: Training failed: {e}")
import traceback
traceback.print_exc()
trainer.cleanup()
return
finally:
trainer.cleanup()
if __name__ == '__main__':
# Check MT5 connection
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
print("Make sure MetaTrader 5 is running and you're logged in.")
sys.exit(1)
try:
main()
except KeyboardInterrupt:
print("\n\nTraining interrupted by user")
finally:
mt5.shutdown()
+115
View File
@@ -0,0 +1,115 @@
"""
Backtest XAUUSD ONNX Model
This script backtests a trained ONNX model for XAUUSD.
Make sure you have trained the model first using train_onnx_model.py
"""
import sys
import os
from datetime import datetime, timedelta
import MetaTrader5 as mt5
# Add paths
current_dir = os.path.dirname(os.path.abspath(__file__))
backtest_dir = os.path.join(os.path.dirname(current_dir), 'backtesting', 'MT5')
sys.path.insert(0, backtest_dir)
from backtest_engine import BacktestEngine
from onnx_backtest_strategy import ONNXBacktestStrategy
from performance_analyzer import PerformanceAnalyzer
def main():
"""Run backtest for XAUUSD ONNX model."""
print("="*60)
print("XAUUSD ONNX Model Backtest")
print("="*60)
# Configuration
symbol = 'XAUUSD'
timeframe = mt5.TIMEFRAME_H1
model_path = 'models/XAUUSD_H1_model.onnx'
scaler_path = 'models/XAUUSD_H1_scaler.pkl'
initial_balance = 10000.0
# Check if model exists
if not os.path.exists(model_path):
print(f"\nERROR: Model not found: {model_path}")
print("Please train the model first using:")
print(" python train_onnx_model.py --symbol XAUUSD --timeframe H1")
return
if not os.path.exists(scaler_path):
print(f"\nWARNING: Scaler not found: {scaler_path}")
print("Will use default normalization (may affect accuracy)")
scaler_path = None
# Backtest date range
end_date = datetime.now()
start_date = end_date - timedelta(days=180) # Last 6 months
print(f"\nModel: {model_path}")
print(f"Symbol: {symbol}")
print(f"Timeframe: H1")
print(f"Date Range: {start_date.date()} to {end_date.date()}")
print(f"Initial Balance: ${initial_balance:,.2f}\n")
# Create strategy
try:
strategy = ONNXBacktestStrategy(
symbol=symbol,
timeframe=timeframe,
model_path=model_path,
scaler_path=scaler_path,
initial_balance=initial_balance,
prediction_threshold=0.0001, # 0.01% minimum change
min_confidence=0.3, # 30% minimum confidence
lot_size=0.1,
stop_loss_pips=50,
take_profit_pips=100
)
except Exception as e:
print(f"ERROR: Failed to create strategy: {e}")
import traceback
traceback.print_exc()
return
# Initialize MT5
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
print("Make sure MetaTrader 5 is running and you're logged in.")
return
try:
# Run backtest
print("Running backtest...\n")
engine = BacktestEngine(strategy, start_date, end_date)
results = engine.run()
# Analyze results
print("\n" + "="*60)
print("Performance Analysis")
print("="*60)
analyzer = PerformanceAnalyzer(results)
analyzer.generate_report('onnx_xauusd_backtest')
print("\n" + "="*60)
print("Backtest Completed!")
print("="*60)
print(f"\nResults saved to: onnx_xauusd_backtest/")
except Exception as e:
print(f"\nERROR: Backtest failed: {e}")
import traceback
traceback.print_exc()
finally:
mt5.shutdown()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print("\n\nInterrupted by user")
+21
View File
@@ -0,0 +1,21 @@
"""
Quick script to train XAUUSD ONNX model
Run this to train a model for XAUUSD.
After training, you can use the model for backtesting or live trading.
"""
import sys
import os
# Ensure we're in the right directory
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Train the model
print("Training XAUUSD ONNX model...")
print("This will take several minutes...\n")
os.system('python train_onnx_model.py --symbol XAUUSD --timeframe H1 --lookback 60 --epochs 30 --batch-size 32')
print("\nTraining completed! Model saved to models/XAUUSD_H1_model.onnx")
print("Scaler saved to models/XAUUSD_H1_scaler.pkl")
+103
View File
@@ -0,0 +1,103 @@
"""
Test with very low thresholds to see if we can get any trades
"""
import os
import sys
from datetime import datetime, timedelta
import MetaTrader5 as mt5
# Add paths
current_dir = os.path.dirname(os.path.abspath(__file__))
backtest_dir = os.path.join(os.path.dirname(current_dir), 'backtesting', 'MT5')
sys.path.insert(0, backtest_dir)
from backtest_engine import BacktestEngine
from onnx_backtest_strategy import ONNXBacktestStrategy
from performance_analyzer import PerformanceAnalyzer
def main():
"""Test with very low thresholds."""
print("="*60)
print("Testing with VERY LOW Thresholds")
print("="*60)
symbol = 'XAUUSD'
timeframe = mt5.TIMEFRAME_H1
model_path = 'models/XAUUSD_H1_model.onnx'
scaler_path = 'models/XAUUSD_H1_scaler.pkl'
initial_balance = 10000.0
if not os.path.exists(model_path):
print(f"ERROR: Model not found: {model_path}")
return
end_date = datetime.now()
start_date = end_date - timedelta(days=180)
print(f"\nModel: {model_path}")
print(f"Date Range: {start_date.date()} to {end_date.date()}")
print(f"\nVERY RELAXED Parameters:")
print(" Prediction Threshold: 0.00001 (0.001%)")
print(" Min Confidence: 0.05 (5%)")
print(" Stop Loss: 50 pips")
print(" Take Profit: 100 pips")
print(" Lot Size: 0.1\n")
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
return
try:
# Create strategy with VERY low thresholds
strategy = ONNXBacktestStrategy(
symbol=symbol,
timeframe=timeframe,
model_path=model_path,
scaler_path=scaler_path,
initial_balance=initial_balance,
prediction_threshold=0.00001, # Very low: 0.001%
min_confidence=0.05, # Very low: 5%
lot_size=0.1,
stop_loss_pips=50,
take_profit_pips=100
)
print("Running backtest...\n")
engine = BacktestEngine(strategy, start_date, end_date)
results = engine.run()
analyzer = PerformanceAnalyzer(results)
metrics = analyzer.metrics
print("\n" + "="*60)
print("Results")
print("="*60)
print(f"Total Trades: {metrics.get('total_trades', 0)}")
print(f"Final Balance: ${metrics.get('final_balance', initial_balance):,.2f}")
print(f"Total Return: {metrics.get('total_return_pct', 0):.2f}%")
if metrics.get('total_trades', 0) == 0:
print("\n" + "="*60)
print("STILL NO TRADES!")
print("="*60)
print("This suggests the model predictions may be:")
print(" 1. Too small in magnitude")
print(" 2. Not meeting even very low thresholds")
print(" 3. Or there's an issue with the prediction logic")
print("\nNext steps:")
print(" - Check model predictions directly")
print(" - Verify feature preparation matches training")
print(" - Consider retraining with different architecture")
except Exception as e:
print(f"\nERROR: {e}")
import traceback
traceback.print_exc()
finally:
mt5.shutdown()
if __name__ == '__main__':
main()
+164
View File
@@ -0,0 +1,164 @@
"""
Train ONNX Model for XAUUSD and Backtest
This script:
1. Trains an ONNX model for XAUUSD
2. Runs backtest using the trained model
3. Generates performance report
"""
import os
import sys
from datetime import datetime, timedelta
import MetaTrader5 as mt5
# Add paths
current_dir = os.path.dirname(os.path.abspath(__file__))
backtest_dir = os.path.join(os.path.dirname(current_dir), 'backtesting', 'MT5')
sys.path.insert(0, current_dir)
sys.path.insert(0, backtest_dir)
from train_onnx_model import ONNXModelTrainer
from backtest_engine import BacktestEngine
from onnx_backtest_strategy import ONNXBacktestStrategy
from performance_analyzer import PerformanceAnalyzer
def main():
"""Main function to train model and run backtest."""
print("="*60)
print("XAUUSD ONNX Model Training and Backtesting")
print("="*60)
# Configuration
symbol = 'XAUUSD'
timeframe_str = 'H1'
lookback = 60
epochs = 30 # Reduced for faster training
initial_balance = 10000.0
# Convert timeframe
timeframe_map = {
'M1': mt5.TIMEFRAME_M1,
'M5': mt5.TIMEFRAME_M5,
'M15': mt5.TIMEFRAME_M15,
'M30': mt5.TIMEFRAME_M30,
'H1': mt5.TIMEFRAME_H1,
'H4': mt5.TIMEFRAME_H4,
'D1': mt5.TIMEFRAME_D1
}
timeframe = timeframe_map[timeframe_str]
# Create models directory
models_dir = 'models'
os.makedirs(models_dir, exist_ok=True)
# Step 1: Train Model
print("\n" + "="*60)
print("STEP 1: Training ONNX Model")
print("="*60)
trainer = ONNXModelTrainer(
symbol=symbol,
timeframe=timeframe,
lookback=lookback
)
try:
print(f"\nTraining model for {symbol} on {timeframe_str} timeframe...")
print(f"Lookback: {lookback} bars")
print(f"Epochs: {epochs}")
print("\nThis may take several minutes...\n")
trainer.train(epochs=epochs, batch_size=32, verbose=1)
# Export model
model_name = f"{symbol}_{timeframe_str}_model.onnx"
model_path = os.path.join(models_dir, model_name)
print(f"\nExporting model to ONNX format...")
trainer.export_to_onnx(model_path)
# Save scaler
scaler_name = f"{symbol}_{timeframe_str}_scaler.pkl"
scaler_path = os.path.join(models_dir, scaler_name)
import pickle
with open(scaler_path, 'wb') as f:
pickle.dump(trainer.scaler, f)
print(f"✓ Scaler saved to: {scaler_path}")
print(f"\n✓ Model saved to: {model_path}")
except Exception as e:
print(f"\n✗ Training failed: {e}")
import traceback
traceback.print_exc()
trainer.cleanup()
return
finally:
trainer.cleanup()
# Step 2: Run Backtest
print("\n" + "="*60)
print("STEP 2: Running Backtest")
print("="*60)
# Backtest date range (last 6 months for testing)
end_date = datetime.now()
start_date = end_date - timedelta(days=180)
# Create strategy
strategy = ONNXBacktestStrategy(
symbol=symbol,
timeframe=timeframe,
model_path=model_path,
scaler_path=scaler_path,
initial_balance=initial_balance,
prediction_threshold=0.0001, # 0.01% minimum change
min_confidence=0.3, # 30% minimum confidence
lot_size=0.1,
stop_loss_pips=50,
take_profit_pips=100
)
# Run backtest
try:
print(f"\nRunning backtest from {start_date.date()} to {end_date.date()}...")
engine = BacktestEngine(strategy, start_date, end_date)
results = engine.run()
# Analyze results
print("\n" + "="*60)
print("STEP 3: Performance Analysis")
print("="*60)
analyzer = PerformanceAnalyzer(results)
analyzer.generate_report('onnx_backtest_results')
print("\n" + "="*60)
print("Training and Backtesting Completed!")
print("="*60)
print(f"\nModel: {model_path}")
print(f"Scaler: {scaler_path}")
print(f"Results: onnx_backtest_results/")
except Exception as e:
print(f"\n✗ Backtest failed: {e}")
import traceback
traceback.print_exc()
if __name__ == '__main__':
# Check MT5 connection
if not mt5.initialize():
print("ERROR: Failed to initialize MT5")
print("Make sure MetaTrader 5 is running and you're logged in.")
sys.exit(1)
try:
main()
except KeyboardInterrupt:
print("\n\nInterrupted by user")
finally:
mt5.shutdown()
+493
View File
@@ -0,0 +1,493 @@
"""
ONNX Model Training Script for MetaTrader 5
This script trains a neural network model for price prediction and exports it to ONNX format.
Based on MQL5 ONNX documentation: https://www.mql5.com/en/docs/onnx/onnx_prepare
Usage:
python train_onnx_model.py --symbol XAUUSD --timeframe H1 --lookback 60 --epochs 50
"""
import argparse
import os
import sys
from datetime import datetime, timedelta
import numpy as np
import pandas as pd
import MetaTrader5 as mt5
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
import tf2onnx
import onnx
from tqdm import tqdm
class ONNXModelTrainer:
"""
Trainer class for creating ONNX models from MT5 data.
"""
def __init__(self, symbol: str, timeframe: int, lookback: int = 60,
prediction_horizon: int = 1, features: list = None):
"""
Initialize the trainer.
Args:
symbol: Trading symbol (e.g., 'XAUUSD', 'EURUSD')
timeframe: MT5 timeframe constant
lookback: Number of bars to look back for prediction
prediction_horizon: Number of bars ahead to predict
features: List of features to use (default: OHLC + volume)
"""
self.symbol = symbol
self.timeframe = timeframe
self.lookback = lookback
self.prediction_horizon = prediction_horizon
self.features = features or ['open', 'high', 'low', 'close', 'tick_volume']
self.scaler = MinMaxScaler()
self.model = None
# Initialize MT5
if not mt5.initialize():
raise RuntimeError(f"MT5 initialization failed: {mt5.last_error()}")
def fetch_data(self, start_date: datetime, end_date: datetime) -> pd.DataFrame:
"""
Fetch historical data from MT5.
Args:
start_date: Start date for data
end_date: End date for data
Returns:
DataFrame with OHLCV data
"""
print(f"Fetching data for {self.symbol} from {start_date} to {end_date}...")
rates = mt5.copy_rates_range(self.symbol, self.timeframe, start_date, end_date)
if rates is None or len(rates) == 0:
raise ValueError(f"No data available for {self.symbol} in the specified date range")
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
df.set_index('time', inplace=True)
print(f"Fetched {len(df)} bars")
return df
def prepare_features(self, df: pd.DataFrame) -> pd.DataFrame:
"""
Prepare features for training.
Args:
df: Raw OHLCV data
Returns:
DataFrame with features
"""
feature_df = df[self.features].copy()
# Add technical indicators as features
feature_df['rsi'] = self._calculate_rsi(df['close'], period=14)
feature_df['ema_20'] = df['close'].ewm(span=20).mean()
feature_df['ema_50'] = df['close'].ewm(span=50).mean()
feature_df['atr'] = self._calculate_atr(df, period=14)
# Price changes
feature_df['price_change'] = df['close'].pct_change()
feature_df['high_low_ratio'] = df['high'] / df['low']
# Volume features
feature_df['volume_ma'] = df['tick_volume'].rolling(window=20).mean()
feature_df['volume_ratio'] = df['tick_volume'] / feature_df['volume_ma']
# Drop NaN values
feature_df = feature_df.dropna()
return feature_df
def _calculate_rsi(self, prices: pd.Series, period: int = 14) -> pd.Series:
"""Calculate RSI indicator."""
delta = prices.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
def _calculate_atr(self, df: pd.DataFrame, period: int = 14) -> pd.Series:
"""Calculate ATR indicator."""
high_low = df['high'] - df['low']
high_close = np.abs(df['high'] - df['close'].shift())
low_close = np.abs(df['low'] - df['close'].shift())
tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
atr = tr.rolling(window=period).mean()
return atr
def create_sequences(self, data: np.ndarray, target: np.ndarray) -> tuple:
"""
Create sequences for LSTM/RNN training.
Args:
data: Feature data
target: Target values (price change percentages)
Returns:
Tuple of (X, y) sequences
"""
X, y = [], []
for i in range(self.lookback, len(data) - self.prediction_horizon + 1):
X.append(data[i - self.lookback:i])
# Target is already the price change percentage at position i
y.append(target[i])
return np.array(X), np.array(y)
def build_model(self, input_shape: tuple) -> keras.Model:
"""
Build the neural network model.
Args:
input_shape: Shape of input data (lookback, features)
Returns:
Compiled Keras model
"""
model = keras.Sequential([
layers.LSTM(128, return_sequences=True, input_shape=input_shape),
layers.Dropout(0.3),
layers.LSTM(64, return_sequences=True),
layers.Dropout(0.3),
layers.LSTM(32),
layers.Dropout(0.3),
layers.Dense(32, activation='relu'),
layers.Dense(16, activation='relu'),
layers.Dense(1) # Predict price change percentage
])
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.0005), # Lower learning rate for stability
loss='mse',
metrics=['mae']
)
return model
def train(self, epochs: int = 50, batch_size: int = 32,
validation_split: float = 0.2, verbose: int = 1):
"""
Train the model.
Args:
epochs: Number of training epochs
batch_size: Batch size for training
validation_split: Fraction of data to use for validation
verbose: Verbosity level
"""
# Fetch data (last 2 years)
end_date = datetime.now()
start_date = end_date - timedelta(days=730)
df = self.fetch_data(start_date, end_date)
feature_df = self.prepare_features(df)
# Prepare data - align target with features after dropna
feature_data = feature_df.values
# Get target data aligned with feature_df (after dropna)
# Use .loc to align by index, then convert to values
close_prices = df.loc[feature_df.index, 'close'].values
# Predict price change percentage instead of absolute price (more stable)
# Calculate future price change: (future_price - current_price) / current_price
target_data = []
for i in range(len(close_prices)):
if i + self.prediction_horizon < len(close_prices):
current_price = close_prices[i]
future_price = close_prices[i + self.prediction_horizon]
price_change_pct = (future_price - current_price) / current_price if current_price > 0 else 0.0
target_data.append(price_change_pct)
else:
target_data.append(0.0)
target_data = np.array(target_data)
# Scale features
feature_data_scaled = self.scaler.fit_transform(feature_data)
# Create sequences
X, y = self.create_sequences(feature_data_scaled, target_data)
# Split data
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=validation_split, shuffle=False
)
print(f"\nTraining data shape: {X_train.shape}")
print(f"Validation data shape: {X_test.shape}")
# Build model - use actual feature count from data
actual_num_features = X_train.shape[2]
print(f"Actual number of features: {actual_num_features}")
self.model = self.build_model((X_train.shape[1], X_train.shape[2]))
print("\nModel architecture:")
self.model.summary()
# Train model
print("\nTraining model...")
history = self.model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(X_test, y_test),
verbose=verbose,
callbacks=[
keras.callbacks.EarlyStopping(
monitor='val_loss',
patience=10,
restore_best_weights=True
),
keras.callbacks.ReduceLROnPlateau(
monitor='val_loss',
factor=0.5,
patience=5,
min_lr=0.0001
)
]
)
# Evaluate
train_loss = self.model.evaluate(X_train, y_train, verbose=0)
test_loss = self.model.evaluate(X_test, y_test, verbose=0)
print(f"\nTraining Loss: {train_loss[0]:.4f}, MAE: {train_loss[1]:.4f}")
print(f"Validation Loss: {test_loss[0]:.4f}, MAE: {test_loss[1]:.4f}")
return history
def export_to_onnx(self, output_path: str):
"""
Export the trained model to ONNX format.
Args:
output_path: Path to save ONNX model
"""
if self.model is None:
raise ValueError("Model must be trained before exporting")
print(f"\nExporting model to ONNX format: {output_path}")
# Get actual number of features from model input shape
# The model was built with the actual feature count during training
if self.model is not None and hasattr(self.model, 'input_shape'):
num_features = self.model.input_shape[2] if len(self.model.input_shape) > 2 else self.model.input_shape[1]
else:
# Fallback calculation
# Base features: open, high, low, close, tick_volume (5)
# Added features: rsi, ema_20, ema_50, atr, price_change, high_low_ratio, volume_ma, volume_ratio (8)
num_features = len(self.features) + 8
print(f"Using {num_features} features for ONNX export")
# Create input signature
input_shape = (None, self.lookback, num_features)
spec = (tf.TensorSpec(input_shape, tf.float32, name="input"),)
# Convert to ONNX using tf2onnx
# Workaround for tf2onnx 1.16.1 issue with Sequential models (GitHub issue #2319)
# Fix: Add output_names attribute to Sequential model if missing
if hasattr(self.model, 'output_names') is False:
# Workaround: Create a wrapper or use functional API
try:
# Try to get output names from model outputs
if hasattr(self.model, 'outputs') and self.model.outputs:
self.model.output_names = [f'output_{i}' for i in range(len(self.model.outputs))]
else:
self.model.output_names = ['output']
except:
pass
# Create input signature tuple
spec = (tf.TensorSpec((None, self.lookback, num_features), tf.float32, name="input"),)
# Skip direct Sequential conversion - use Functional API directly
# This avoids the 'output_names' attribute error
try:
# Method 1: Convert Sequential to Functional API model (more reliable)
print("Converting Sequential model to Functional API...")
# Create functional model from Sequential
input_layer = keras.Input(shape=(self.lookback, num_features), name="input")
x = input_layer
# Rebuild model as functional
for layer in self.model.layers:
x = layer(x)
functional_model = keras.Model(inputs=input_layer, outputs=x)
# Convert functional model
onnx_model_proto, _ = tf2onnx.convert.from_keras(
functional_model,
input_signature=spec,
opset=13
)
onnx.save_model(onnx_model_proto, output_path)
print(f"ONNX model saved to: {output_path}")
except Exception as e1:
# Method 2: Use concrete function approach
try:
print("Trying concrete function method...")
# Create concrete function
input_spec = tf.TensorSpec(shape=(None, self.lookback, num_features), dtype=tf.float32)
@tf.function
def model_func(x):
return self.model(x)
# Get concrete function
concrete_func = model_func.get_concrete_function(input_spec)
# Convert with input_signature as list
input_signature_list = [input_spec]
onnx_model_proto, _ = tf2onnx.convert.from_function(
concrete_func,
input_signature=input_signature_list,
opset=13
)
onnx.save_model(onnx_model_proto, output_path)
print(f"ONNX model saved to: {output_path}")
except Exception as e2:
# Method 3: Try alternative conversion method
try:
print("Trying alternative conversion method...")
# Save model first, then convert
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
# Save as .keras format
keras_path = os.path.join(tmpdir, "model.keras")
self.model.save(keras_path)
# Load and convert
loaded_model = keras.models.load_model(keras_path)
# Convert Sequential to Functional
input_layer = keras.Input(shape=(self.lookback, num_features), name="input")
x = input_layer
for layer in loaded_model.layers:
x = layer(x)
functional_model = keras.Model(inputs=input_layer, outputs=x)
# Try conversion again
onnx_model_proto, _ = tf2onnx.convert.from_keras(
functional_model,
input_signature=spec,
opset=13
)
onnx.save_model(onnx_model_proto, output_path)
print(f"ONNX model saved to: {output_path}")
except Exception as e3:
raise RuntimeError(
f"Failed to export ONNX model.\n"
f"Error 1 (functional): {str(e1)[:200]}\n"
f"Error 2 (concrete): {str(e2)[:200]}\n"
f"Error 3 (alternative): {str(e3)[:200]}\n\n"
f"Please try upgrading tf2onnx: pip install --upgrade tf2onnx"
)
# Verify ONNX model
try:
onnx_model = onnx.load(output_path)
onnx.checker.check_model(onnx_model)
print("ONNX model validation passed")
except Exception as e:
print(f"⚠ ONNX model validation warning: {e}")
def cleanup(self):
"""Clean up MT5 connection."""
mt5.shutdown()
def main():
"""Main function."""
parser = argparse.ArgumentParser(description='Train ONNX model for MT5 price prediction')
parser.add_argument('--symbol', type=str, default='XAUUSD', help='Trading symbol')
parser.add_argument('--timeframe', type=str, default='H1',
choices=['M1', 'M5', 'M15', 'M30', 'H1', 'H4', 'D1'],
help='Timeframe')
parser.add_argument('--lookback', type=int, default=60,
help='Number of bars to look back')
parser.add_argument('--epochs', type=int, default=50, help='Training epochs')
parser.add_argument('--batch-size', type=int, default=32, help='Batch size')
parser.add_argument('--output', type=str, default='models',
help='Output directory for ONNX model')
args = parser.parse_args()
# Convert timeframe string to MT5 constant
timeframe_map = {
'M1': mt5.TIMEFRAME_M1,
'M5': mt5.TIMEFRAME_M5,
'M15': mt5.TIMEFRAME_M15,
'M30': mt5.TIMEFRAME_M30,
'H1': mt5.TIMEFRAME_H1,
'H4': mt5.TIMEFRAME_H4,
'D1': mt5.TIMEFRAME_D1
}
timeframe = timeframe_map[args.timeframe]
# Create output directory
os.makedirs(args.output, exist_ok=True)
# Create trainer
trainer = ONNXModelTrainer(
symbol=args.symbol,
timeframe=timeframe,
lookback=args.lookback
)
try:
# Train model
trainer.train(epochs=args.epochs, batch_size=args.batch_size)
# Export to ONNX
model_name = f"{args.symbol}_{args.timeframe}_model.onnx"
output_path = os.path.join(args.output, model_name)
trainer.export_to_onnx(output_path)
# Save scaler for consistent normalization
scaler_path = os.path.join(args.output, f"{args.symbol}_{args.timeframe}_scaler.pkl")
import pickle
with open(scaler_path, 'wb') as f:
pickle.dump(trainer.scaler, f)
print(f"Scaler saved to: {scaler_path}")
print(f" Use this with predict_with_onnx.py for consistent normalization")
print(f"\n{'='*60}")
print("Training completed successfully!")
print(f"ONNX model saved to: {output_path}")
print(f"{'='*60}\n")
except Exception as e:
print(f"\nError: {e}")
sys.exit(1)
finally:
trainer.cleanup()
if __name__ == '__main__':
main()