forked from airat77786/MQL5Book
168 lines
5.9 KiB
MQL5
168 lines
5.9 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| MatrixForexBasket.mq5 |
| |||
//| Copyright 2022, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#include <Graphics/Graphic.mqh>
| |||
#include "..\..\Include\MatrixProcessor.mqh"
| |||
| |||
#property script_show_inputs
| |||
| |||
input int BarCount = 20; // BarCount (in-sample "history" and out-of-sample "future")
| |||
input int BarOffset = 10; // BarOffset (where "future" begins)
| |||
input ENUM_CURVE_TYPE CurveType = CURVE_LINES;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Model of ideal constantly growing balance curve |
| |||
//+------------------------------------------------------------------+
| |||
void ConstantGrow(vector &v)
| |||
{
| |||
for(ulong i = 0; i < v.Size(); ++i)
| |||
{
| |||
v[i] = (double)(i + 1);
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Convert vector of one primitive type (S) to another (T) |
| |||
//+------------------------------------------------------------------+
| |||
template<typename T, typename S>
| |||
void ConvertV(vector<T> &t, const S &v)
| |||
{
| |||
for(ulong i = 0; i < v.Size(); ++i)
| |||
{
| |||
t[i] = (T)v[i];
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Script program start function |
| |||
//+------------------------------------------------------------------+
| |||
void OnStart()
| |||
{
| |||
// work symbols are hardcoded (adjust according to your needs and environment)
| |||
const string symbols[] = {"EURUSD", "GBPUSD", "USDJPY", "USDCAD", "USDCHF", "AUDUSD", "NZDUSD"};
| |||
const int size = ArraySize(symbols);
| |||
| |||
// check if the linear system is well-defined
| |||
if(size > BarCount - BarOffset)
| |||
{
| |||
Print("Symbol count must be larger than number of historic bars, given: ",
| |||
size, " and ", BarCount - BarOffset, " respectively");
| |||
return;
| |||
}
| |||
| |||
// create a matrix for given symbols and bar count
| |||
matrix rates(BarCount, size);
| |||
// build a model of best balance - stable profit on every bar
| |||
vector model(BarCount - BarOffset, ConstantGrow);
| |||
// aux vector to hold intermediate row of i-th symbol quotes
| |||
vector close;
| |||
| |||
for(int i = 0; i < size; i++) // process all symbols
| |||
{
| |||
// get rates
| |||
if(close.CopyRates(symbols[i], _Period, COPY_RATES_CLOSE, 0, BarCount))
| |||
{
| |||
// get price increment (profit)
| |||
close -= close[0];
| |||
// adjust profit by point value
| |||
close *= SymbolInfoDouble(symbols[i], SYMBOL_TRADE_TICK_VALUE) /
| |||
SymbolInfoDouble(symbols[i], SYMBOL_TRADE_TICK_SIZE);
| |||
// place vector to specific column in the matrix
| |||
rates.Col(close, i);
| |||
}
| |||
else
| |||
{
| |||
Print("vector.CopyRates(%d, COPY_RATES_CLOSE) failed. Error ", symbols[i], _LastError);
| |||
return;
| |||
}
| |||
}
| |||
| |||
// split the matrix for starting part to get solution
| |||
// (which emulates optimization on history)
| |||
// and second part for forward test
| |||
matrix split[];
| |||
if(BarOffset > 0)
| |||
{
| |||
// training = backtest on BarCount - BarOffset bars
| |||
// out of sample future = forward test on BarOffset bars
| |||
ulong parts[] = {BarCount - BarOffset, BarOffset};
| |||
rates.Split(parts, 0, split);
| |||
}
| |||
| |||
// solve linear system equation against the model
| |||
vector x = (BarOffset > 0) ? split[0].LstSq(model) : rates.LstSq(model);
| |||
| |||
Print("Solution (lots per symbol): ");
| |||
{
| |||
// use float vector just for shorter printing of the solution
| |||
vectorf xf(size, ConvertV, x);
| |||
Print(xf);
| |||
}
| |||
| |||
// use vector for simulated balance curve
| |||
vector balance = vector::Zeros(BarCount);
| |||
for(int i = 1; i < BarCount; ++i)
| |||
{
| |||
balance[i] = 0;
| |||
for(int j = 0; j < size; ++j)
| |||
{
| |||
balance[i] += (float)(rates[i][j] * x[j]);
| |||
}
| |||
}
| |||
| |||
// now estimate the quality of solution
| |||
if(BarOffset > 0)
| |||
{
| |||
// NB: MQL5 doesn't have Split for vectors!
| |||
// NB: MQL5 can't assign vector to matrix or matrix to vector!
| |||
// make a copy of balance
| |||
vector backtest = balance;
| |||
// only historic in-sample bars are used for backtest estimation
| |||
backtest.Resize(BarCount - BarOffset);
| |||
// prepare forward out-of-sample part of the bars manually
| |||
vector forward(BarOffset);
| |||
for(int i = 0; i < BarOffset; ++i)
| |||
{
| |||
forward[i] = balance[BarCount - BarOffset + i];
| |||
}
| |||
// calculate regression metrics for backtest and forward
| |||
Print("Backtest R2 = ", backtest.RegressionMetric(model, REGRESSION_R2));
| |||
model.Resize(BarOffset);
| |||
model += BarCount - BarOffset;
| |||
Print("Forward R2 = ", forward.RegressionMetric(model, REGRESSION_R2));
| |||
}
| |||
else
| |||
{
| |||
Print("R2 = ", balance.RegressionMetric(model, REGRESSION_R2));
| |||
}
| |||
| |||
// copy the 'balance' vector into array of doubles
| |||
double array[];
| |||
Export(balance, array); // TODO: balance.Swap(array);
| |||
| |||
// pretty-printing with 2 digits
| |||
Print("Balance: ");
| |||
ArrayPrint(array, 2);
| |||
| |||
// let's draw the graph of the balance (both "backtest" and "forward")
| |||
GraphPlot(array, CurveType);
| |||
| |||
if(MQLInfoInteger(MQL_DEBUG))
| |||
{
| |||
Sleep(5000);
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
/*
| |||
| |||
(EURUSD,H1) Solution (lots per symbol):
| |||
(EURUSD,H1) [-0.0057809334,-0.0079846876,0.0088985749,-0.0041461736,-0.010710154,-0.0025694175,0.01493552]
| |||
(EURUSD,H1) Backtest R2 = 0.9896645616246145
| |||
(EURUSD,H1) Forward R2 = 0.8667852183780984
| |||
(EURUSD,H1) Balance:
| |||
(EURUSD,H1) 0.00 1.68 3.38 3.90 5.04 5.92 7.09 7.86 9.17 9.88 9.55 10.77 12.06 13.67 15.35 15.89 16.28 15.91 16.85 16.58
| |||
| |||
*/
| |||
//+------------------------------------------------------------------+
|