Dr. Tradelove veya Ben Endişelenmeyi Nasıl Bıraktım ve Kendi Kendine Çalışan Bir Expert Advisor'ı Nasıl Yarattım?
Kavram
Expert Advisor'ı oluşturduktan sonra, optimal parametreleri seçmek için yerleşik Strateji Test Cihazı kullanmaya başvuruyoruz. Bunları seçtikten sonra Expert Advisor'ı çalıştırıyoruz ve bunda herhangi bir önemli değişiklik meydana geldiğinde, Expert Advisor durduruluyor ve Strateji Test Cihazı kullanılarak tekrar tekrar optimize ediliyor ve bu böyle devam ediyor.
Yeniden optimizasyon karar verme ve yeniden optimizasyon işlemlerini, doğal olarak işini kesintiye uğratmadan Expert Advisor'a bir süreç olarak atayabilir miyiz?
Bu sorunun çözümlerinden biri için Quantum "Uyarlanabilir Alım Satım Sistemleri ve MetaTrader5 Terminalinde Kullanımları" başlıklı makalesinde, aralarından şimdiye kadar en yüksek karı getiren bir stratejinin seçildiği birkaç (sınırsız sayıda) sanal alım satım stratejisinin yanı sıra gerçek bir alım satım sisteminin kullanılmasını önermiştir. Belirli bir sabit çubuk değeri aşıldıktan sonra alım satım stratejisini değiştirme kararı benimsenir.
Joo tarafından "Genetik Algoritmalar - Çok Kolay!" başlıklı makalesinde ortaya koyduğu genetik algoritma (GA) kodunu kullanmayı öneriyorum. Bu tür bir Expert Advisor'ın uygulamasına bir göz atalım (aşağıdaki örneklerden biri, Otomatik Alım Satım Şampiyonası 2011'e katılım için önerilen bir EA'dır).
Devam eden çalışma
Dolayısıyla Expert Advisor'ın neler yapabilmesi gerektiğini belirlememiz gerekiyor. İlk olarak, seçilen stratejiyi kullanarak alım satım işlemi yapılacağını söylemeye gerek yok. İkincisi, bir karar vermek: Yeniden optimize etme zamanının gelip gelmediği (giriş parametrelerinin yeni bir optimizasyonunu gerçekleştirmek için). Ve üçüncüsü, GA'yı kullanarak yeniden optimize etmek. Öncelikle, en basit yeniden optimizasyonu gözden geçireceğiz - Bir strateji var ve biz yalnızca yeni parametreleri seçiyoruz. Daha sonra, GA'yı kullanarak değişen bir piyasa ortamında başka bir strateji seçip seçemeyeceğimizi ve seçebiliyorsak bunun nasıl yapılabileceğini göreceğiz.
Ayrıca, uygunluk işlevinde simülasyonu kolaylaştırmak için tek bir enstrümanda yalnızca tamamlanmış çubuklarla alım satım işlemi yapma kararı alıyoruz. Ek pozisyonlar ve kısmi kapanışlar olmayacak. Sabit durdurmaları ve takip eden zarar durdurucuları kullanmayı tercih edenler, uygunluk işlevinde Zararı Durdur ve Kar Al talimatı kontrollerini uygulamak için "MetaTrader 5 Strateji Test Cihazı'nda Tick Oluşturma Algoritması" başlıklı makaleye başvurabilirler. Aşağıdaki akıllı ifadeyi genişleteceğim:
Uygunluk işlevinde, Test Cihazında "Yalnızca Açılış Fiyatları" olarak bilinen bir test modunu simüle ediyorum. ANCAK! Bunun uygunluk işlevindeki tek olası test süreci simülasyonu olduğu anlamına gelmez. Daha titiz kişiler, "Her Tick" modunu kullanarak bir uygunluk işlevi testi uygulamak isteyebilir. Tekerleği yeniden icat etmemek veya "her tick"i telafi etmemek için MetaQuotes tarafından geliştirilen mevcut bir algoritmaya dikkatlerini çekmek istiyorum. Başka bir deyişle, bu makaleyi okuduktan sonra, FF'de zararı durdurmalar ve kar almaların doğru simülasyonu için gerekli bir koşul olan uygunluk işlevinde "Her Tick" modunu simüle edebileceksiniz.
Ana noktaya - strateji uygulamasına - geçmeden önce, kısaca teknik özellikleri gözden geçirelim ve yeni bir çubuğun açılmasının yanı sıra pozisyonların açılıp kapatılmasını tanımlayan yardımcı işlevleri uygulayalım:
//+------------------------------------------------------------------+ //| Define whether a new bar has opened | //+------------------------------------------------------------------+ bool isNewBars() { CopyTime(s,tf,0,1,curBT); TimeToStruct(curBT[0],curT); if(tf==PERIOD_M1|| tf==PERIOD_M2|| tf==PERIOD_M3|| tf==PERIOD_M4|| tf==PERIOD_M5|| tf==PERIOD_M6|| tf==PERIOD_M10|| tf==PERIOD_M12|| tf==PERIOD_M15|| tf==PERIOD_M20|| tf==PERIOD_M30) if(curT.min!=prevT.min) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; if(tf==PERIOD_H1|| tf==PERIOD_H2|| tf==PERIOD_H3|| tf==PERIOD_H4|| tf==PERIOD_H6|| tf==PERIOD_H8|| tf==PERIOD_M12) if(curT.hour!=prevT.hour) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; if(tf==PERIOD_D1|| tf==PERIOD_W1) if(curT.day!=prevT.day) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; if(tf==PERIOD_MN1) if(curT.mon!=prevT.mon) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; return(false); } //+------------------------------------------------------------------+ //| ClosePosition | //+------------------------------------------------------------------+ void ClosePosition() { request.action=TRADE_ACTION_DEAL; request.symbol=PositionGetSymbol(0); if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) request.type=ORDER_TYPE_SELL; else request.type=ORDER_TYPE_BUY; request.type_filling=ORDER_FILLING_FOK; if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { request.sl=NULL; request.tp=NULL; request.deviation=100; } while(PositionsTotal()>0) { request.volume=NormalizeDouble(MathMin(PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(PositionGetSymbol(0),SYMBOL_VOLUME_MAX)),2); if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); } OrderSend(request,result); Sleep(10000); } } //+------------------------------------------------------------------+ //| OpenPosition | //+------------------------------------------------------------------+ void OpenPosition() { double vol; request.action=TRADE_ACTION_DEAL; request.symbol=s; request.type_filling=ORDER_FILLING_FOK; if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { request.sl=NULL; request.tp=NULL; request.deviation=100; } vol=MathFloor(AccountInfoDouble(ACCOUNT_FREEMARGIN)*optF*AccountInfoInteger(ACCOUNT_LEVERAGE) /(SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP)))*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP); vol=MathMax(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MIN)); vol=MathMin(vol,GetPossibleLots()*0.95); if(SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)!=0) vol=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)),2); request.volume=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2); while(PositionSelect(s)==false) { if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); } OrderSend(request,result); Sleep(10000); PositionSelect(s); } while(PositionGetDouble(POSITION_VOLUME)<vol) { request.volume=NormalizeDouble(MathMin(vol-PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2); if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); } OrderSend(request,result); Sleep(10000); PositionSelect(s); } } //+------------------------------------------------------------------+
Dikkatli bir şekilde değerlendirdikten sonra, pozisyon açma işlevinde üç önemli parametreyi fark edebilirsiniz: s ve optF değişkenleri ve GetPossibleLots() işlev çağrısı:
- s - GA tarafından optimize edilen değişkenlerden biri olan alım satım enstrümanı,
- optF - Alım satım için kullanılacak mevduatın bir kısmı (GA tarafından optimize edilmiş başka bir değişken),
- GetPossibleLots() işlevi - Alım satım için kullanılacak mevduat kısmını döndürür:
//+------------------------------------------------------------------+ //| GetPossibleLots | //+------------------------------------------------------------------+ double GetPossibleLots() { request.volume=1.0; if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); OrderCheck(request,check); return(NormalizeDouble(AccountInfoDouble(ACCOUNT_FREEMARGIN)/check.margin,2)); }
Anlatımın düzenini biraz bozarak, tüm Expert Advisor'lar için ortak olan ve İkinci aşamada gerekli olan iki işlevi daha tanıtıyoruz:
//+------------------------------------------------------------------+ //| InitRelDD | //+------------------------------------------------------------------+ void InitRelDD() { ulong DealTicket; double curBalance; prevBT[0]=D'2000.01.01 00:00:00'; TimeToStruct(prevBT[0],prevT); curBalance=AccountInfoDouble(ACCOUNT_BALANCE); maxBalance=curBalance; HistorySelect(D'2000.01.01 00:00:00',TimeCurrent()); for(int i=HistoryDealsTotal();i>0;i--) { DealTicket=HistoryDealGetTicket(i); curBalance=curBalance+HistoryDealGetDouble(DealTicket,DEAL_PROFIT); if(curBalance>maxBalance) maxBalance=curBalance; } } //+------------------------------------------------------------------+ //| GetRelDD | //+------------------------------------------------------------------+ double GetRelDD() { if(AccountInfoDouble(ACCOUNT_BALANCE)>maxBalance) maxBalance=AccountInfoDouble(ACCOUNT_BALANCE); return((maxBalance-AccountInfoDouble(ACCOUNT_BALANCE))/maxBalance); }
Burada ne görebiliriz? İlk işlev maksimum hesap bakiyesi değerini belirler, ikinci işlev hesabın göreli mevcut düşüşünü hesaplar. Özellikleri, İkinci aşamanın açıklamasında ayrıntılı olarak açıklanacaktır.
- Biri hareketli ortalamaların kesişim noktalarını kullanarak alım satım işlemi yapar (Altın Kesişim - bir enstrüman alıyoruz, Ölüm Kesişimi - satıyoruz);
- Diğeri ise son beş alım satım işlemi seansında [0..1] aralığında fiyat değişiklikleri alan basit bir nöral ağdır.
Algoritmik olarak kendi kendini optimize eden bir Expert Advisor'ın çalışması aşağıdaki gibi örneklendirilebilir:
-
Expert Advisor tarafından kullanılan değişkenlerin başlatılması: Gösterge arabelleklerini tanımlayın ve başlatın veya nöral ağ topolojisini ayarlayın (bir katmandaki katman/nöron sayısı; tüm katmanlarda nöron sayısının aynı olduğu basit bir nöral ağ örnek olarak verilebilir), çalışma zaman dilimini ayarlayın. Ayrıca, muhtemelen en önemli adıma - sırayla en önde gelen işlevi - uygunluk işlevini (bundan sonra - FF olarak anılacaktır) ele alan Genetik Optimizasyon işlevi diyoruz.
ÖNEMLİ! Her alım satım stratejisi için yeni bir FF vardır; yani her seferinde yeniden oluşturulur, örneğin tek bir hareketli ortalama için FF, iki hareketli ortalama için FF'den tamamen farklıdır ve nöral ağ FF'den önemli ölçüde farklılık gösterir.
Expert Advisor'larımdaki FF performans sonucu, göreceli düşüşün harici bir değişken olarak belirlenen kritik değeri aşmaması koşuluyla maksimum bir bakiyedir (örneklerimizde - 0,5). Başka bir deyişle, bir sonraki GA çalıştırması 100.000 bakiye veriyorsa, göreceli bakiye düşüşü -0,6 iken, FF=0,0 olur. Sizin durumunuzda sevgili Okuyucu, FF sonucu tamamen farklı kriterler getirebilir.
Genetik Algoritma performans sonuçlarını toplayın: Hareketli ortalamaların kesişimi için bunlar açıkça hareketli ortalama dönemleri olacaktır, bir nöral ağ durumunda sinaps ağırlıkları olacaktır ve her ikisi için (ve diğer Expert Advisor'larım için) ortak sonuç bir sonraki yeniden optimizasyona kadar işlem görecek bir enstrüman ve halihazırda aşina olduğumuz optF'ye, yani alım satım işlemi için kullanılacak mevduatın bir kısmı olacaktır. Kendi takdirinize bağlı olarak FF'nize optimize edilmiş parametreler eklemekte özgürsünüz; örneğin zaman dilimini veya diğer parametreleri de seçebilirsiniz...
Başlatmadaki son adım, maksimum hesap bakiyesi değerini bulmaktır. Peki bu neden önemli? Çünkü bu, yeniden optimizasyon kararı almak için bir başlangıç noktasıdır.
ÖNEMLİ! Yeniden optimizasyon kararı nasıl alınır? Göreceli BAKİYE düşüşü, harici bir değişken olarak belirlenen belirli bir kritik değere ulaştığında (örneklerimizde - 0,2), yeniden optimize etmemiz gerekir. Bir Expert Advisor'ın kritik düşüşe ulaşıldığında her çubukta yeniden optimizasyon uygulaması yapmaması için maksimum bakiye değeri mevcut bir değerle değiştirilir.
- Alım satım işlemi devam ediyor.
- Her kapatılan pozisyon üzerine, bakiye düşüşünün kritik değere ulaşıp ulaşmadığını kontrol ederiz. Kritik değere ulaşılırsa, GA'yı çalıştırır ve performans sonuçlarını toplarız (bu, yeniden optimizasyondur!)
-
Ya forex direktöründen dünyanın iflas etmemesi için bir çağrı bekliyoruz ya da (daha sık olarak) Stop Out, Marjin Çağrısı, acil ambulans...
Aşağıda, hareketli ortalamalar stratejisini (kaynak kodu da mevcuttur) ve nöral ağı kullanan Expert Advisor için yukarıdakilerin programlanmış bir uygulamasını bulabilirsiniz - tümü kaynak kodu olarak.
Kod, GPL lisansı hüküm ve koşulları kapsamında sağlanır.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { tf=Period(); //---for bar-to-bar test... prevBT[0]=D'2001.01.01'; //---... long ago TimeToStruct(prevBT[0],prevT); //--- historical depth (should be set since the optimisation is based on historical data) depth=10000; //--- copies at a time (should be set since the optimisation is based on historical data) count=2; ArrayResize(LongBuffer,count); ArrayResize(ShortBuffer,count); ArrayInitialize(LongBuffer,0); ArrayInitialize(ShortBuffer,0); //--- calling the neural network genetic optimisation function GA(); //--- getting the optimised neural network parameters and other variables GetTrainResults(); //--- getting the account drawdown InitRelDD(); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(isNewBars()==true) { bool trig=false; CopyBuffer(MAshort,0,0,count,ShortBuffer); CopyBuffer(MAlong,0,0,count,LongBuffer); if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1]) { if(PositionsTotal()>0) { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { ClosePosition(); trig=true; } } } if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1]) { if(PositionsTotal()>0) { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) { ClosePosition(); trig=true; } } } if(trig==true) { //--- if the account drawdown has exceeded the allowable value: if(GetRelDD()>maxDD) { //--- calling the neural network genetic optimisation function GA(); //--- getting the optimised neural network parameters and other variables GetTrainResults(); //--- readings of the drawdown will from now on be based on the current balance instead of the maximum balance maxBalance=AccountInfoDouble(ACCOUNT_BALANCE); } } CopyBuffer(MAshort,0,0,count,ShortBuffer); CopyBuffer(MAlong,0,0,count,LongBuffer); if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1]) { request.type=ORDER_TYPE_SELL; OpenPosition(); } if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1]) { request.type=ORDER_TYPE_BUY; OpenPosition(); } }; } //+------------------------------------------------------------------+ //| Preparing and calling the genetic optimizer | //+------------------------------------------------------------------+ void GA() { //--- number of genes (equal to the number of optimised variables), //--- all of them should be specified in the FitnessFunction()) GeneCount =OptParamCount+2; //--- number of chromosomes in a colony ChromosomeCount=GeneCount*11; //--- minimum search range RangeMinimum =0.0; //--- maximum search range RangeMaximum =1.0; //--- search pitch Precision =0.0001; //--- 1 is a minimum, anything else is a maximum OptimizeMethod =2; ArrayResize(Chromosome,GeneCount+1); ArrayInitialize(Chromosome,0); //--- number of epochs without any improvement Epoch =100; //--- ratio of replication, natural mutation, artificial mutation, gene borrowing, //--- crossingover, interval boundary displacement ratio, every gene mutation probabilty, % UGA(100.0,1.0,1.0,1.0,1.0,0.5,1.0); } //+------------------------------------------------------------------+ //| Fitness function for neural network genetic optimizer: | //| selecting a pair, optF, synapse weights; | //| anything can be optimised but it is necessary | //| to carefully monitor the number of genes | //+------------------------------------------------------------------+ void FitnessFunction(int chromos) { int b; //--- is there an open position? bool trig=false; //--- direction of an open position string dir=""; //--- opening price double OpenPrice=0; //--- intermediary between a gene colony and optimised parameters int z; //--- current balance double t=cap; //--- maximum balance double maxt=t; //--- absolute drawdown double aDD=0; //--- relative drawdown double rDD=0.000001; //--- fitness function proper double ff=0; //--- GA is selecting a pair z=(int)MathRound(Colony[GeneCount-1][chromos]*12); switch(z) { case 0: {s="AUDUSD"; break;}; case 1: {s="AUDUSD"; break;}; case 2: {s="EURAUD"; break;}; case 3: {s="EURCHF"; break;}; case 4: {s="EURGBP"; break;}; case 5: {s="EURJPY"; break;}; case 6: {s="EURUSD"; break;}; case 7: {s="GBPCHF"; break;}; case 8: {s="GBPJPY"; break;}; case 9: {s="GBPUSD"; break;}; case 10: {s="USDCAD"; break;}; case 11: {s="USDCHF"; break;}; case 12: {s="USDJPY"; break;}; default: {s="EURUSD"; break;}; } MAshort=iMA(s,tf,(int)MathRound(Colony[1][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); MAlong =iMA(s,tf,(int)MathRound(Colony[2][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); dig=MathPow(10.0,(double)SymbolInfoInteger(s,SYMBOL_DIGITS)); //--- GA is selecting the optimal F optF=Colony[GeneCount][chromos]; leverage=AccountInfoInteger(ACCOUNT_LEVERAGE); contractSize=SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE); b=MathMin(Bars(s,tf)-1-count-MaxMAPeriod,depth); //--- for a neural network using historical data - where the data is copied from for(from=b;from>=1;from--) { CopyBuffer(MAshort,0,from,count,ShortBuffer); CopyBuffer(MAlong,0,from,count,LongBuffer); if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1]) { if(trig==false) { CopyOpen(s,tf,from,count,o); OpenPrice=o[1]; dir="SELL"; trig=true; } else { if(dir=="BUY") { CopyOpen(s,tf,from,count,o); if(t>0) t=t+t*optF*leverage*(o[1]-OpenPrice)*dig/contractSize; else t=0; if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t; if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt; OpenPrice=o[1]; dir="SELL"; trig=true; } } } if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1]) { if(trig==false) { CopyOpen(s,tf,from,count,o); OpenPrice=o[1]; dir="BUY"; trig=true; } else { if(dir=="SELL") { CopyOpen(s,tf,from,count,o); if(t>0) t=t+t*optF*leverage*(OpenPrice-o[1])*dig/contractSize; else t=0; if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t; if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt; OpenPrice=o[1]; dir="BUY"; trig=true; } } } } if(rDD<=trainDD) ff=t; else ff=0.0; AmountStartsFF++; Colony[0][chromos]=ff; } //+---------------------------------------------------------------------+ //| getting the optimized neural network parameters and other variables | //| should always be equal to the number of genes | //+---------------------------------------------------------------------+ void GetTrainResults() { //--- intermediary between a gene colony and optimised parameters int z; MAshort=iMA(s,tf,(int)MathRound(Chromosome[1]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); MAlong =iMA(s,tf,(int)MathRound(Chromosome[2]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); CopyBuffer(MAshort,0,from,count,ShortBuffer); CopyBuffer(MAlong,0,from,count,LongBuffer); //--- save the best pair z=(int)MathRound(Chromosome[GeneCount-1]*12); switch(z) { case 0: {s="AUDUSD"; break;}; case 1: {s="AUDUSD"; break;}; case 2: {s="EURAUD"; break;}; case 3: {s="EURCHF"; break;}; case 4: {s="EURGBP"; break;}; case 5: {s="EURJPY"; break;}; case 6: {s="EURUSD"; break;}; case 7: {s="GBPCHF"; break;}; case 8: {s="GBPJPY"; break;}; case 9: {s="GBPUSD"; break;}; case 10: {s="USDCAD"; break;}; case 11: {s="USDCHF"; break;}; case 12: {s="USDJPY"; break;}; default: {s="EURUSD"; break;}; } //--- saving the best optimal F optF=Chromosome[GeneCount]; } //+------------------------------------------------------------------+
Algoritmanın ana işlevine, (uygunluk işlevine) bakalım.
Kendi kendini optimize eden bir Expert Advisor'ın ardındaki tüm fikir, Genetik Algoritmadan (GA işlevi()) optimize edilmiş değişkenlerin girdisini alan uygunluk işlevinde belirli bir süre içinde (örneğin, 10000 barlık bir geçmiş) alım satım sürecinin simülasyonuna ( Test Cihazında olduğu gibi) dayanır. Hareketli ortalamaların kesişimine dayalı bir algoritma olması durumunda, optimize edilmiş değişkenler şunları içerir:
- Enstrüman (forex'te - bir para birimi çifti); evet, tipik bir çok para birimli Expert Advisor'dır ve Genetik Algoritma bir enstrüman seçer (kod Şampiyonaya katılım için önerilen Expert Advisor'dan alındığı için, sahip olduğu çiftler Şampiyona para birimi çiftlerine karşılık gelir; genel olarak bir broker tarafından fiyat teklifi verilen herhangi bir enstrüman olabilir).
Not: Ne yazık ki, Expert Advisor test modunda MarketWatch penceresinden çift listesini alamıyor (burada MetaQuotes kullanıcıları sayesinde bunu açıklığa kavuşturduk - Yok artık!). Bu nedenle Test Cihazında Expert Advisor'ı forex ve hisseler için ayrı ayrı çalıştırmak istiyorsanız, FF ve GetTrainResults() işlevinde kendi enstrümanlarınızı belirtmeniz yeterlidir.
- alım satım için kullanılacak mevduatın bir kısmı;
- iki hareketli ortalamanın dönemleri.
Aşağıdaki örneklerde, TEST İÇİN Expert Advisor'ın bir çeşidi gösterilmiştir!
GERÇEK ALIM SATIM İŞLEMİ kodu, Piyasa İzleme penceresindeki enstrüman listesi kullanılarak önemli ölçüde basitleştirilebilir.
Bunu FF ve GetTrainResults() işlevinde yapmak için "//--- GA bir çift seçiyor" ve "//--- en iyi çifti kaydediyor" yorumlarıyla yazmanız yeterlidir:
//--- GA is selecting a pair z=(int)MathRound(Colony[GeneCount-1][chromos]*(SymbolsTotal(true)-1)); s=SymbolName(z,true);
Bu nedenle, FF'nin başında, gerektiğinde, geçmişe dayalı alım satımın simülasyonu için değişkenleri belirler ve başlatırız. Bir sonraki aşamada Genetik Algoritmadan optimize edilmiş değişkenlerin farklı değerlerini toplarız; örneğin bu satırdan "optF=Colony[GeneCount][chromos]; mevduat kısmı değeri GA'dan FF'ye aktarılır.
Ayrıca geçmişteki mevcut çubuk sayısını da kontrol ederiz ve 10000'inci çubuktan veya ilk kullanılabilir çubuktan başlayarak "Yalnızca açılış fiyatları" modunda fiyat teklifi alma ve alım satım kararları verme sürecini simüle ederiz:
- Hareketli ortalamaların değerlerini arabelleklere kopyalayın;
- Bunun bir Ölüm Kesişimi olup olmadığını kontrol edin.
- Ölüm kesişimi varsa ve açık pozisyon yoksa (if(trig==false)) - sanal bir SAT pozisyonu açın (açılış fiyatını ve yönünü unutmayın);
- Bir Ölüm Kesişimi ve açık bir AL pozisyonu varsa (if(dir=="BUY")) - çubuk açılış fiyatını alın ve aşağıdaki çok önemli üç satıra dikkat edin:
- Bir pozisyonun kapanmasını ve bakiyedeki değişikliği simüle eder: cari bakiye, mevduatın işlem görecek kısmı ile çarpılan, açılış ve kapanış fiyatları arasındaki farkla çarpılan ve pip fiyatı (kaba) ile çarpılan bir cari bakiye değeri kadar artırılır;
- Mevcut bakiyenin alım satım simülasyonu geçmişi boyunca maksimuma ulaşıp ulaşmadığını kontrol edin; ulaşmadıysa, paradaki maksimum bakiye düşüşünü hesaplayın;
- Daha önce hesaplanan para düşüşünü göreceli bakiye düşüşüne dönüştürün;
- Sanal bir SAT pozisyonu açın (açılış fiyatını ve yönünü unutmayın);
- benzer kontrolleri ve hesaplamaları Altın Kesişim için de yapın.
Sanal alım satım işleminin mevcut tüm geçmişini ve simülasyonunu gözden geçirdikten sonra, nihai FF değerini hesaplayın: Hesaplanan göreceli bakiye düşüşü, test için ayarlanandan azsa, FF=bakiye, aksi takdirde FF=0'dır. Genetik Algoritma, uygunluk işlevinin maksimize edilmesini amaçlar!
Sonuç olarak, Genetik Algoritma, enstrümanların çeşitli değerlerini, mevduat kısımlarını ve hareketli ortalamaların dönemlerini vererek, minimum (minimum, kullanıcı tarafından belirlenir) göreceli düşüşte bakiyeyi maksimize eden değerleri bulacaktır.
Sonuç
Kısa sonuç şu şekildedir: Kendi kendini eğiten bir Expert Advisor oluşturmak kolaydır, zor kısım ne girileceğini bulmaktır (önemli olan fikirdir, uygulama yalnızca teknik bir konudur).
Kötümserlerin "İşe yarıyor mu?" sorusuna bir cevabım var - Evet, işe yarıyor; iyimserlere sözümse - Bu bir Kutsal Kase değil.
Önerilen yöntem ile Quantum'un yöntemi arasındaki temel fark nedir? Bu, MA'ları kullanan Expert Advisor'ları karşılaştırarak en iyi şekilde örneklendirilebilir:
- Uyarlanabilir Alım Satım Sistemi'nde MA'ların dönemlerine ilişkin karar, derlemeden önce ve sıkı bir şekilde kodlanmalıdır ve seçim ancak bu sınırlı sayıdaki varyantlar arasından yapılabilir; Genetik Olarak Optimize Edilmiş Expert Advisor'da derleme öncesi dönemler hakkında herhangi bir karar almıyoruz; bu karar GA tarafından alınacak, varyant sayısı ise yalnızca sağduyu ile sınırlıdır.
- Uyarlanabilir Alım Satım Sistemi'nde sanal alım satım çubuktan çubuğadır; bu, Genetik Olarak Optimize Edilmiş Expert Advisor'da nadiren böyledir - ve daha sonra yalnızca yeniden optimizasyon için koşulların ortaya çıkması üzerine. Artan sayıda strateji, parametre ve enstrüman üzerindeki bilgisayar performansı, Uyarlanabilir Alım Satım Sistemi için sınırlayıcı bir faktör olabilir.
Ek
Nöral ağın, 01.01.2010 tarihinden itibaren herhangi bir optimizasyon olmadan ve günlük grafiklere dayalı olarak Test Cihazında çalıştırılması durumunda elde ettiğimiz sonuçlar şu şekildedir:
Strateji Test Cihazı Raporu | ||||||||||||
MetaQuotes-Demo (Derleme 523) | ||||||||||||
Expert Advisor: | ANNExample | |||||||||||
Sembol: | EURUSD | |||||||||||
Dönem: | Günlük (01.01.2010 - 30.09.2011) | |||||||||||
Giriş parametreleri: | trainDD=0,9 | |||||||||||
maxDD=0,1 | ||||||||||||
Aracı: | Alpari NZ Limited | |||||||||||
Para birimi: | USD | |||||||||||
İlk para yatırma: | 10 000.00 | |||||||||||
Kaldıraç: | 1:100 | |||||||||||
Sonuçlar | ||||||||||||
Geçmiş kalitesi: | %100 | |||||||||||
Çubuklar: | 454 | Tickler: | 2554879 | |||||||||
Toplam net kar: | -9 094.49 | Brüt kar: | 29 401.09 | Brüt zarar: | -38 495.58 | |||||||
Kar faktörü: | 0,76 | Beklenen kazanç: | -20,53 | Marj düzeyi: | %732,30 | |||||||
Kurtarma faktörü: | -0,76 | Sharpe oranı: | -0,06 | OnTester sonucu: | 0 | |||||||
Bakiye düşüşü: | ||||||||||||
Mutlak bakiye düşüşü: | 9 102,56 | Maksimum bakiye düşüşü: | 11 464.70 (%92,74) | Göreceli bakiye düşüşü: | %92,74 (11 464.70) | |||||||
Hisse senedi düşüşü: | ||||||||||||
Mutlak hisse senedi düşüşü: | 9 176.99 | Maksimum hisse senedi düşüşü: | 11 904.00 (%93,53) | Göreceli hisse senedi düşüşü: | %93,53 (11 904.00) | |||||||
Toplam alım satım işlemi: | 443 | Kısa alım satımlar (kazanan, %): | 7 (%14,29) | Uzun alım satımlar (kazanan, %): | 436 (%53,44) | |||||||
Toplam yatırım: | 886 | Kar alım satımları, (toplam %) | 234 (%52,82) | Zarar alım satımları (toplam %): | 209 (%47,18) | |||||||
En büyük kar alım satımı: | 1 095.57 | En büyük zarar alım satımı: | -1 438.85 | |||||||||
Ortalama kar alım satımı: | 125.65 | Ortalama zarar alım satımı: | -184.19 | |||||||||
Maks. ardışık kazanç (para cinsinden kar): | 8 (397,45) | Maks. ardışık zarar (para cinsinden zarar): | 8 (-1 431.44) | |||||||||
Maks. ardışık kar (kazanç sayısı): | 1 095.57 (1) | Maks. ardışık zarar (zarar sayısı): | -3 433.21 (6) | |||||||||
Ortalama ardışık kazanç: | 2 | Maksimum ardışık zararlar: | 2 |
ve aşağıda, aralarından seçim yapabileceğiniz üç yeniden optimizasyon çeşidi bulunmaktadır:
ilk...
Zaman | Yatırım | Sembol | Tür | Yön | Hacim | Fiyat | Talimat | Swap | Kar | Bakiye |
2010.01.01 00:00 | 1 | bakiye | 0.00 | 10 000.00 | 10 000.00 | |||||
2010.01.04 00:00 | 2 | AUDUSD | alış | giriş | 0.90 | 0.89977 | 2 | 0.00 | 0.00 | 10 000.00 |
2010.01.05 00:00 | 3 | AUDUSD | satış | çıkış | 0.90 | 0.91188 | 3 | 5.67 | 1 089.90 | 11 095.57 |
2010.01.05 00:00 | 4 | AUDUSD | alış | giriş | 0.99 | 0.91220 | 4 | 0.00 | 0.00 | 11 095.57 |
2010.01.06 00:00 | 5 | AUDUSD | satış | çıkış | 0.99 | 0.91157 | 5 | 6.24 | -62.37 | 11 039.44 |
2010.01.06 00:00 | 6 | AUDUSD | alış | giriş | 0.99 | 0.91190 | 6 | 0.00 | 0.00 | 11 039.44 |
2010.01.07 00:00 | 7 | AUDUSD | satış | çıkış | 0.99 | 0.91924 | 7 | 18.71 | 726.66 | 11 784.81 |
ikinci...
Zaman | Yatırım | Sembol | Tür | Yön | Hacim | Fiyat | Talimat | Komisyon | Swap | Kar | Bakiye |
2010.05.19 00:00 | 189 | AUDUSD | satış | çıkış | 0.36 | 0.86110 | 189 | 0.00 | 2.27 | -595.44 | 4 221.30 |
2010.05.19 00:00 | 190 | EURAUD | satış | giriş | 0.30 | 1.41280 | 190 | 0.00 | 0.00 | 0.00 | 4 221.30 |
2010.05.20 00:00 | 191 | EURAUD | alış | çıkış | 0.30 | 1.46207 | 191 | 0.00 | 7.43 | -1 273.26 | 2 955.47 |
2010.05.20 00:00 | 192 | AUDUSD | alış | giriş | 0.21 | 0.84983 | 192 | 0.00 | 0.00 | 0.00 | 2 955.47 |
üçüncü
Zaman | Yatırım | Sembol | Tür | Yön | Hacim | Fiyat | Talimat | Swap | Kar | Bakiye |
2010.06.16 00:00 | 230 | GBPCHF | alış | giriş | 0.06 | 1.67872 | 230 | 0.00 | 0.00 | 2 128.80 |
2010.06.17 00:00 | 231 | GBPCHF | satış | çıkış | 0.06 | 1.66547 | 231 | 0.13 | -70.25 | 2 058.68 |
2010.06.17 00:00 | 232 | GBPCHF | alış | giriş | 0.06 | 1.66635 | 232 | 0.00 | 0.00 | 2 058.68 |
2010.06.18 00:00 | 233 | GBPCHF | satış | çıkış | 0.06 | 1.64705 | 233 | 0.04 | -104.14 | 1 954.58 |
2010.06.18 00:00 | 234 | AUDUSD | alış | giriş | 0.09 | 0.86741 | 234 | 0.00 | 0.00 | 1 954.58 |
2010.06.21 00:00 | 235 | AUDUSD | satış | çıkış | 0.09 | 0.87184 | 235 | 0.57 | 39.87 | 1 995.02 |
2010.06.21 00:00 | 236 | AUDUSD | alış | giriş | 0.09 | 0.88105 | 236 | 0.00 | 0.00 | 1 995.02 |
2010.06.22 00:00 | 237 | AUDUSD | satış | çıkış | 0.09 | 0.87606 | 237 | 0.57 | -44.91 | 1 950.68 |
2010.06.22 00:00 | 238 | AUDUSD | alış | giriş | 0.09 | 0.87637 | 238 | 0.00 | 0.00 | 1 950.68 |
2010.06.23 00:00 | 239 | AUDUSD | satış | çıkış | 0.09 | 0.87140 | 239 | 0.57 | -44.73 | 1 906.52 |
2010.06.23 00:00 | 240 | AUDUSD | alış | giriş | 0.08 | 0.87197 | 240 | 0.00 | 0.00 | 1 906.52 |
2010.06.24 00:00 | 241 | AUDUSD | satış | çıkış | 0.08 | 0.87385 | 241 | 1.51 | 15.04 | 1 923.07 |
2010.06.24 00:00 | 242 | AUDUSD | alış | giriş | 0.08 | 0.87413 | 242 | 0.00 | 0.00 | 1 923.07 |
2010.06.25 00:00 | 243 | AUDUSD | satış | çıkış | 0.08 | 0.86632 | 243 | 0.50 | -62.48 | 1 861.09 |
2010.06.25 00:00 | 244 | AUDUSD | alış | giriş | 0.08 | 0.86663 | 244 | 0.00 | 0.00 | 1 861.09 |
2010.06.28 00:00 | 245 | AUDUSD | satış | çıkış | 0.08 | 0.87375 | 245 | 0.50 | 56.96 | 1 918.55 |
2010.06.28 00:00 | 246 | AUDUSD | alış | giriş | 0.08 | 0.87415 | 246 | 0.00 | 0.00 | 1 918.55 |
2010.06.29 00:00 | 247 | AUDUSD | satış | çıkış | 0.08 | 0.87140 | 247 | 0.50 | -22.00 | 1 897.05 |
2010.06.29 00:00 | 248 | AUDUSD | alış | giriş | 0.08 | 0.87173 | 248 | 0.00 | 0.00 | 1 897.05 |
2010.07.01 00:00 | 249 | AUDUSD | satış | çıkış | 0.08 | 0.84053 | 249 | 2.01 | -249.60 | 1 649.46 |
2010.07.01 00:00 | 250 | EURGBP | satış | giriş | 0.07 | 0.81841 | 250 | 0.00 | 0.00 | 1 649.46 |
2010.07.02 00:00 | 251 | EURGBP | alış | çıkış | 0.07 | 0.82535 | 251 | -0.04 | -73.69 | 1 575.73 |
2010.07.02 00:00 | 252 | EURGBP | satış | giriş | 0.07 | 0.82498 | 252 | 0.00 | 0.00 | 1 575.73 |
2010.07.05 00:00 | 253 | EURGBP | alış | çıkış | 0.07 | 0.82676 | 253 | -0.04 | -18.93 | 1 556.76 |
2010.07.05 00:00 | 254 | EURGBP | satış | giriş | 0.06 | 0.82604 | 254 | 0.00 | 0.00 | 1 556.76 |
2010.07.06 00:00 | 255 | EURGBP | alış | çıkış | 0.06 | 0.82862 | 255 | -0.04 | -23.43 | 1 533.29 |
Not Ön çalışma olarak: Yalnızca belirli bir sistemin parametrelerini seçmek değil, aynı zamanda belirli bir anda piyasaya en uygun sistemi seçmek (ipucu - sistem bankasından).
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/334
- Ücretsiz ticaret 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