NN_in_Trading/Experts/Unsupervised/PCA/pca_net.mq5

613 lines
44 KiB
MQL5
Raw Permalink Normal View History

2026-06-05 22:00:59 +03:00
//+------------------------------------------------------------------+
2026-03-12 15:02:23 +02:00
//| pca_net.mq5 |
//| Copyright 2022, DNG |
//| https://www.mql5.com/ru/users/dng |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, DNG"
#property link "https://www.mql5.com/ru/users/dng"
#property version "1.00"
//+------------------------------------------------------------------+
//| Includes |
//+------------------------------------------------------------------+
#include "pca.mqh"
#include <Trade\SymbolInfo.mqh>
#include <Indicators\Oscilators.mqh>
//---
#define FileName Symb.Name()+"_"+EnumToString((ENUM_TIMEFRAMES)Period())+"_"+IntegerToString(PCA.VectorSize(),3)+StringSubstr(__FILE__,0,StringFind(__FILE__,".",0))
#define CSV __FILE__+".csv"
//---
enum ENUM_SIGNAL
{
Sell = -1,
Undefine = 0,
Buy = 1
};
//+------------------------------------------------------------------+
//| input parameters |
//+------------------------------------------------------------------+
input int StudyPeriod = 2; //Study period, years
/*input*/ uint HistoryBars = 20; //Depth of history
ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT;
//---
input group "---- RSI ----"
input int RSIPeriod = 14; //Period
input ENUM_APPLIED_PRICE RSIPrice = PRICE_CLOSE; //Applied price
//---
input group "---- CCI ----"
input int CCIPeriod = 14; //Period
input ENUM_APPLIED_PRICE CCIPrice = PRICE_TYPICAL; //Applied price
//---
input group "---- ATR ----"
input int ATRPeriod = 14; //Period
//---
input group "---- MACD ----"
input int FastPeriod = 12; //Fast
input int SlowPeriod = 26; //Slow
input int SignalPeriod = 9; //Signal
input ENUM_APPLIED_PRICE MACDPrice = PRICE_CLOSE; //Applied price
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSymbolInfo *Symb;
MqlRates Rates[];
CNet *Net;
CBufferFloat *TempData;
CiRSI *RSI;
CiCCI *CCI;
CiATR *ATR;
CiMACD *MACD;
CPCA *PCA;
//---
float dError;
float dUndefine;
float dForecast;
float dPrevSignal;
datetime dtStudied;
bool bEventStudy;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
Symb = new CSymbolInfo();
if(CheckPointer(Symb) == POINTER_INVALID || !Symb.Name(_Symbol))
return INIT_FAILED;
Symb.Refresh();
//---
RSI = new CiRSI();
if(CheckPointer(RSI) == POINTER_INVALID || !RSI.Create(Symb.Name(), TimeFrame, RSIPeriod, RSIPrice))
return INIT_FAILED;
//---
CCI = new CiCCI();
if(CheckPointer(CCI) == POINTER_INVALID || !CCI.Create(Symb.Name(), TimeFrame, CCIPeriod, CCIPrice))
return INIT_FAILED;
//---
ATR = new CiATR();
if(CheckPointer(ATR) == POINTER_INVALID || !ATR.Create(Symb.Name(), TimeFrame, ATRPeriod))
return INIT_FAILED;
//---
MACD = new CiMACD();
if(CheckPointer(MACD) == POINTER_INVALID || !MACD.Create(Symb.Name(), TimeFrame, FastPeriod, SlowPeriod, SignalPeriod, MACDPrice))
return INIT_FAILED;
//---
PCA = new CPCA();
if(CheckPointer(PCA) == POINTER_INVALID)
return INIT_FAILED;
int handl = FileOpen("pca.net", FILE_READ | FILE_BIN);
if(handl == INVALID_HANDLE)
return INIT_FAILED;
if(!PCA.Load(handl))
return INVALID_HANDLE;
FileClose(handl);
2026-03-14 22:28:53 +02:00
HistoryBars = (uint)PCA.Inputs() / 8;
PrintFormat("History bars %d => PCA vector size %d", HistoryBars, PCA.VectorSize());
2026-03-12 15:02:23 +02:00
//---
Net = new CNet(NULL);
ResetLastError();
if(CheckPointer(Net) == POINTER_INVALID || !Net.Load(FileName + ".nnw", dError, dUndefine, dForecast, dtStudied, false))
{
printf("%s - %d -> Error of read %s prev Net %d", __FUNCTION__, __LINE__, FileName + ".nnw", GetLastError());
CArrayObj *Topology = new CArrayObj();
if(CheckPointer(Topology) == POINTER_INVALID)
return INIT_FAILED;
//--- 0
CLayerDescription *desc = new CLayerDescription();
if(CheckPointer(desc) == POINTER_INVALID)
return INIT_FAILED;
desc.count = (int)PCA.VectorSize();
desc.type = defNeuronBaseOCL;
desc.optimization = ADAM;
desc.activation = None;
if(!Topology.Add(desc))
return INIT_FAILED;
//--- 1
desc = new CLayerDescription();
if(CheckPointer(desc) == POINTER_INVALID)
return INIT_FAILED;
desc.count = 300;
desc.type = defNeuron;
desc.activation = LReLU;
desc.optimization = ADAM;
if(!Topology.Add(desc))
return INIT_FAILED;
//--- 2
desc = new CLayerDescription();
if(CheckPointer(desc) == POINTER_INVALID)
return INIT_FAILED;
desc.count = 300;
desc.type = defNeuron;
desc.activation = TANH;
desc.optimization = ADAM;
if(!Topology.Add(desc))
return INIT_FAILED;
//--- 3
desc = new CLayerDescription();
if(CheckPointer(desc) == POINTER_INVALID)
return INIT_FAILED;
desc.count = 300;
desc.type = defNeuron;
desc.activation = TANH;
desc.optimization = ADAM;
if(!Topology.Add(desc))
return INIT_FAILED;
//--- 4
desc = new CLayerDescription();
if(CheckPointer(desc) == POINTER_INVALID)
return INIT_FAILED;
desc.count = 3;
desc.type = defNeuron;
desc.activation = SIGMOID;
desc.optimization = ADAM;
if(!Topology.Add(desc))
return INIT_FAILED;
delete Net;
Net = new CNet(Topology);
delete Topology;
if(CheckPointer(Net) == POINTER_INVALID)
return INIT_FAILED;
dError = -1;
dUndefine = 0;
dForecast = 0;
dtStudied = 0;
}
//---
TempData = new CBufferFloat();
if(CheckPointer(TempData) == POINTER_INVALID)
return INIT_FAILED;
//---
bEventStudy = EventChartCustom(ChartID(), 1, (long)MathMax(0, MathMin(iTime(Symb.Name(), PERIOD_CURRENT, (int)(100 * Net.recentAverageSmoothingFactor * (dForecast >= 70 ? 1 : 10))), dtStudied)), 0, "Init");
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
if(CheckPointer(Symb) != POINTER_INVALID)
delete Symb;
//---
if(CheckPointer(PCA) != POINTER_INVALID)
delete PCA;
//---
if(CheckPointer(RSI) != POINTER_INVALID)
delete RSI;
//---
if(CheckPointer(CCI) != POINTER_INVALID)
delete CCI;
//---
if(CheckPointer(ATR) != POINTER_INVALID)
delete ATR;
//---
if(CheckPointer(MACD) != POINTER_INVALID)
delete MACD;
//---
if(CheckPointer(Net) != POINTER_INVALID)
delete Net;
if(CheckPointer(TempData) != POINTER_INVALID)
delete TempData;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
if(!bEventStudy && (dPrevSignal == -2 || dtStudied < SeriesInfoInteger(Symb.Name(), TimeFrame, SERIES_LASTBAR_DATE)))
bEventStudy = EventChartCustom(ChartID(), 1, (long)MathMax(0, MathMin(iTime(Symb.Name(), PERIOD_CURRENT, (int)(100 * Net.recentAverageSmoothingFactor * (dForecast >= 70 ? 1 : 10))), dtStudied)), 0, "New Bar");
//---
Comment(StringFormat("K7>2 A>1KB8O %s; PrevSignal %.5f; >45;L >1CG5=0 %s -> %s", (string)bEventStudy, dPrevSignal, TimeToString(dtStudied), TimeToString(SeriesInfoInteger(Symb.Name(), TimeFrame, SERIES_LASTBAR_DATE))));
}
//+------------------------------------------------------------------+
//| Trade function |
//+------------------------------------------------------------------+
void OnTrade()
{
//---
}
//+------------------------------------------------------------------+
//| TradeTransaction function |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
const MqlTradeRequest& request,
const MqlTradeResult& result)
{
//---
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
//---
if(id == 1001)
{
Net.TrainMode(true);
Train(lparam);
bEventStudy = false;
OnTick();
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void Train(datetime StartTrainBar = 0)
{
//---
MqlDateTime start_time;
TimeCurrent(start_time);
start_time.year -= StudyPeriod;
if(start_time.year <= 0)
start_time.year = 1900;
datetime st_time = StructToTime(start_time);
//---
int bars = CopyRates(Symb.Name(), TimeFrame, st_time, TimeCurrent(), Rates);
if(!RSI.BufferResize(bars) || !CCI.BufferResize(bars) || !ATR.BufferResize(bars) || !MACD.BufferResize(bars))
{
ExpertRemove();
return;
}
if(!ArraySetAsSeries(Rates, true))
{
ExpertRemove();
return;
}
//---
RSI.Refresh();
CCI.Refresh();
ATR.Refresh();
MACD.Refresh();
//---
int total = bars - (int)HistoryBars - 1;
float fractals[];
matrixf data;
if(!data.Init(total, 8 * HistoryBars) ||
ArrayResize(fractals, total * 3) <= 0)
{
ExpertRemove();
return;
}
//---
for(int i = 0; (i < total && !IsStopped()); i++)
{
Comment(StringFormat("Create data: %d of %d", i, total));
for(int b = 0; b < (int)HistoryBars; b++)
{
int bar = i + b;
int shift = b * 8;
double open = Rates[bar].open;
data[i, shift] = float(open - Rates[bar].low);
data[i, shift + 1] = float(Rates[bar].high - open);
data[i, shift + 2] = float(Rates[bar].close - open);
data[i, shift + 3] = float(RSI.GetData(MAIN_LINE, bar));
data[i, shift + 4] = float(CCI.GetData(MAIN_LINE, bar));
data[i, shift + 5] = float(ATR.GetData(MAIN_LINE, bar));
data[i, shift + 6] = float(MACD.GetData(MAIN_LINE, bar));
data[i, shift + 7] = float(MACD.GetData(SIGNAL_LINE, bar));
}
int shift = i * 3;
int bar = i;
fractals[shift] = (float)(Rates[bar - 1].high <= Rates[bar].high && Rates[bar + 1].high < Rates[bar].high);
fractals[shift + 1] = (float)(Rates[bar - 1].low >= Rates[bar].low && Rates[bar + 1].low > Rates[bar].low);
fractals[shift + 2] = (float)((fractals[shift] + fractals[shift]) == 0);
}
if(IsStopped())
{
ExpertRemove();
return;
}
CBufferFloat *Fractals = new CBufferFloat();
if(CheckPointer(Fractals) == POINTER_INVALID ||
!Fractals.AssignArray(fractals))
return;
//---
ResetLastError();
matrixf pca = PCA.ReduceM(data);
if(pca.Rows() <= 0 || pca.Cols() != PCA.VectorSize())
{
printf("H81:0 2K?>;=5=8O %d", GetLastError());
ExpertRemove();
return;
}
//---
if(CheckPointer(TempData) == POINTER_INVALID)
{
TempData = new CBufferFloat();
if(CheckPointer(TempData) == POINTER_INVALID)
{
ExpertRemove();
return;
}
}
double prev_un, prev_for, prev_er;
dUndefine = 0;
dForecast = 0;
dError = -1;
dPrevSignal = 0;
bool stop = false;
int count = 0;
do
{
prev_un = dUndefine;
prev_for = dForecast;
prev_er = dError;
ENUM_SIGNAL bar = Undefine;
//---
stop = IsStopped();
for(int it = 0; (it < total - 300 && !IsStopped()); it++)
{
int i = (int)((MathRand() * MathRand() / MathPow(32767, 2)) * (total - 300)) + 300;
TempData.Clear();
if(!TempData.Reserve((int)PCA.VectorSize()))
{
if(CheckPointer(Fractals) == POINTER_DYNAMIC)
delete Fractals;
if(CheckPointer(TempData) == POINTER_DYNAMIC)
delete TempData;
Comment("");
//---
ExpertRemove();
return;
}
for(ulong c = 0; c < pca.Cols(); c++)
if(!TempData.Add(pca[i, c]))
{
if(CheckPointer(Fractals) == POINTER_DYNAMIC)
delete Fractals;
if(CheckPointer(TempData) == POINTER_DYNAMIC)
delete TempData;
Comment("");
//---
ExpertRemove();
return;
}
2026-03-14 22:28:53 +02:00
if(!Net.feedForward(TempData, 1, false, (CBufferFloat*)NULL))
2026-03-12 15:02:23 +02:00
{
if(CheckPointer(Fractals) == POINTER_DYNAMIC)
delete Fractals;
if(CheckPointer(TempData) == POINTER_DYNAMIC)
delete TempData;
Comment("");
//---
ExpertRemove();
return;
}
Net.getResults(TempData);
//---
float sum = 0;
for(int res = 0; res < 3; res++)
{
float temp = exp(TempData.At(res));
sum += temp;
TempData.Update(res, temp);
}
for(int res = 0; (res < 3 && sum > 0); res++)
TempData.Update(res, TempData.At(res) / sum);
//---
2026-03-14 22:28:53 +02:00
switch(TempData.Argmax())
{
case 1:
dPrevSignal = (TempData[1] != TempData[2] ? TempData[1] : 0);
2026-03-12 15:02:23 +02:00
break;
case 2:
dPrevSignal = -TempData[2];
break;
default:
dPrevSignal = 0;
break;
}
string s = StringFormat("Study -> Era %d -> %.2f -> Undefine %.2f%% foracast %.2f%%\n %d of %d -> %.2f%% \nError %.2f\n%s -> %.2f ->> Buy %.5f - Sell %.5f - Undef %.5f", count, dError, dUndefine, dForecast, it + 1, total - 300, (double)(it + 1.0) / (total - 300) * 100, Net.getRecentAverageError(), EnumToString(DoubleToSignal(dPrevSignal)), dPrevSignal, TempData[1], TempData[2], TempData[0]);
2026-03-14 22:28:53 +02:00
Comment(s);
stop = IsStopped();
if(!stop)
{
int shift = i * 3;
TempData.Clear();
2026-03-12 15:02:23 +02:00
TempData.Add(Fractals.At(shift + 2));
TempData.Add(Fractals.At(shift));
TempData.Add(Fractals.At(shift + 1));
2026-03-14 22:28:53 +02:00
Net.backProp(TempData, NULL, NULL);
2026-03-12 15:02:23 +02:00
ENUM_SIGNAL signal = DoubleToSignal(dPrevSignal);
if(signal != Undefine)
{
if((signal == Sell && Fractals.At(shift + 1) == 1) || (signal == Buy && Fractals.At(shift) == 1))
dForecast += (100 - dForecast) / Net.recentAverageSmoothingFactor;
else
dForecast -= dForecast / Net.recentAverageSmoothingFactor;
dUndefine -= dUndefine / Net.recentAverageSmoothingFactor;
}
else
{
if(Fractals.At(shift + 2) == 1)
dUndefine += (100 - dUndefine) / Net.recentAverageSmoothingFactor;
}
}
}
count++;
for(int i = 0; i < 300; i++)
{
TempData.Clear();
if(!TempData.Reserve((int)pca.Cols()))
{
if(CheckPointer(Fractals) == POINTER_DYNAMIC)
delete Fractals;
if(CheckPointer(TempData) == POINTER_DYNAMIC)
delete TempData;
Comment("");
//---
ExpertRemove();
return;
}
for(ulong c = 0; c < pca.Cols(); c++)
if(!TempData.Add(pca[i, c]))
{
if(CheckPointer(Fractals) == POINTER_DYNAMIC)
delete Fractals;
if(CheckPointer(TempData) == POINTER_DYNAMIC)
delete TempData;
Comment("");
//---
ExpertRemove();
return;
}
2026-03-14 22:28:53 +02:00
if(!Net.feedForward(TempData, 1, false, (CBufferFloat*)NULL))
2026-03-12 15:02:23 +02:00
{
if(CheckPointer(Fractals) == POINTER_DYNAMIC)
delete Fractals;
if(CheckPointer(TempData) == POINTER_DYNAMIC)
delete TempData;
Comment("");
//---
ExpertRemove();
return;
}
Net.getResults(TempData);
//---
float sum = 0;
for(int res = 0; res < 3; res++)
{
float temp = exp(TempData.At(res));
sum += temp;
TempData.Update(res, temp);
}
for(int res = 0; (res < 3 && sum > 0); res++)
TempData.Update(res, TempData.At(res) / sum);
//---
2026-03-14 22:28:53 +02:00
switch(TempData.Argmax())
2026-03-12 15:02:23 +02:00
{
case 1:
dPrevSignal = (TempData[1] != TempData[2] ? TempData[1] : 0);
break;
case 2:
dPrevSignal = -TempData[2];
break;
default:
dPrevSignal = 0;
break;
}
if(DoubleToSignal(dPrevSignal) == Undefine)
DeleteObject(Rates[i + 2].time);
else
DrawObject(Rates[i + 2].time, dPrevSignal, Rates[i + 2].high, Rates[i + 2].low);
}
if(!stop)
{
dError = Net.getRecentAverageError();
Net.Save(FileName + ".nnw", dError, dUndefine, dForecast, Rates[0].time, false);
printf("Era %d -> error %.2f %% forecast %.2f", count, dError, dForecast);
ChartScreenShot(0, FileName + IntegerToString(count) + ".png", 750, 400);
int h = FileOpen(FileName + ".csv", FILE_READ | FILE_WRITE | FILE_CSV);
if(h != INVALID_HANDLE)
{
FileSeek(h, 0, SEEK_END);
FileWrite(h, lr, count, dError, dUndefine, dForecast);
FileFlush(h);
FileClose(h);
}
}
}
while((!(DoubleToSignal(dPrevSignal) != Undefine || dForecast > 70) || !(dError < 0.1 && MathAbs(dError - prev_er) < 0.01 && MathAbs(dUndefine - prev_un) < 0.1 && MathAbs(dForecast - prev_for) < 0.1)) && !stop);
//---
if(CheckPointer(Fractals) == POINTER_DYNAMIC)
delete Fractals;
if(CheckPointer(TempData) == POINTER_DYNAMIC)
delete TempData;
Comment("");
//---
ExpertRemove();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
ENUM_SIGNAL DoubleToSignal(double value)
{
value = NormalizeDouble(value, 1);
if(MathAbs(value) > 1 || MathAbs(value) <= 0)
return Undefine;
if(value > 0)
return Buy;
else
return Sell;
//---
return Undefine;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void DrawObject(datetime time, double signal, double high, double low)
{
double price = 0;
int arrow = 0;
color clr = 0;
ENUM_ARROW_ANCHOR anch = ANCHOR_BOTTOM;
switch(DoubleToSignal(signal))
{
case Buy:
price = low;
arrow = 217;
clr = clrBlue;
anch = ANCHOR_TOP;
break;
case Sell:
price = high;
arrow = 218;
clr = clrRed;
anch = ANCHOR_BOTTOM;
break;
}
if(price == 0 || arrow == 0)
return;
//---
string name = TimeToString(time);
if(ObjectFind(0, name) < 0)
{
ResetLastError();
if(!ObjectCreate(0, name, OBJ_ARROW, 0, time, 0))
{
printf("Error of creating object %d", GetLastError());
return;
}
}
//printf("%s - %d -> %s",__FUNCTION__,__LINE__,name);
ObjectSetDouble(0, name, OBJPROP_PRICE, price);
ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrow);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_ANCHOR, anch);
ObjectSetString(0, name, OBJPROP_TOOLTIP, EnumToString(DoubleToSignal(signal)) + " " + DoubleToString(signal, 5));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void DeleteObject(datetime time)
{
string name = TimeToString(time);
if(ObjectFind(0, name) >= 0)
ObjectDelete(0, name);
}
//+------------------------------------------------------------------+