序言
很久以前,一个论坛(MQL5)发表了两篇文章:“遗传算法-简单!(作者Joo)和“TradeLove博士…”(作者是我)。在第一篇文章中,作者为我们提供了一个强大的工具来优化您需要的任何东西,包括交易策略,一个在MQL5中实现的遗传算法。
在第二篇文章中,我尝试利用该算法开发一个自寻优的EA事务。本文最后对下面描述的任务进行了详细的描述:创建EA事务(当然是自优化),它不仅可以选择特定交易系统的最佳参数,而且可以选择所有已建立策略的最佳策略。让我们看看是否有可能,如果有,怎么做。
贸易机器人编年史
首先,我们制定了自优化EA事务的一般要求。
它应该能够(基于历史数据):
- 从目标中选择最佳策略
- 选择最佳金融工具
- 选择具有杠杆修正的交易的最佳存款金额
- 选择战略中指标的最佳参数
此外,在实际交易中,它应该能够:
- 建立仓库和放置仓库
- 选择仓库的大小
- 决定是否需要新的优化
下图显示了建议的EA事务的示意图。
带有边界的详细信息位于所附的文件方案中。
记住,掌握无穷大是不可能的。我们在EA事务逻辑中引入了限制。我们相信(重要的):
- EA交易需要在新支柱出现时做出交易决策(在我们选择的任何时候)。
- 根据但不限于P.1,EA交易将只根据指标信号关闭交易,而不使用利润、止损和相应的跟踪止损。
- 开始新的优化的条件是,在级别初始化期间,余额损失高于预设值。请注意,这只是我的个人情况,你们每个人都可以选择自己的具体情况。
- 一个适应度函数模型根据历史条件进行交易,在模拟交易余额相对损失低于某一预设水平的前提下,使模型余额最大化。还应该注意的是,这只是我的个人健身功能,你可以选择自己的具体健身功能。
- 我们将指标缓冲区中要优化的参数数量限制为5个,除了三个常规参数(策略、工具和存款)。该限制在逻辑上遵循具有内置技术指标的索引缓冲区的最大数量。如果您想要描述一种使用大型索引缓冲区的自定义度量的策略,您只需要将main.mq5文件中的optparamcount变量更改为所需的数字。
既然指定了需求并选择了限制,您就可以看到实现所有这些功能的代码。
我们从所有起作用的功能开始。
void OnTick() { if(isNewBars()==true) { trig=false; switch(strat) { case 0: {trig=NeedCloseMA() ; break;}; //The number of case strings must be equal to the number of strategies case 1: {trig=NeedCloseSAR() ; break;}; case 2: {trig=NeedCloseStoch(); break;}; default: {trig=NeedCloseMA() ; break;}; } if(trig==true) { if(GetRelDD()>maxDD) //If a balance drawdown is above the max allowed value: { GA(); //Call the genetic optimization function GetTrainResults(); //Get the optimized parameters maxBalance=AccountInfoDouble(ACCOUNT_BALANCE); //Now count the drawdown not from the balance maximum... //...but from the current balance } } switch(strat) { case 0: {trig=NeedOpenMA() ; break;}; //The number of case strings must be equal to the number of strategies case 1: {trig=NeedOpenSAR() ; break;}; case 2: {trig=NeedOpenStoch(); break;}; default: {trig=NeedOpenMA() ; break;}; } Print(TimeToString(TimeCurrent()),";","Main:OnTick:isNewBars(true)", ";","strat=",strat); } }
这里是什么?如图所示,我们查看每个价格变化,不管是否有新的列。如果有一个新的列,我们调用它的特定函数来检查是否有一个打开的位置,如果必要的话,在我们知道我们选择的策略时关闭这个位置。假设最佳突破策略为sar,则需要分别调用closesar函数:
bool NeedCloseSAR() { CopyBuffer(SAR,0,0,count,SARBuffer); CopyOpen(s,tf,0,count,o); Print(TimeToString(TimeCurrent()),";","StrategySAR:NeedCloseSAR", ";","SAR[0]=",SARBuffer[0],";","SAR[1]=",SARBuffer[1],";","Open[0]=",o[0],";","Open[1]=",o[1]); if((SARBuffer[0]>o[0]&&SARBuffer[1]<o[1])|| (SARBuffer[0]<o[0]&&SARBuffer[1]>o[1])) { if(PositionsTotal()>0) { ClosePosition(); return(true); } } return(false); }
任何关闭函数都必须是布尔值,并且在关闭时返回true。这允许ontick()函数的下一个代码块确定是否需要新的优化:
if(trig==true) { if(GetRelDD()>maxDD) //If the balance drawdown is above the max allowed one: { GA(); //Call the genetic optimization function GetTrainResults(); //Get optimized parameters maxBalance=AccountInfoDouble(ACCOUNT_BALANCE); //Now count the drawdown not from the balance maximum... //...but from the current balance } }
求出当前余额损失,并与最大允许损失进行比较。如果超过最大值,将运行新的优化(GA())。GA()函数依次调用gamodule的适应度函数(int chromos)。MQH模块,EA事务的核心:
void FitnessFunction(int chromos) //A fitness function for the genetic optimizer:... //...selects a strategy, symbol, deposit share,... //...parameters of indicator buffers;... //...you can optimize whatever you need, but... //...watch carefully the number of genes { double ff=0.0; //The fitness function strat=(int)MathRound(Colony[GeneCount-2][chromos]*StratCount); //GA selects a strategy //For EA testing mode use the following code... z=(int)MathRound(Colony[GeneCount-1][chromos]*3); //GA selects a symbol switch(z) { case 0: {s="EURUSD"; break;}; case 1: {s="GBPUSD"; break;}; case 2: {s="USDCHF"; break;}; case 3: {s="USDJPY"; break;}; default: {s="EURUSD"; break;}; } //..for real mode, comment the previous code and uncomment the following one (symbols are selected in the MarketWatch window) /* z=(int)MathRound(Colony[GeneCount-1][chromos]*(SymbolsTotal(true)-1));//GA selects a symbol s=SymbolName(z,true); */ optF=Colony[GeneCount][chromos]; //GA selects a deposit share switch(strat) { case 0: {ff=FFMA( Colony[1][chromos], //The number of case strings must be equal to the number of strategies Colony[2][chromos], Colony[3][chromos], Colony[4][chromos], Colony[5][chromos]); break;}; case 1: {ff=FFSAR( Colony[1][chromos], Colony[2][chromos], Colony[3][chromos], Colony[4][chromos], Colony[5][chromos]); break;}; case 2: {ff=FFStoch(Colony[1][chromos], Colony[2][chromos], Colony[3][chromos], Colony[4][chromos], Colony[5][chromos]); break;}; default: {ff=FFMA( Colony[1][chromos], Colony[2][chromos], Colony[3][chromos], Colony[4][chromos], Colony[5][chromos]); break;}; } AmountStartsFF++; Colony[0][chromos]=ff; Print(TimeToString(TimeCurrent()),";","GAModule:FitnessFunction", ";","strat=",strat,";","s=",s,";","optF=",optF, ";",Colony[1][chromos],";",Colony[2][chromos],";",Colony[3][chromos],";",Colony[4][chromos],";",Colony[5][chromos]); }
根据当前选择的策略,将调用特定策略的适应度函数计算模块。例如,如果GA选择随机度量,则将调用ffstoch(),并将指标缓冲区的最佳参数传递给它:
double FFStoch(double par1,double par2,double par3,double par4,double par5) { int b; bool FFtrig=false; //Is there an open position? string dir=""; //Direction of the open position double OpenPrice; //Position Open price double t=cap; //Current balance double maxt=t; //Maximum balance double aDD=0.0; //Absolute drawdown double rDD=0.000001; //Relative drawdown Stoch=iStochastic(s,tf,(int)MathRound(par1*MaxStochPeriod)+1, (int)MathRound(par2*MaxStochPeriod)+1, (int)MathRound(par3*MaxStochPeriod)+1,MODE_SMA,STO_CLOSECLOSE); StochTopLimit =par4*100.0; StochBottomLimit=par5*100.0; dig=MathPow(10.0,(double)SymbolInfoInteger(s,SYMBOL_DIGITS)); leverage=AccountInfoInteger(ACCOUNT_LEVERAGE); contractSize=SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE); b=MathMin(Bars(s,tf)-1-count-MaxMAPeriod,depth); for(from=b;from>=1;from--) //Where to start copying of history { CopyBuffer(Stoch,0,from,count,StochBufferMain); CopyBuffer(Stoch,1,from,count,StochBufferSignal); if((StochBufferMain[0]>StochBufferSignal[0]&&StochBufferMain[1]<StochBufferSignal[1])|| (StochBufferMain[0]<StochBufferSignal[0]&&StochBufferMain[1]>StochBufferSignal[1])) { if(FFtrig==true) { 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; } 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; } FFtrig=false; } } if(StochBufferMain[0]>StochBufferSignal[0]&&StochBufferMain[1]<StochBufferSignal[1]&&StochBufferMain[1]>StochTopLimit) { CopyOpen(s,tf,from,count,o); OpenPrice=o[1]; dir="SELL"; FFtrig=true; } if(StochBufferMain[0]<StochBufferSignal[0]&&StochBufferMain[1]>StochBufferSignal[1]&&StochBufferMain[1]<StochBottomLimit) { CopyOpen(s,tf,from,count,o); OpenPrice=o[1]; dir="BUY"; FFtrig=true; } } Print(TimeToString(TimeCurrent()),";","StrategyStoch:FFStoch", ";","K=",(int)MathRound(par1*MaxStochPeriod)+1,";","D=",(int)MathRound(par2*MaxStochPeriod)+1, ";","Slow=",(int)MathRound(par3*MaxStochPeriod)+1,";","TopLimit=",StochTopLimit,";","BottomLimit=",StochBottomLimit, ";","rDD=",rDD,";","Cap=",t); if(rDD<=trainDD) return(t); else return(0.0); }
随机指数的适应度函数将一个模拟的平衡返回给主函数,然后传递给遗传算法。GA将决定在某一点结束优化,同时我们将使用gettrainresults()函数返回策略的最佳当前值(如移动平均值)、交易的种类、存款份额和基础程序索引缓冲区的参数,并为进一步的实际交易创建指标:
void GetTrainResults() //Get the best parameters { strat=(int)MathRound(Chromosome[GeneCount-2]*StratCount); //Remember the best strategy //For EA testing mode use the following code... z=(int)MathRound(Chromosome[GeneCount-1]*3); //Remember the best symbol switch(z) { case 0: {s="EURUSD"; break;}; case 1: {s="GBPUSD"; break;}; case 2: {s="USDCHF"; break;}; case 3: {s="USDJPY"; break;}; default: {s="EURUSD"; break;}; } //...for real mode, comment the previous code and uncomment the following one (symbols are selected in the MarketWatch window) /* z=(int)MathRound(Chromosome[GeneCount-1]*(SymbolsTotal(true)-1)); //Remember the best symbol s=SymbolName(z,true); */ optF=Chromosome[GeneCount]; //Remember the best deposit share switch(strat) { case 0: {GTRMA( Chromosome[1], //The number of case strings must be equal to the number of strategies Chromosome[2], Chromosome[3], Chromosome[4], Chromosome[5]) ; break;}; case 1: {GTRSAR( Chromosome[1], Chromosome[2], Chromosome[3], Chromosome[4], Chromosome[5]) ; break;}; case 2: {GTRStoch(Chromosome[1], Chromosome[2], Chromosome[3], Chromosome[4], Chromosome[5]) ; break;}; default: {GTRMA( Chromosome[1], Chromosome[2], Chromosome[3], Chromosome[4], Chromosome[5]) ; break;}; } Print(TimeToString(TimeCurrent()),";","GAModule:GetTrainResults", ";","strat=",strat,";","s=",s,";","optF=",optF, ";",Chromosome[1],";",Chromosome[2],";",Chromosome[3],";",Chromosome[4],";",Chromosome[5]); } void GTRMA(double par1,double par2,double par3,double par4,double par5) { MAshort=iMA(s,tf,(int)MathRound(par1*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); MAlong =iMA(s,tf,(int)MathRound(par2*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); CopyBuffer(MAshort,0,from,count,ShortBuffer); CopyBuffer(MAlong, 0,from,count,LongBuffer ); Print(TimeToString(TimeCurrent()),";","StrategyMA:GTRMA", ";","MAL=",(int)MathRound(par2*MaxMAPeriod)+1,";","MAS=",(int)MathRound(par1*MaxMAPeriod)+1); }
现在,它完全回到了一切工作的地方(Ontick()):当它知道什么策略是最好的时,它会检查是否是时候进入市场了:
bool NeedOpenMA() { CopyBuffer(MAshort,0,0,count,ShortBuffer); CopyBuffer(MAlong, 0,0,count,LongBuffer ); Print(TimeToString(TimeCurrent()),";","StrategyMA:NeedOpenMA", ";","LB[0]=",LongBuffer[0],";","LB[1]=",LongBuffer[1],";","SB[0]=",ShortBuffer[0],";","SB[1]=",ShortBuffer[1]); if(LongBuffer[0]>LongBuffer[1]&&ShortBuffer[0]>LongBuffer[0]&&ShortBuffer[1]<LongBuffer[1]) { request.type=ORDER_TYPE_SELL; OpenPosition(); return(false); } if(LongBuffer[0]<LongBuffer[1]&&ShortBuffer[0]<LongBuffer[0]&&ShortBuffer[1]>LongBuffer[1]) { request.type=ORDER_TYPE_BUY; OpenPosition(); return(false); } return(true); }
此循环已关闭。
让我们看看它是如何工作的。这是一份关于2011年一小时时间表的报告。主要有四种货币对:欧元兑美元、英镑兑美元、美元兑瑞士法郎、美元兑日元:
策略测试报告
Instaforex Server(内部版本567)
设置ea:主要交易品种:欧元兑美元期限:h1(2011.01.01-2011.12.31)输入参数:traindd=0.50000000 maxdd=0.20000000经纪人:Instax Forex Companies集团货币:美元初始存款:100000.00杠杆比率:1:100结果历史质量:100%列:6197价格变化:1321631净利润总额:-538.74毛利:3 535.51净亏损:-4074.25利润率:0.87预计利润:-89.79预付款区间水平:85.71%回收率:-0.08夏普比率:0.07内部结果:0余额损失:余额损失绝对值:4074.25余额损失最大值:4074.25(40.74%)余额损失相对值:40.74%(4074.25)市值损失:绝对值损失等值:4889.56最大市值损失:6690.90(50.53%)相对市值损失:50.53%(6690.90)总交易量:6短期交易(利润)6(16.67%)长期交易(利润)0(0.00%)总交易量:12利润交易(总交易)1(16.67%)亏损交易(总交易)):5(83.33%)最大盈利交易:3 535.51最大亏损交易:1 325.40平均盈利交易:3 535.51平均亏损交易:814.85最大持续盈利:1(3 535.51)最大持续亏损:5(-4 074.25)最大持续盈利(频率):3 535.51(1)最大持续亏损(频率):-4 074.25(5)平均持续盈利OFIT:1平均持续损失:5
型
订单开放时间,订单交易品种类型交易量价格S/L T/P时间状态备注2011.01.03 01:002USDCHF卖出28.21/28.210.93212011.01.03 01.03 01:00填写2011.01.03 03:003USDCHF买入28.21/28.210.93652011.01.03:00填写2011.01.03:06:004USDCHF卖出24.47/24.470.93522011.01.06:00002USDCHF买入2011.01.03.09:005USD4.47/24.470.470.470.93722011.93722011.01.470.93722011.01.470.93722011.01.2011.01.03.2011.01.03.47.470.47/24.47.470.470.93722011.01.03.470.47/24.47/24.470.470.470.470.470.93722011.01.470.470.47/24.47.47/24.470.470.93722011.01.470.93722011.2011.01.2011.01.01.03.03.03 09:00填2011.01.01.03.09.03 09:00填2011.01.03.03.03.03.00填2011.01.03 0.0981.0981.5781.57 2011.01.03.18:00小时填写2011.01.01.03.03.01.03 21.03 21:002011.01.03 21:00填写2011.01.04 01:0010美元日元卖出64.54/64.5481.672011.01.04 01:00已填写2011.01.04 02:0011美元日元买入64.54/64.5481.782011.01.04 02:00已填写2011.10.20 21:0012美元CHF卖出56.30/56.300.8964 2011.10.21:00已填写2011.10.21.12:0013美元CHF买入56.30/56.300.8908 2011.10.21:00已填写交易类型和方向的交易量。价格或订单手续费交换利润余额附注2011.01.01.01.01:00:001余额0.000.0010 000.0010 000.0010 000.00 2011.01.03 01:002USDCHF售出28.210.932120.00000000000000000000000000000000000000000000 000 000.00 2011.01.03 03:003USDCHF买入28.210.936530.000.00 00 00-1 325.408 674.325.408 674.60 2011.01.03 060606004 USDCHF售出24.470.93520.93520.93520.935240.9352000.9352000.000000.674.674.000000000000输出24.470.937250.000.00-5 22.198.674.674.00000000000000000输出24.470.937250.00.00-5 22.198 152.93520.93520.93520.93520.93520.93520.93520.93520.93520.93520.93520.93520.93520.935200.93520.935240.935200.935200.935200.935200000000000000000000000000000000000000000000000000000000000000000000.24.4700.24470.935200.9303 18:002008美元日元卖出72.0981.5780.5780.000.000。0000000 2008 2008 2008 2008 2008 2008 2008 2008 2008 2008年美元日元售出72.0981.0981.5780.5780.000.00000000000000000000000000000 2008 2008 2008 2008 2008 2008 2008 2008 2008.08080808080804 01:0010美元日元售出64.5481.67100.000.006 793.86 2011.01.04 02:0011美元Djpy买入64.5481.78110.000.00-868.115 925.75 2011.10.20 21:0012美元CHF卖出56.300.8964美元120.000.000.005 925.75 2011.10.21:0013美元CHF买入56.300.8908130.00-3.783 539.299 461.260.00-3.78-534.96 461.26版权所有2001-2011,Metaware Corp.
让我们解释图表上标记的区域(日志分析摘录):
- 在EA交易开始后,遗传算法选择突破策略sar,前提是交易中存款份额为USDCHF的28%。随后,交易于1月3日晚结束,亏损余额超过20%,并开始重新优化。
- 然后,EA决定突破基于美元兑日元的sar交易,但使用所有存款(98%)。当然,它不能进行长期交易,因此它在1月4日上午开始了第三次优化。
- 这一次,它决定根据整个存款交易的美元兑瑞士法郎移动平均数来交叉黄金和死亡。此外,该公司一直等到10月20日,将其出售至最高价值,并收回所有损失。从那时起至年底,EA交易未能找到进入市场的有利时机。
后面还有什么吗?
后面有吗?下一代EA交易会是什么样子?制定EA交易策略,并在其中选择最佳策略。此外,它还可以管理资金,购买更强大的硬件和渠道等。
风险预警:
这种简短的声明不足以充分揭示基于预付款的外币交易的所有风险和其他重要因素。您需要清楚地了解交易的性质以及您面临风险的程度。鉴于您的经验、目标、财务资源和其他相关环境,您应该认真考虑交易是否适合您。
外汇市场不仅有利可图,而且风险巨大。就提前还款交易而言,汇率相对较小的波动可能对交易员的账户产生重大影响,从而导致初始存款的全部损失,以及为维持开户而存入账户的任何金额。你不应该把你不能接受的损失金额存起来。在决定交易之前,确保你了解所有的风险并考虑你的经验水平。如有必要,请咨询独立顾问。
许可证:
mqh模块由Andrey Dik aka Joo根据BSD许可证开发和发布。
本文所附的EA贸易和辅助模块由Roman Rich根据BSD许可证创建和发布。有关许可证文本,请参阅lic.txt文件。
本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/350。
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。