简介
首先,对所有圣杯猎人和吹毛求疵者提醒一句:就像上一篇文章中介绍的工具一样,此工具并不是能够帮助你完全避免亏损而仅获得收益的魔术棒。 它仅仅是一个能够让你快速进行计算并以直观的可视化形式显示计算结果的工具。 指标图应被视为交易者思考使用的交易战术和信号所用到的资料。 然而,在短期(尤其是日内)交易中,此指标可成为一个很方便的工具。
有关修改旧指标并为其提供新的形式的想法源自于一条天真而真诚的评论,此评论是与有关 Expert Advisor 的说明一起在赫兹量化的一个论坛帖子中发布的。 “ 年做的很好。 到目前为止,我很满意。 为了促进信号监控,我...” Expert Advisor 的确表现良好,根据其开发者指定的交易品种和时间范围进行交易。 但是,任何更改参数的尝试会将一个良好的执行者变成一个赤裸裸的杀手,几乎在瞬间就会失去所有保证金。 尽管 Expert Advisor 的开发者并不打算将其用于真实交易,但在我的脑海里总是出现这样的想法:如何能够不仅检查预期交易结果,还能够评估交易行为、结果的稳定性(如果你愿意的话)。 我希望能够快速而明显地直观展示一些信号和交易战术的无效性。 此外,我的脑海中还有另一个想法:Expert Advisor 可以使用现有参数产生可观的获利交易。 我曾经认为,如果我能够像这样手动交易,我会对结果非常满意。 假设能够进一步改进结果将会怎样? 一般说来,此问题本身似乎很有趣,我开始寻找解决此问题的方式。
问题定义
很显然,解决方案需要我在文章“指标和提醒的盈利能力的可视化测试”中的指标,这意味着大部分的工作已完成,只需要进行升级。 之前的实现缺少什么? 很明显,它是一种便捷的参数选择机制。 每次都需要检查指标参数,进行更改,然后再使用历史数据分析产生的图和订单行,这项工作很累人。 这是可以理解的 - 原始指标用于检查就绪信号,而在这种情况下还需要其他东西。 那会是什么? 在 MT 中积极使用策略测试程序的人员对此必须有非常清晰的了解。 要评估交易结果,赫兹量化首先需要余额图表和修改用于计算信号值的初始参数的可能性。
余额图表
这是最简单的事情。由于所有操作都绘制在图表中,每个操作的结果都是已知的,赫兹量化需要做的是获得所有这些结果并将它们加起来作为运行总计。 可在单独的窗口中显示增加(或减少)图表。 为了满足那些希望分析以币值或点数表示的结果的人员的需求,我们应提供合适的参数。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
三角形滑块模拟
相比于需要与交易者交互的任务,调整面向交易的自动化平台是一件困难得多的事情。 标准资源仅允许我实现一系列操作:选择指标、调用其属性、修改参数和重新计算。 这种至少十几个变量的迭代需要花费大量的时间。 等到你获得第十个变量的结果时,你已经完全忘记了第一个变量的结果。
由于操作系统的标准 GUI 元素(按钮、复选框、组合框、滑块等)无法用于指标和 EA(除非你打算使用以其他编程语言编写的 DLL 库),我不得不寻找一个适当的替代。 我决定调整一个三角形,一个可轻松添加到任何图表的对象,用作为标准滑块,它可以在给定的最小值到给定的最大值范围内更改值。 如果它的两个顶点垂直(或水平)分布,同时第三个顶点在这两个顶点之间移动,赫兹量化可以获得一个相当合适的线性滑块模型。 此类滑块的垂直位置能够获得一系列更加平滑(连续)的值。 为了能够同时更改多个参数,我们需要实现单独处理不同三角形的可能性。
已编写了一个用于处理三角形滑块的特殊函数:
double ParamValue(int ParamNo, string ParamName, double ParamValue, double vMin, double vMax, color clr) { double Triangle[3],vCur, WMax, WMin; datetime tt1, tt2; // if there is no triangle, create it if(ObjectFind(ParamName) < 0) { // determine the chart boundaries in the current scale vertically WMax = WindowPriceMax(); WMin = WindowPriceMin(); // calculate the coordinates of points by time... tt1 = Time[0] + Period()*60*ParamNo*20; tt2 = tt1 + Period()*60*20; // ... and "price" vCur = WMin + (ParamValue - vMin) * (WMax - WMin) / (vMax - vMin); // create an object and fill it with the color specified in the parameters ObjectCreate(ParamName,OBJ_TRIANGLE, 0, tt1,WMax, tt2,vCur, tt1,WMin); ObjectSet(ParamName,OBJPROP_COLOR,clr); } // the triangle exists - get its coordinates Triangle[0] = ObjectGet(ParamName,OBJPROP_PRICE1); Triangle[1] = ObjectGet(ParamName,OBJPROP_PRICE2); Triangle[2] = ObjectGet(ParamName,OBJPROP_PRICE3); // arrange the vertices in the order of "increase" ArraySort(Triangle); // convert the midpoint coordinate to the scale of real values between vMin and vMax vCur = vMin + (Triangle[1] - Triangle[0]) / (Triangle[2] - Triangle[0]) * (vMax - vMin); // write the value to the object comment ObjectSetText(ParamName,DoubleToStr(vCur,2)); // return the value to the main module return(vCur); }
注释提供了此函数算法的非常详细的说明,因此我们将仅限于介绍函数参数的用途。
ParamNo – 使用的参数数量(它决定三角形沿时间轴相互之间移动的方式)。 然后,你可以根据需要更改它们的位置和大小。
ParamName – 参数名称,必须是唯一的以区分三角形,而且对显示在工具提示中有意义。
ParamValue – 初始参数值(用于正确地放置在最小值 vMin 和最大值 vMax 之间的中间顶点,将在优化过程中使用)。
сlr – 三角形的颜色。
以下代码用于处理三角形:
MAPeriod = ParamValue(0, SliderPrefix+"MA Period", MAPeriod, 5, 35, Blue); RSIPeriod = ParamValue(1, SliderPrefix+"RSI Period", RSIPeriod, 2, 25, Red); RSILevel = ParamValue(2, SliderPrefix+"RSI Level", RSILevel, 5, 95, Orange);
获得的值进一步用于计算相应的信号。
为了阐明指标开发和调试,我选择了用户提议的一个指标 <s1>Helen</s1> (<a2>HI_Line_E_RSI_MA.mq4</a2>),基本上是此指标激励了我做这一切。 尽管代码基本上保持不变,但信号计算块需要重新编写,以便适合在我的指标中使用:
// fill signal arrays with values and count their number for(i=DisplayBars;i>=0;i--) { double t1=iRSI(NULL,0,RSIPeriod,MAPrice,i+1); double t11=iRSI(NULL,0,RSIPeriod,MAPrice,i+2); double t2=iMA(NULL,0,MAPeriod,0,MAMode,MAPrice,i+1); double t3=iMA(NULL,0,MAPeriod*3,0,MAMode,MAPrice,i+1); if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel&&t2>t3) {sBuy[i]=1; sBuyCnt++;} if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)) {sCloseBuy[i]=1; sBuyCloseCnt++;} if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)&&t2<t3) {sSell[i]=1; sSellCnt++; } if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel) {sCloseSell[i]=1; sSellCloseCnt++;} }
我决定先不使用第四个参数 - 用于获取长移动平均线的周期放大率。 因此,我只是使用了比主移动平均线长三倍的 移动平均线 (MAPeriod*3)。 如果你需要的话,你可以自己轻松地实现这一点。
还在原始指标中添加了余额曲线计算和显示块。
// plot the balance chart // get profit values one by one and add them up i2 = DisplayBars; bBallance[i2] = 0; for(i=DisplayBars-1;i>=0;i--) { if(bBallance[i] != 0) { bBallance[i2] = bBallance[i2+1] + bBallance[i]; i2--; } } double multiplier; if(ProfitInPoints) multiplier = 1; else multiplier = ToCurrency; for(i=0;i<DisplayBars-2;i++) bBallance[i] = bBallance[i+i2+1] * multiplier; SetIndexStyle(0,DRAW_HISTOGRAM,STYLE_SOLID,ProfitWidth,Blue);
第一个回路逐步添加了之前设置的操作利润值,而第二个回路将它们聚集成一堆,相互之间没有空格。 因此,值得注意的是,寻找上图表和下图表之间的任何对应关系没有任何意义:建立订单和关闭订单垂直线与下图表没有任何关联(这就是它们默认被禁用的原因)。
结果,此指标的文本如下:
/*///—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— IndicatorOptimizer.mq4 Visual testing of profitability of indicators and alerts Copyright © , Sergey Kravchuk, .ua /*///—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— #property copyright "Copyright © -, Sergey Kravchuk. .ua" #property link ".ua" #property indicator_separate_window #property indicator_buffers 1 #property indicator_level1 0 // parameters for displaying chart elements extern bool ShowZZ = true; // whether to draw ZZ extern bool ShowMARKERS = false; // whether to draw vertical marker lines extern int ProfitWidth = 1; extern bool ProfitInPoints = true; //chart plotting dates extern datetime DateStart = D'1.01.1970'; extern datetime DateEnd = D'31.12.2037'; //profit calculation parameters extern double LotForCalc = 0.05; // lot size for profit calculation // RSI indicator parameters extern int RSIPeriod = 8; // RSI calculation period extern int RSILevel = 73; // RSI calculation level // MA indicator parameters extern int MAPeriod = 20; // МА calculation period extern int MAMode = 3; // 0-MODE_SMA 1-MODE_EMA 2-MODE_SMMA 3-MODE_LWMA extern int MAPrice = 6; // 0-Close 1-Open 2-High 3-Low 4-(H+L)/2 5-(H+L+C)/3 6-(H+L+2*C)/4 // MACD indicator parameters extern double MACDOpenLevel = 3; extern double MACDCloseLevel = 2; extern double MATrendPeriod = 26; // colors of Buy lines extern color ColorProfitBuy = Blue; // line color for profitable Buys extern color ColorLossBuy = Red; // line color for unprofitable Buys extern color ColorZeroBuy = Gray; // line color for zero-profit Buys // colors of Sell lines extern color ColorProfitSell = Blue; // line color for profitable Sells extern color ColorLossSell = Red; // line color for unprofitable Sells extern color ColorZeroSell = Gray; // line color for zero-profit Sells // colors of signal lines extern color ColorBuy = CornflowerBlue; // Buy signal line color extern color ColorSell = HotPink; // Sell signal line color extern color ColorClose = Gainsboro; // line color of the signal for closing //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— double sBuy[],sCloseBuy[],sSell[],sCloseSell[]; // arrays for signals int sBuyCnt,sSellCnt,sBuyCloseCnt,sSellCloseCnt;// signal counters int i,DisplayBars; double bBallance[]; // for the balance on operations int IndicatorWindowNo; // indicator window number //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // service codes #define MrakerPrefix "IO_" #define SliderPrefix "SL_" #define OP_CLOSE_BUY 678 #define OP_CLOSE_SELL 876 //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— int init() { SetIndexBuffer(0,bBallance); IndicatorShortName("IndicatorOptimizer"); return(0); } int deinit() { ClearMarkers(); ClearSliders(); return(0); } double ParamValue(int ParamNo, string ParamName, double ParamValue, double vMin, double vMax, color clr) { double Triangle[3],vCur, WMax, WMin; datetime tt1, tt2; // if there is no triangle, create it if(ObjectFind(ParamName) < 0) { // determine the chart boundaries in the current scale vertically WMax = WindowPriceMax(); WMin = WindowPriceMin(); // calculate the coordinates of points by time... tt1 = Time[0] + Period()*60*ParamNo*20; tt2 = tt1 + Period()*60*20; // ... and "price" vCur = WMin + (ParamValue - vMin) * (WMax - WMin) / (vMax - vMin); // create an object and fill it with the color specified in the parameters ObjectCreate(ParamName,OBJ_TRIANGLE, 0, tt1,WMax, tt2,vCur, tt1,WMin); ObjectSet(ParamName,OBJPROP_COLOR,clr);