Optimizasyon. Birkaç Basit Fikir
Giriş
EA'nın alım satım yapması için tutarlı bir strateji bulduktan sonra, onu EURUSD grafiğinde piyasaya sürdük, öyle değil mi? Strateji olarak diğer döviz çiftleri daha karlı olabilir mi? Geometrik ilerlemede lotu artırmaya gerek kalmadan stratejinin daha iyi sonuçlar vereceği başka döviz çiftleri var mı?
EURUSD ve standart H1 zaman dilimini seçersek ve sonuçtan memnun kalmazsak, H4'te EURJPY ile değiştirirsek ne olur?
Ayrıca, sistemi test etme hızı hakkında endişelenmeyi bırakmamıza izin veren 64 bitlik bir işletim sistemimiz varsa, optimizasyon sırasında tam numaralandırmada yer alan ve nihai raporda hangi sonuçları göz ardı etmemiz gerektiğini gösteren ticaret sistemi giriş parametreleri kombinasyonlarını unutuyor muyuz?
Bu "küçük sorunları" kendi kendime çözdüm ve etkili çözümleri bu makalede paylaşıyorum. Takdir ediyorum ki yine de daha optimal çözümler olabileceğine inanıyorum.
Zaman Dilimlerine göre Optimizasyon
MQL5 eksiksiz bir zaman dilimi kümesi sağlar: M1, M2 , M3, M4,... H1, H2,... aylık grafiklere göre. Toplamda 21 zaman dilimi vardır. Ancak optimizasyon sürecinde, stratejimize en çok hangi zaman dilimlerinin uygun olduğunu bilmek istiyoruz - М1 ve М5 gibi kısa olanlar, orta olanlar - H2 ve H4 gibi veya uzun olanlar - D1 ve W1.
Başlangıçta bu seçenek çeşitliliğine ihtiyacımız yoktur. Her durumda, stratejinin М5 zaman diliminde etkili olduğunu kanıtlayabilirsek, optimizasyonun bir sonraki aşamasında М3 veya М6 üzerinde çalışıp çalışmayacağını kontrol edebiliriz.
Girdi parametresi olarak ENUM_TIMEFRAMES türünde bir değişken kullanırsak:
input ENUM_TIMEFRAMES marcoTF= PERIOD_M5;
daha sonra Optimize Edici 21 optimizasyon varyasyonu sunacaktır. Bu miktara gerçekten ihtiyacımız var mı?
Başlangıçta gerek yoktur. Optimizasyonu nasıl basitleştirebiliriz? İlk başta numaralandırmayı tanımlayabiliriz:
enum mis_MarcosTMP { _M1= PERIOD_M1, _M5= PERIOD_M5, _M15=PERIOD_M15, // _M20=PERIOD_M20, _M30=PERIOD_M30, _H1= PERIOD_H1, _H2= PERIOD_H2, _H4= PERIOD_H4, // _H8= PERIOD_H8, _D1= PERIOD_D1, _W1= PERIOD_W1, _MN1=PERIOD_MN1 };
ilgili zaman dilimlerini ekleyebileceğimiz veya silebileceğimiz yerdir. Optimizasyon için, girdi değişkenini kodun başında tanımlayın:
input mis_MarcosTMP timeframe= _H1;
ve .mqh kitaplığında yeni bir işlev tanımlayın:
//----------------------------------------- DEFINE THE TIMEFRAME ---------------------------------------------------------- ENUM_TIMEFRAMES defMarcoTiempo(mi_MARCOTMP_CORTO marco) { ENUM_TIMEFRAMES resp= _Period; switch(marco) { case _M1: resp= PERIOD_M1; break; case _M5: resp= PERIOD_M5; break; case _M15: resp= PERIOD_M15; break; //case _M20: resp= PERIOD_M20; break; case _M30: resp= PERIOD_M30; break; case _H1: resp= PERIOD_H1; break; case _H2: resp= PERIOD_H2; break; case _H4: resp= PERIOD_H4; break; //case _H8: resp= PERIOD_H8; break; case _D1: resp= PERIOD_D1; break; case _W1: resp= PERIOD_W1; break; case _MN1: resp= PERIOD_MN1; } return(resp); }
Global değişkenler kapsamında yeni bir değişken bildirin:
ENUM_TIMEFRAMES marcoTmp= defMarcoTiempo(marcoTiempo); //timeframe is defined as a global variable
"marcoTmp", gerekli bir grafik zaman dilimi tanımlamak için EA tarafından kullanılacak olan global bir değişkendir. Optimizer parametreleri tablosunda, "marcoTiempo" değişkenini başlatma aralığı tanımlanabilir. Bu М6 veya М12'yi analiz etmek için zaman ve kaynak harcamadan sadece ilgilendiğimiz adımları kapsayacaktır. Bu şekilde, EA'nın farklı zaman dilimlerindeki çalışma sonuçlarını analiz edebiliriz.
Kesinlikle, bununla tamamlanabilir
ENUM_TIMEFRAMES marcoTmp= (ENUM_TIMEFRAMES)marcoTiempo;
Bu eğer mükemmeliyetçiyseniz ve kodu basitleştirmeye çalışırken çok fazla incelerseniz, aylar, hatta yıllar süren programlamadan sonra belirginleşir. Veya bir VPS kullanıyorsanız ve bilgisayar performansını optimize ederek faturanızı minimumda tutmaya çalışıyorsanız.
Bir Sembolü veya Bir Sembol Kümesini Optimize Etme
MetaTrader 5 Strateji Test Cihazında, EA'yı MarketWatch penceresinde seçilen tüm semboller üzerinde çalıştırmayı kolaylaştıran bir optimizasyon modu vardır. Ancak bu işlev, seçilen sembol başka bir parametreymiş gibi optimizasyonun düzenlenmesine izin vermez. Yani eğer seçilen 15 sembol varsa, Test Cihazı 15 çalıştırma işlemi düzenleyecektir. EA'mız için hangi sembolün en iyisi olduğunu nasıl anlayabiliriz? Bu çoklu para birimli bir EA ise, o zaman hangi sembol grubu ve parametre kümesi ile en iyi sonucu verir? MQL5'te dize değişkenleri optimize edilmez. Nasıl yapılabilir?
Girdi parametresi değeriyle bir sembolü veya birkaç sembolü aşağıdaki şekilde kodlayın:
input int selecDePar= 0; string cadParesFX= selecPares(selecDePar);
"selecDePar" parametresi, bir dize değişkenine dönüştürülen bir optimizasyon parametresi olarak kullanılır. EA içinde "cadParesFX" değişkenini kullanın. Döviz çifti/çiftleri (bu kod için stratejinin çoklu para birimi olup olmaması önemli değildir) diğer normal optimizasyon parametreleriyle birlikte bu değişkende saklanacaktır.
//------------------------------------- SELECT THE SET OF PAIRS ------------------------------------- string selecPares(int combina= 0) { string resp="EURUSD"; switch(combina) { case 1: resp= "EURJPY"; break; case 2: resp= "USDJPY"; break; case 3: resp= "USDCHF"; break; case 4: resp= "GBPJPY"; break; case 5: resp= "GBPCHF"; break; case 6: resp= "GBPUSD"; break; case 7: resp= "USDCAD"; break; case 8: resp= "CADJPY"; break; case 9: resp= "XAUUSD"; break; case 10: resp= "EURJPY;USDJPY"; break; case 11: resp= "EURJPY;GBPJPY"; break; case 12: resp= "GBPCHF;GBPJPY"; break; case 13: resp= "EURJPY;GBPCHF"; break; case 14: resp= "USDJPY;GBPCHF"; break; case 15: resp= "EURUSD;EURJPY;GBPJPY"; break; case 16: resp= "EURUSD;EURJPY;GBPCHF"; break; case 17: resp= "EURUSD;EURJPY;USDJPY"; break; case 18: resp= "EURJPY;GBPCHF;USDJPY"; break; case 19: resp= "EURJPY;GBPUSD;GBPJPY"; break; case 20: resp= "EURJPY;GBPCHF;GBPJPY"; break; case 21: resp= "USDJPY;GBPCHF;GBPJPY"; break; case 22: resp= "EURUSD;USDJPY;GBPJPY"; break; case 23: resp= "EURUSD;EURJPY;USDJPY;GBPUSD;USDCHF;USDCAD"; break; case 24: resp= "EURUSD;EURJPY;USDJPY;GBPUSD;USDCHF;USDCAD;AUDUSD"; break; } return(resp); }
Hedefimizin ne olduğuna bağlı olarak, parite kombinasyonlarını tanımlayın ve analiz edilecek aralığı Test Cihazına bildirin. "selecDePar" parametresini 15 ila 22 aralığında optimize etmesi için Strateji Test Cihazına komut verin (aşağıdaki resme bakın). Tek bir para birimi için sonuçları karşılaştırmak istediğimizde ne yaparız? Bu durumda optimizasyonu 0'dan 9'a kadar olan aralıkta çalıştırırız.
Örneğin, EA, cadParesFX= "EURUSD;EURJPY;GBPCHF" parametresinin değerini alır. OnInit() içinde, cadParesFX parametresindeki ";" sembolü ile bölünen arrayPares[] dinamik dizisini dizelerle dolduran "cargaPares()" işlevini çağırın. Tüm global değişkenler, mümkünse sembol üzerinde yeni bir çubuk açma kontrolü de dahil olmak üzere her sembolün değerlerini kaydeden dinamik dizilere yüklenmelidir. Tek bir sembolle çalışmamız durumunda dizinin boyutları bire eşit olacaktır.
//-------------------------------- STRING CONVERSION FROM CURRENCY PAIRS INTO AN ARRAY ----------------------------------------------- int cargaPares(string cadPares, string &arrayPares[]) { //convierte "EURUSD;GBPUSD;USDJPY" a {"EURUSD", "GBPUSD", "USDJPY"}; devuelve el número de paresFX string caract= ""; int i= 0, k= 0, contPares= 1, longCad= StringLen(cadPares); if(cadPares=="") { ArrayResize(arrayPares, contPares); arrayPares[0]= _Symbol; } else { for (k= 0; k<longCad; k++) if (StringSubstr(cadPares, k, 1)==";") contPares++; ArrayResize(arrayPares, contPares); ZeroMemory(arrayPares); for(k=0; k<longCad; k++) { caract= StringSubstr(cadPares, k, 1); if (caract!=";") arrayPares[i]= arrayPares[i]+caract; else i++; } } return(contPares); }
OnInit()'te bu işlev şu şekilde uygulanır:
string ar_ParesFX[]; //array, containing names of the pairs for the EA to work with int numSimbs= 1; //variable, containing information about the number of symbols it works with int OnInit() { //... numSimbs= cargaPares(cadParesFX, ar_ParesFX); //returns the ar_ParesFX array with pairs for work in the EA //... }
numSimbs>1 ise, OnChartEvent() işlevi çağrılır. Bu çoklu para birimi sistemi ile çalışır. Aksi takdirde, OnTick() işlevi kullanılır:
void OnTick() { string simb=""; bool entrar= (nSimbs==1); if(entrar) { .../... simb= ar_ParesFX[0]; gestionOrdenes(simb); .../... } return; } //+------------------------------------------------------------------+ //| EVENT HANDLER | //+-----------------------------------------------------------------+ void OnChartEvent(const int idEvento, const long& lPeriodo, const double& dPrecio, const string &simbTick) { bool entrar= nSimbs>1 && (idEvento>=CHARTEVENT_CUSTOM); if(entrar) { .../... gestionOrdenes(simbTick); .../... } }
Bu, girdi parametresi rolündeki tüm işlevlerin en azından talep altında olan sembolü içermesi gerektiği anlamına gelir. Örneğin, Digits() işlevi yerine aşağıdakileri kullanmalıyız:
//--------------------------------- SYMBOLS OF A SYMBOL --------------------------------------- int digitosSimb(string simb= NULL) { int numDig= (int)SymbolInfoInteger(simb, SYMBOL_DIGITS); return(numDig); }
Başka bir deyişle, Symbol() veya Point() işlevlerini ve ayrıca Ask veya Bid gibi МetaТtarder 4 için geleneksel olan diğer değişkenleri unutmamız gerekir.
//----------------------------------- POINT VALUE in price (Point())--------------------------------- double valorPunto(string simb= NULL) { double resp= SymbolInfoDouble(simb, SYMBOL_POINT); return(resp); }
//--------------------------- precio ASK-BID ----------------------------------------- double precioAskBid(string simb= NULL, bool ask= true) { ENUM_SYMBOL_INFO_DOUBLE precioSolic= ask? SYMBOL_ASK: SYMBOL_BID; double precio= SymbolInfoDouble(simb, precioSolic); return(precio); }
Ayrıca, bu tür kodların mevcut olduğu çubuk açıklığı üzerindeki kontrol işlevini de unuttuk. EURUSD'de alınan tickler yeni bir çubuğun açılacağını bildiriyorsa, USDJPY tickleri sonraki 2 saniye içinde alınmayabilir. Sonuç olarak, bir sonraki USDJPY işaretinde EA, EURUSD için bu olay 2 saniye önce gerçekleşmiş olsa bile, bu sembol için yeni bir çubuğun açıldığını keşfedecektir.
//------------------------------------- NEW MULTI-CURRENCY CANDLESTICK ------------------------------------- bool nuevaVelaMD(string simb= NULL, int numSimbs= 1, ENUM_TIMEFRAMES marcoTmp= PERIOD_CURRENT) { static datetime arrayHoraNV[]; static bool primVez= true; datetime horaVela= iTime(simb, marcoTmp, 0); //received opening time of the current candlestick bool esNueva= false; int codS= buscaCadArray(simb, nombreParesFX); if(primVez) { ArrayResize(arrayHoraNV, numSimbs); ArrayInitialize(arrayHoraNV, 0); primVez= false; } esNueva= codS>=0? arrayHoraNV[codS]!= horaVela: false; if(esNueva) arrayHoraNV[codS]= horaVela; return(esNueva); }
Bu yöntem, bir optimizasyon geçişi sırasında şunları keşfetmeme olanak tanıdı:
- EA EURUSD'de iyi çalışıyor,
- EURJPY'de çok kötü çalışıyor,
- ve USDJPY'de yeterli şekilde çalışıyor
- EURUSD, GBPCHF, EURJPY paritelerinde çok iyi çalışıyor (gerçek durum).
Bu, М5 dönemi ve diğer optimizasyon parametrelerinin belirli bir kombinasyonu için geçerlidir, ancak Н1 veya Н2 için geçerli değildir.
Burada garip bir an vardır. Bununla ilgili teknik desteğe sordum. Bunun neden olduğunu bilmiyorum ama optimizasyon sonucu, Strateji Test Cihazında seçtiğimiz sembole göre değişiyor. Bu nedenle sonucu kontrol etmek için bu pariteyi strateji geliştirme boyunca sabit tutuyorum ve bunun Optimize Edicide analiz edilebilecek paritelerden biri olduğuna emin oluyorum.
Parametre Kombinasyonunun Optimizasyonu
Bazen optimizasyonda yer alan tüm parametre kombinasyonlarından bazı mantıksız olanları uygun olabilir. Bazıları stratejiyi mantıksız kılıyor. Örneğin, "maxSpread" girişinin değişkeni bir alım satım işlemi için spread kümesinin değerini tanımlarsa, bu değişkeni ortalama aracı spreadinin 30'dan az ve XAUUSD'nin 400 olduğu çeşitli pariteler için optimize ederiz. Bu pariteleri, 50'yi aşıyorsa ve XAUUSD 200'den azsa analiz etmek saçma olur. Verileri optimize ediciye ilettikten sonra, "maksSpread değerini 20 ile 0 ile 600 arasında değerlendir" olarak ayarlayın. Ancak böyle bir küme, diğer parametrelerle birlikte anlamsız olan çok sayıda kombinasyon üretir.
Önceki bölümde açıklanan formasyonu takip ederek, optimizasyon için "selecPares()" işlevinde pariteler tanımladık. EURUSD'ye 0 seçeneği atanır ve XAUUSD'ye 9 seçeneği atanır. Ardından bool global değişkenini "paramCorrect" türünde tanımlayın.
bool paramCorrect= (selecDePar<9 && maxSpread<50) || (selecDePar==9 && maxSpread>200);
Eylemi OnInit() içinde yalnızca paramCorrect doğru pozisyondaysa gerçekleştirin.
int OnInit() { ENUM_INIT_RETCODE resp= paramCorrect? INIT_SUCCEEDED: INIT_PARAMETERS_INCORRECT; if (paramCorrect) { //... nSimbs= cargaPares(cadParesFX, nombreParesFX); //return the array nombreParesFX containing pairs for work in the EA //... function of the EA initialization } return(resp); }
ParamCorrect yanlış pozisyondaysa, EA OnInit() içinde herhangi bir eylem gerçekleştirmez ve INIT_PARAMETERS_INCORRECT Strateji Test Cihazına dönüşür, bu da yanlış bir girdi veri kümesi anlamına gelir. Strateji Test Cihazı, OnInit()'ten INIT_PARAMETERS_INCORRECT değerini aldığında, o zaman bu parametre kümesi uygulama için diğer test aracılarına iletilmez ve optimizasyon sonuçlarının olduğu tablo satırı sıfırlarla doldurulur ve kırmızı ile vurgulanır (aşağıdaki resme bakın).
Programın kapanmasının nedeni, bir girdi değişkeni olarak OnDeinit()'e iletilir ve EA'nın kapanmasının nedeninin anlaşılmasına yardımcı olur. Gerçi bu farklı bir konudur.
void OnDeinit(const int motivo) { if(paramCorrect) { //functions of the program shutdown } infoDeInit(motivo); return; } //+-------------------------------------- INFORMATION ABOUT THE PROGRAM SHUTDOWN---------------------------- string infoDeInit(int codDeInit) { //informs of the reason of the program shutdown string texto= "program initialization...", text1= "CIERRE por: "; switch(codDeInit) { case REASON_PROGRAM: texto= text1+"The EA finished its work with the ExpertRemove() function"; break; //0 case REASON_ACCOUNT: texto= text1+"The account was changed"; break; //6 case REASON_CHARTCHANGE: texto= text1+"Symbol or timeframe change"; break; //3 case REASON_CHARTCLOSE: texto= text1+"The chart was closed"; break; //4 case REASON_PARAMETERS: texto= text1+"Input parameters changed by the user"; break; //5 case REASON_RECOMPILE: texto= text1+"The program was recompiled"; break; //2 case REASON_REMOVE: texto= text1+"The program was deleted from the chart"; break; //1 case REASON_TEMPLATE: texto= text1+"Another chart template was used"; break; //7 case REASON_CLOSE: texto= text1+"The terminal was closed"; break; //9 case REASON_INITFAILED: texto= text1+"The OnInit() handler returned non-zero value"; break; //8 default: texto= text1+"Other reason"; } Print(texto); return(texto); }
Gerçek şu ki, belirtilen aşamada Optimize Edici tarafından alınan parametre kümesi "paramCorrect"i yanlış olarak ayarlarsa (örneğin EURUSD spreadi 100 puana ayarlanmışsa), o zaman EA'yı çalıştırmayız ve bilgisayar kaynaklarınızın gereksiz kullanımı ve MQL5.сcommunity hesabınız için aracı kiralama masrafları olmadan da bu optimizasyon adımı sıfır haline gelir.
Elbette yukarıda bahsedilenlerin tümü OnTesterInit() ve ParameterGetRange() ve ParameterSetRange() işlevleriyle degerçekleştirilebilir, ancak açıklanan formasyon daha basit görünüyor. Bunun çalışması garanti edilirken OnTesterInit() ile gerçekleştirilen formasyon tutarlı değildir.
Sonuç
МetaТrader 5 dize değişkenlerini optimize etmesine izin vermediğinde ve EA'nın kullandığı sembol sayısını kayıtsız hale getirdiğinde, МetaТrader 5 içinde en uygun zaman dilimleri aramasını hızlandırmayı, "sembol" parametresini optimize etmeyi tartıştık. Ayrıca, bilgisayarınızın performansını koruyan ve fon tasarrufu sağlayan mantıksız girdi parametreleri kümelerini bırakarak optimizasyon adımlarının sayısını nasıl azaltabileceğinizi gösteren bir çizim de gördük.
Yukarıdaki fikirler yeni değildir ve acemi veya biraz deneyime sahip bir programcı tarafından da uygulanabilir. Bu fikirler uzun bir bilgi araştırmasının ve hata ayıklama programı kullanımının bir sonucuydu. Bunlar çok basit ama etkili fikirler. MQL5'in kar elde etmesini istiyorsam bunları neden paylaştığımı sorabilirsiniz. Cevap, programcının "yalnızlığının" üstesinden gelmektir.
İlginiz için teşekkür ederim. Sonuna kadar okuduysanız ve deneyimli bir programcıysanız lütfen beni çok sert yargılamayın.
MetaQuotes Ltd tarafından İspanyolcadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/es/articles/1052
- Ü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