ONNX modellerini sınıflara sarma
Giriş
Bir önceki makalede, oylama sınıflandırıcısını ayarlamak için iki ONNX modeli kullandık. Kaynak metnin tamamı tek bir MQ5 dosyası olarak düzenlenmiştir. Kodun tamamı fonksiyonlara bölünmüştür. Peki ya modelleri değiştirmeye çalışırsak? Ya da başka bir model eklersek? Orijinal metin daha da büyüyecektir. Nesne yönelimli yaklaşımı deneyelim.
1. Hangi modelleri kullanacağız?
Önceki oylama sınıflandırıcısında, bir sınıflandırma modeli ve bir regresyon modeli kullandık. Regresyon modelinde, öngörülen fiyat hareketi (aşağı, yukarı, değişmez) yerine, sınıfı hesaplamak için kullanılan öngörülen fiyatı elde ederiz. Ancak, bu durumda, sınıfa göre bir olasılık dağılımımız yoktur, bu da "yumuşak oylama" olarak adlandırılan duruma izin vermez.
3 adet sınıflandırma modeli hazırladık. “MQL5'te ONNX modellerinin nasıl bir araya getirileceğine dair bir örnek” makalesinde iki model zaten kullanılmıştır. İlk model (regresyon) bir sınıflandırma modeline dönüştürülmüştür. Eğitim 10 OHLC fiyatından oluşan bir seri üzerinde gerçekleştirilmiştir. İkinci model ise sınıflandırma modelidir. Eğitim 63 Kapanış fiyatından oluşan bir seri üzerinde gerçekleştirilmiştir.
Son olarak, bir model daha vardır. Sınıflandırma modeli, 30 Kapanış fiyatı serisi ve ortalama alma periyotları 21 ve 34 olan iki basit hareketli ortalama serisi üzerinde eğitilmiştir. Hareketli ortalamaların Kapanış grafiği ile ve kendi aralarında çaprazlaması hakkında herhangi bir varsayımda bulunmadık - tüm modeller ağ tarafından katmanlar arasında katsayı matrisleri şeklinde hesaplanacak ve hatırlanacaktır.
Tüm modeller MetaQuotes-Demo sunucu verileri, EURUSD D1 üzerinde 2010.01.01 - 2023.01.01 tarihleri arasında eğitilmiştir. Her üç model için eğitim komut dosyaları Python'da yazılmıştır ve bu makaleye eklenmiştir. Okuyucunun dikkatini makalemizin ana konusundan uzaklaştırmamak için kaynak kodlarını burada vermeyeceğiz.
2. Tüm modeller için tek bir temel sınıf gereklidir
Üç model bulunmaktadır. Her biri, girdi verilerinin büyüklüğü ve hazırlanması açısından diğerlerinden farklıdır. Tüm modeller aynı arayüze sahiptir. Tüm modellerin sınıfları aynı temel sınıftan kalıtılmalıdır.
Temel sınıfı tanımlayalım.
//+------------------------------------------------------------------+ //| ModelSymbolPeriod.mqh | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ //--- price movement prediction #define PRICE_UP 0 #define PRICE_SAME 1 #define PRICE_DOWN 2 //+------------------------------------------------------------------+ //| Base class for models based on trained symbol and period | //+------------------------------------------------------------------+ class CModelSymbolPeriod { protected: long m_handle; // created model session handle string m_symbol; // symbol of trained data ENUM_TIMEFRAMES m_period; // timeframe of trained data datetime m_next_bar; // time of next bar (we work at bar begin only) double m_class_delta; // delta to recognize "price the same" in regression models public: //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CModelSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES period,const double class_delta=0.0001) { m_handle=INVALID_HANDLE; m_symbol=symbol; m_period=period; m_next_bar=0; m_class_delta=class_delta; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ ~CModelSymbolPeriod(void) { Shutdown(); } //+------------------------------------------------------------------+ //| virtual stub for Init | //+------------------------------------------------------------------+ virtual bool Init(const string symbol,const ENUM_TIMEFRAMES period) { return(false); } //+------------------------------------------------------------------+ //| Check for initialization, create model | //+------------------------------------------------------------------+ bool CheckInit(const string symbol,const ENUM_TIMEFRAMES period,const uchar& model[]) { //--- check symbol, period if(symbol!=m_symbol || period!=m_period) { PrintFormat("Model must work with %s,%s",m_symbol,EnumToString(m_period)); return(false); } //--- create a model from static buffer m_handle=OnnxCreateFromBuffer(model,ONNX_DEFAULT); if(m_handle==INVALID_HANDLE) { Print("OnnxCreateFromBuffer error ",GetLastError()); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+ //| Release ONNX session | //+------------------------------------------------------------------+ void Shutdown(void) { if(m_handle!=INVALID_HANDLE) { OnnxRelease(m_handle); m_handle=INVALID_HANDLE; } } //+------------------------------------------------------------------+ //| Check for continue OnTick | //+------------------------------------------------------------------+ virtual bool CheckOnTick(void) { //--- check new bar if(TimeCurrent()<m_next_bar) return(false); //--- set next bar time m_next_bar=TimeCurrent(); m_next_bar-=m_next_bar%PeriodSeconds(m_period); m_next_bar+=PeriodSeconds(m_period); //--- work on new day bar return(true); } //+------------------------------------------------------------------+ //| virtual stub for PredictPrice (regression model) | //+------------------------------------------------------------------+ virtual double PredictPrice(void) { return(DBL_MAX); } //+------------------------------------------------------------------+ //| Predict class (regression -> classification) | //+------------------------------------------------------------------+ virtual int PredictClass(void) { double predicted_price=PredictPrice(); if(predicted_price==DBL_MAX) return(-1); int predicted_class=-1; double last_close=iClose(m_symbol,m_period,1); //--- classify predicted price movement double delta=last_close-predicted_price; if(fabs(delta)<=m_class_delta) predicted_class=PRICE_SAME; else { if(delta<0) predicted_class=PRICE_UP; else predicted_class=PRICE_DOWN; } //--- return predicted class return(predicted_class); } }; //+------------------------------------------------------------------+
Temel sınıf hem regresyon hem de sınıflandırma modelleri için kullanılabilir. Yalnızca alt sınıfta uygun metodu uygulamamız gerekir - PredictPrice veya PredictClass.
Temel sınıf, modelin çalışacağı sembol zaman dilimini belirler (modelin eğitildiği veriler). Temel sınıf ayrıca modeli kullanan Uzman Danışmanın gerekli sembol zaman diliminde çalıştığını kontrol eder ve modeli çalıştırmak için bir ONNX oturumu oluşturur. Temel sınıf yalnızca yeni bir çubuğun başlangıcında çalışma sağlar.
3. Birinci model sınıfı
İlk modelimiz model.eurusd.D1.10.class.onnx olarak adlandırılır ve EURUSD D1 üzerinde 10 OHLC fiyatı serisi üzerinde eğitilmiş bir sınıflandırma modelidir.
//+------------------------------------------------------------------+ //| ModelEurusdD1_10Class.mqh | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "ModelSymbolPeriod.mqh" #resource "Python/model.eurusd.D1.10.class.onnx" as uchar model_eurusd_D1_10_class[] //+------------------------------------------------------------------+ //| ONNX-model wrapper class | //+------------------------------------------------------------------+ class CModelEurusdD1_10Class : public CModelSymbolPeriod { private: int m_sample_size; public: //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CModelEurusdD1_10Class(void) : CModelSymbolPeriod("EURUSD",PERIOD_D1) { m_sample_size=10; } //+------------------------------------------------------------------+ //| ONNX-model initialization | //+------------------------------------------------------------------+ virtual bool Init(const string symbol, const ENUM_TIMEFRAMES period) { //--- check symbol, period, create model if(!CModelSymbolPeriod::CheckInit(symbol,period,model_eurusd_D1_10_class)) { Print("model_eurusd_D1_10_class : initialization error"); return(false); } //--- since not all sizes defined in the input tensor we must set them explicitly //--- first index - batch size, second index - series size, third index - number of series (OHLC) const long input_shape[] = {1,m_sample_size,4}; if(!OnnxSetInputShape(m_handle,0,input_shape)) { Print("model_eurusd_D1_10_class : OnnxSetInputShape error ",GetLastError()); return(false); } //--- since not all sizes defined in the output tensor we must set them explicitly //--- first index - batch size, must match the batch size of the input tensor //--- second index - number of classes (up, same or down) const long output_shape[] = {1,3}; if(!OnnxSetOutputShape(m_handle,0,output_shape)) { Print("model_eurusd_D1_10_class : OnnxSetOutputShape error ",GetLastError()); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+ //| Predict class | //+------------------------------------------------------------------+ virtual int PredictClass(void) { static matrixf input_data(m_sample_size,4); // matrix for prepared input data static vectorf output_data(3); // vector to get result static matrix mm(m_sample_size,4); // matrix of horizontal vectors Mean static matrix ms(m_sample_size,4); // matrix of horizontal vectors Std static matrix x_norm(m_sample_size,4); // matrix for prices normalize //--- prepare input data matrix rates; //--- request last bars if(!rates.CopyRates(m_symbol,m_period,COPY_RATES_OHLC,1,m_sample_size)) return(-1); //--- get series Mean vector m=rates.Mean(1); //--- get series Std vector s=rates.Std(1); //--- prepare matrices for prices normalization for(int i=0; i<m_sample_size; i++) { mm.Row(m,i); ms.Row(s,i); } //--- the input of the model must be a set of vertical OHLC vectors x_norm=rates.Transpose(); //--- normalize prices x_norm-=mm; x_norm/=ms; //--- run the inference input_data.Assign(x_norm); if(!OnnxRun(m_handle,ONNX_NO_CONVERSION,input_data,output_data)) return(-1); //--- evaluate prediction return(int(output_data.ArgMax())); } }; //+------------------------------------------------------------------+
Yukarıda da belirtildiği gibi: "Üç model bulunmaktadır. Her biri, girdi verilerinin büyüklüğü ve hazırlanması açısından diğerlerinden farklıdır.” Sadece iki metodu yeniden tanımladık - Init ve PredictClass. Aynı metotlar diğer iki model için diğer iki sınıfta yeniden tanımlanacaktır.
Init metodu, ONNX modelimiz için bir oturumun oluşturulduğu ve girdi ve çıktı tensörlerinin büyüklüklerinin açıkça ayarlandığı CheckInit temel sınıf metodunu çağırır. Burada koddan çok yorum vardır.
PredictClass metodu, modeli eğitirken kullanılan girdi verisi hazırlama işleminin aynısını sağlar. Girdi, normalleştirilmiş OHLC fiyatlarının bir matrisidir.
4. Nasıl çalıştığını kontrol edelim
Sınıfımızın performansını test etmek için çok kompakt bir Uzman Danışman oluşturuldu.
//+------------------------------------------------------------------+ //| ONNX.eurusd.D1.Prediction.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include "ModelEurusdD1_10Class.mqh" #include <Trade\Trade.mqh> input double InpLots = 1.0; // Lots amount to open position CModelEurusdD1_10Class ExtModel; CTrade ExtTrade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if(!ExtModel.Init(_Symbol,_Period)) return(INIT_FAILED); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ExtModel.Shutdown(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(!ExtModel.CheckOnTick()) return; //--- predict next price movement int predicted_class=ExtModel.PredictClass(); //--- check trading according to prediction if(predicted_class>=0) if(PositionSelect(_Symbol)) CheckForClose(predicted_class); else CheckForOpen(predicted_class); } //+------------------------------------------------------------------+ //| Check for open position conditions | //+------------------------------------------------------------------+ void CheckForOpen(const int predicted_class) { ENUM_ORDER_TYPE signal=WRONG_VALUE; //--- check signals if(predicted_class==PRICE_DOWN) signal=ORDER_TYPE_SELL; // sell condition else { if(predicted_class==PRICE_UP) signal=ORDER_TYPE_BUY; // buy condition } //--- open position if possible according to signal if(signal!=WRONG_VALUE && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { double price=SymbolInfoDouble(_Symbol,(signal==ORDER_TYPE_SELL) ? SYMBOL_BID : SYMBOL_ASK); ExtTrade.PositionOpen(_Symbol,signal,InpLots,price,0,0); } } //+------------------------------------------------------------------+ //| Check for close position conditions | //+------------------------------------------------------------------+ void CheckForClose(const int predicted_class) { bool bsignal=false; //--- position already selected before long type=PositionGetInteger(POSITION_TYPE); //--- check signals if(type==POSITION_TYPE_BUY && predicted_class==PRICE_DOWN) bsignal=true; if(type==POSITION_TYPE_SELL && predicted_class==PRICE_UP) bsignal=true; //--- close position if possible if(bsignal && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { ExtTrade.PositionClose(_Symbol,3); //--- open opposite CheckForOpen(predicted_class); } } //+------------------------------------------------------------------+
Model 2023'e kadar olan fiyat verileriyle eğitildiğinden, testi 1 Ocak 2023'ten itibaren başlatalım.
Sonuç aşağıda gösterilmektedir:
Gördüğümüz gibi, model tamamen işlevseldir.
5. İkinci model sınıfı
İkinci model model.eurusd.D1.30.class.onnx olarak adlandırılır. EURUSD D1 üzerinde eğitilen sınıflandırma modeli, 30 Kapanış fiyatı ve ortalama alma periyotları 21 ve 34 olan iki basit hareketli ortalama serisinden oluşmaktadır.
//+------------------------------------------------------------------+ //| ModelEurusdD1_30Class.mqh | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "ModelSymbolPeriod.mqh" #resource "Python/model.eurusd.D1.30.class.onnx" as uchar model_eurusd_D1_30_class[] //+------------------------------------------------------------------+ //| ONNX-model wrapper class | //+------------------------------------------------------------------+ class CModelEurusdD1_30Class : public CModelSymbolPeriod { private: int m_sample_size; int m_fast_period; int m_slow_period; int m_sma_fast; int m_sma_slow; public: //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CModelEurusdD1_30Class(void) : CModelSymbolPeriod("EURUSD",PERIOD_D1) { m_sample_size=30; m_fast_period=21; m_slow_period=34; m_sma_fast=INVALID_HANDLE; m_sma_slow=INVALID_HANDLE; } //+------------------------------------------------------------------+ //| ONNX-model initialization | //+------------------------------------------------------------------+ virtual bool Init(const string symbol, const ENUM_TIMEFRAMES period) { //--- check symbol, period, create model if(!CModelSymbolPeriod::CheckInit(symbol,period,model_eurusd_D1_30_class)) { Print("model_eurusd_D1_30_class : initialization error"); return(false); } //--- since not all sizes defined in the input tensor we must set them explicitly //--- first index - batch size, second index - series size, third index - number of series (Close, MA fast, MA slow) const long input_shape[] = {1,m_sample_size,3}; if(!OnnxSetInputShape(m_handle,0,input_shape)) { Print("model_eurusd_D1_30_class : OnnxSetInputShape error ",GetLastError()); return(false); } //--- since not all sizes defined in the output tensor we must set them explicitly //--- first index - batch size, must match the batch size of the input tensor //--- second index - number of classes (up, same or down) const long output_shape[] = {1,3}; if(!OnnxSetOutputShape(m_handle,0,output_shape)) { Print("model_eurusd_D1_30_class : OnnxSetOutputShape error ",GetLastError()); return(false); } //--- indicators m_sma_fast=iMA(m_symbol,m_period,m_fast_period,0,MODE_SMA,PRICE_CLOSE); m_sma_slow=iMA(m_symbol,m_period,m_slow_period,0,MODE_SMA,PRICE_CLOSE); if(m_sma_fast==INVALID_HANDLE || m_sma_slow==INVALID_HANDLE) { Print("model_eurusd_D1_30_class : cannot create indicator"); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+ //| Predict class | //+------------------------------------------------------------------+ virtual int PredictClass(void) { static matrixf input_data(m_sample_size,3); // matrix for prepared input data static vectorf output_data(3); // vector to get result static matrix x_norm(m_sample_size,3); // matrix for prices normalize static vector vtemp(m_sample_size); static double ma_buffer[]; //--- request last bars if(!vtemp.CopyRates(m_symbol,m_period,COPY_RATES_CLOSE,1,m_sample_size)) return(-1); //--- get series Mean double m=vtemp.Mean(); //--- get series Std double s=vtemp.Std(); //--- normalize vtemp-=m; vtemp/=s; x_norm.Col(vtemp,0); //--- fast sma if(CopyBuffer(m_sma_fast,0,1,m_sample_size,ma_buffer)!=m_sample_size) return(-1); vtemp.Assign(ma_buffer); m=vtemp.Mean(); s=vtemp.Std(); vtemp-=m; vtemp/=s; x_norm.Col(vtemp,1); //--- slow sma if(CopyBuffer(m_sma_slow,0,1,m_sample_size,ma_buffer)!=m_sample_size) return(-1); vtemp.Assign(ma_buffer); m=vtemp.Mean(); s=vtemp.Std(); vtemp-=m; vtemp/=s; x_norm.Col(vtemp,2); //--- run the inference input_data.Assign(x_norm); if(!OnnxRun(m_handle,ONNX_NO_CONVERSION,input_data,output_data)) return(-1); //--- evaluate prediction return(int(output_data.ArgMax())); } }; //+------------------------------------------------------------------+
Önceki sınıfta olduğu gibi, CheckInit temel sınıf metodu Init metodunda çağrılır. Temel sınıf metodunda, ONNX modeli için bir oturum oluşturulur ve girdi ve çıktı tensörlerinin büyüklükleri açıkça ayarlanır.
PredictClass metodu, önceki 30 Kapanış fiyatından ve hesaplanan hareketli ortalamalardan oluşan bir seri sağlar. Veriler eğitimde olduğu gibi aynı şekilde normalleştirilir.
Bu modelin nasıl çalıştığını görelim. Bunu yapmak için, test Uzman Danışmanının sadece iki dizgesini değiştirelim.
#include "ModelEurusdD1_30Class.mqh" #include <Trade\Trade.mqh> input double InpLots = 1.0; // Lots amount to open position CModelEurusdD1_30Class ExtModel; CTrade ExtTrade;
Test parametreleri aynıdır.
Modelin çalıştığını görüyoruz.
6. Üçüncü model sınıfı
Son model model.eurusd.D1.63.class.onnx olarak adlandırılır. EURUSD D1 üzerinde 63 Kapanış fiyatı serisi üzerinde eğitilen sınıflandırma modeli.
//+------------------------------------------------------------------+ //| ModelEurusdD1_63.mqh | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "ModelSymbolPeriod.mqh" #resource "Python/model.eurusd.D1.63.class.onnx" as uchar model_eurusd_D1_63_class[] //+------------------------------------------------------------------+ //| ONNX-model wrapper class | //+------------------------------------------------------------------+ class CModelEurusdD1_63Class : public CModelSymbolPeriod { private: int m_sample_size; public: //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CModelEurusdD1_63Class(void) : CModelSymbolPeriod("EURUSD",PERIOD_D1,0.0001) { m_sample_size=63; } //+------------------------------------------------------------------+ //| ONNX-model initialization | //+------------------------------------------------------------------+ virtual bool Init(const string symbol, const ENUM_TIMEFRAMES period) { //--- check symbol, period, create model if(!CModelSymbolPeriod::CheckInit(symbol,period,model_eurusd_D1_63_class)) { Print("model_eurusd_D1_63_class : initialization error"); return(false); } //--- since not all sizes defined in the input tensor we must set them explicitly //--- first index - batch size, second index - series size const long input_shape[] = {1,m_sample_size}; if(!OnnxSetInputShape(m_handle,0,input_shape)) { Print("model_eurusd_D1_63_class : OnnxSetInputShape error ",GetLastError()); return(false); } //--- since not all sizes defined in the output tensor we must set them explicitly //--- first index - batch size, must match the batch size of the input tensor //--- second index - number of classes (up, same or down) const long output_shape[] = {1,3}; if(!OnnxSetOutputShape(m_handle,0,output_shape)) { Print("model_eurusd_D1_63_class : OnnxSetOutputShape error ",GetLastError()); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+ //| Predict class | //+------------------------------------------------------------------+ virtual int PredictClass(void) { static vectorf input_data(m_sample_size); // vector for prepared input data static vectorf output_data(3); // vector to get result //--- request last bars if(!input_data.CopyRates(m_symbol,m_period,COPY_RATES_CLOSE,1,m_sample_size)) return(-1); //--- get series Mean float m=input_data.Mean(); //--- get series Std float s=input_data.Std(); //--- normalize prices input_data-=m; input_data/=s; //--- run the inference if(!OnnxRun(m_handle,ONNX_NO_CONVERSION,input_data,output_data)) return(-1); //--- evaluate prediction return(int(output_data.ArgMax())); } }; //+------------------------------------------------------------------+
Bu, üç model arasında en basit olanıdır. PredictClass metodunun kodunun bu kadar kompakt olmasının nedeni budur.
Uzman Danışmandaki iki dizgeyi tekrar değiştirelim.
#include "ModelEurusdD1_63Class.mqh" #include <Trade\Trade.mqh> input double InpLots = 1.0; // Lots amount to open position CModelEurusdD1_63Class ExtModel; CTrade ExtTrade;
Testi aynı ayarlarla başlatalım.
Model çalışıyor.
7. Tüm modelleri tek bir Uzman Danışmanda birleştirme. Sert oylama
Her üç model de çalışma kapasitelerini göstermiştir. Şimdi onların çabalarını birleştirmeye çalışalım. Modellerin oylamasını ayarlayalım.
İleri bildiriler ve tanımlar
#include "ModelEurusdD1_10Class.mqh" #include "ModelEurusdD1_30Class.mqh" #include "ModelEurusdD1_63Class.mqh" #include <Trade\Trade.mqh> input double InpLots = 1.0; // Lots amount to open position CModelSymbolPeriod *ExtModels[3]; CTrade ExtTrade;
OnInit fonksiyonu
int OnInit() { ExtModels[0]=new CModelEurusdD1_10Class; ExtModels[1]=new CModelEurusdD1_30Class; ExtModels[2]=new CModelEurusdD1_63Class; for(long i=0; i<ExtModels.Size(); i++) if(!ExtModels[i].Init(_Symbol,_Period)) return(INIT_FAILED); //--- return(INIT_SUCCEEDED); }
OnTick fonksiyonu
void OnTick() { for(long i=0; i<ExtModels.Size(); i++) if(!ExtModels[i].CheckOnTick()) return; //--- predict next price movement int returned[3]={0,0,0}; //--- collect returned classes for(long i=0; i<ExtModels.Size(); i++) { int pred=ExtModels[i].PredictClass(); if(pred>=0) returned[pred]++; } //--- get one prediction for all models int predicted_class=-1; //--- count votes for predictions for(int n=0; n<3; n++) { if(returned[n]>=2) { predicted_class=n; break; } } //--- check trading according to prediction if(predicted_class>=0) if(PositionSelect(_Symbol)) CheckForClose(predicted_class); else CheckForOpen(predicted_class); }
Oy çoğunluğu <toplam oy sayısı>/2 + 1 denklemine göre hesaplanır. Toplam 3 oy için çoğunluk 2 oydur. Bu, "sert oylama" olarak adlandırılan bir durumdur.
Aynı ayarlarla test sonucu.
Her üç modelin çalışmasını, yani kârlı ve kârsız işlemlerin sayısını ayrı ayrı hatırlayalım. İlk model - 11 : 3, ikinci model - 6 : 1, üçüncü model - 16 : 10.
Görünüşe göre sert oylamanın yardımıyla sonucu iyileştirdik - 16 : 4. Ancak elbette tam raporlara ve test grafiklerine bakmamız gerekiyor.
8. Yumuşak oylama
Yumuşak oylamanın sert oylamadan farkı, dikkate alınan oy sayısı değil, her üç modelden her üç sınıfın olasılıklarının toplamı olmasıdır. Sınıf en yüksek olasılığa göre seçilir.
Yumuşak oylamayı sağlamak için bazı değişikliklerin yapılması gerekmektedir.
Temel sınıfta:
//+------------------------------------------------------------------+ //| Predict class (regression -> classification) | //+------------------------------------------------------------------+ virtual int PredictClass(vector& probabilities) { ... //--- set predicted probability as 1.0 probabilities.Fill(0); if(predicted_class<(int)probabilities.Size()) probabilities[predicted_class]=1; //--- and return predicted class return(predicted_class); }
Alt sınıflarda:
//+------------------------------------------------------------------+ //| Predict class | //+------------------------------------------------------------------+ virtual int PredictClass(vector& probabilities) { ... //--- evaluate prediction probabilities.Assign(output_data); return(int(output_data.ArgMax())); }
Uzman Danışmanda:
#include "ModelEurusdD1_10Class.mqh" #include "ModelEurusdD1_30Class.mqh" #include "ModelEurusdD1_63Class.mqh" #include <Trade\Trade.mqh> enum EnVotes { Two=2, // Two votes Three=3, // Three votes Soft=4 // Soft voting }; input double InpLots = 1.0; // Lots amount to open position input EnVotes InpVotes = Two; // Votes to make trade decision CModelSymbolPeriod *ExtModels[3]; CTrade ExtTrade;
void OnTick() { for(long i=0; i<ExtModels.Size(); i++) if(!ExtModels[i].CheckOnTick()) return; //--- predict next price movement int returned[3]={0,0,0}; vector soft=vector::Zeros(3); //--- collect returned classes for(long i=0; i<ExtModels.Size(); i++) { vector prob(3); int pred=ExtModels[i].PredictClass(prob); if(pred>=0) { returned[pred]++; soft+=prob; } } //--- get one prediction for all models int predicted_class=-1; //--- soft or hard voting if(InpVotes==Soft) predicted_class=(int)soft.ArgMax(); else { //--- count votes for predictions for(int n=0; n<3; n++) { if(returned[n]>=InpVotes) { predicted_class=n; break; } } } //--- check trading according to prediction if(predicted_class>=0) if(PositionSelect(_Symbol)) CheckForClose(predicted_class); else CheckForOpen(predicted_class); }
Test ayarları aynıdır. Girdilerde yumuşak oylamayı seçelim.
Sonuç aşağıdaki gibidir:
Kârlı işlemler - 15, kârsız işlemler - 3. Parasal açıdan da sert oylamanın yumuşak oylamadan daha iyi olduğu ortaya çıkmıştır.
Oy birliği ile, yani oy sayısı 3 olan bir oylamanın sonucuna bakalım.
Çok temkinli bir ticaret. Kârlı olmayan tek işlem testin sonunda kapatılmıştır (belki de kârsız değildir).
Önemli not: Makalede kullanılan modellerin yalnızca MQL5 dilini kullanarak ONNX modelleriyle nasıl çalışılacağını göstermek için sunulduğunu lütfen unutmayın. Uzman Danışman, gerçek hesaplarda ticaret yapmak için tasarlanmamıştır.
Sonuç
Bu makalede, nesne yönelimli programlamanın program yazmayı nasıl kolaylaştırdığını gösterdik. Modellerin tüm karmaşıklıkları sınıflarında gizlidir (modeller örnek olarak sunduğumuzdan çok daha karmaşık olabilir). "Karmaşıklığın" geri kalanı OnTick fonksiyonunun 45 dizgesinde yer almaktadır.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/12484
- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz