指标和信号盈利能力的可视化优化

Sergey Kravchuk | 11 四月, 2016

本文是我的上一篇文章“指标和提醒的盈利能力的可视化测试”的延续和拓展。 通过在参数更改过程中添加一些交互性和修改研究对象,我成功地获得了一个新工具,此工具不仅仅显示基于所使用信号的预期交易结果,还允许通过移动在主图表中用作信号参数值控件的虚拟滑块,立即获得交易布局、余额图表和最终交易结果。



简介

首先,对所有圣杯猎人和吹毛求疵者提醒一句:就像上一篇文章中介绍的工具一样,此工具并不是能够帮助你完全避免亏损而仅获得收益的魔术棒。 它仅仅是一个能够让你快速进行计算并以直观的可视化形式显示计算结果的工具。 指标图应被视为交易者思考使用的交易战术和信号所用到的资料。 然而,在短期(尤其是日内)交易中,此指标可成为一个很方便的工具。

有关修改旧指标并为其提供新的形式的想法源自于一条天真而真诚的评论,此评论是与有关 Expert Advisor 的说明一起在我们的一个论坛帖子中发布的。 “2009 年做的很好。 到目前为止,我很满意。 为了促进信号监控,我...” Expert Advisor 的确表现良好,根据其开发者指定的交易品种和时间范围进行交易。 但是,任何更改参数的尝试会将一个良好的执行者变成一个赤裸裸的杀手,几乎在瞬间就会失去所有保证金。 尽管 Expert Advisor 的开发者并不打算将其用于真实交易,但在我的脑海里总是出现这样的想法:如何能够不仅检查预期交易结果,还能够评估交易行为、结果的稳定性(如果你愿意的话)。 我希望能够快速而明显地直观展示一些信号和交易战术的无效性。 此外,我的脑海中还有另一个想法:Expert Advisor 可以使用现有参数产生可观的获利交易。 我曾经认为,如果我能够像这样手动交易,我会对结果非常满意。 假设能够进一步改进结果将会怎样? 一般说来,此问题本身似乎很有趣,我开始寻找解决此问题的方式。



问题定义

很显然,解决方案需要我在文章“指标和提醒的盈利能力的可视化测试”中的指标,这意味着大部分的工作已完成,只需要进行升级。 之前的实现缺少什么? 很明显,它是一种便捷的参数选择机制。 每次都需要检查指标参数,进行更改,然后再使用历史数据分析产生的图和订单行,这项工作很累人。 这是可以理解的 - 原始指标用于检查就绪信号,而在这种情况下还需要其他东西。 那会是什么? 在 MT 中积极使用策略测试程序的人员对此必须有非常清晰的了解。 要评估交易结果,我们首先需要余额图表和修改用于计算信号值的初始参数的可能性。



余额图表

这是最简单的事情。由于所有操作都绘制在图表中,每个操作的结果都是已知的,我们需要做的是获得所有这些结果并将它们加起来作为运行总计。 可在单独的窗口中显示增加(或减少)图表。 为了满足那些希望分析以币值或点数表示的结果的人员的需求,我们应提供合适的参数。



三角形滑块模拟

相比于需要与交易者交互的任务,调整面向交易的自动化平台是一件困难得多的事情。 标准资源仅允许我实现一系列操作:选择指标、调用其属性、修改参数和重新计算。 这种至少十几个变量的迭代需要花费大量的时间。 等到你获得第十个变量的结果时,你已经完全忘记了第一个变量的结果。

由于操作系统的标准 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 © 2009, Sergey Kravchuk, http://forextools.com.ua

/*///——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
#property copyright "Copyright © 2006-2009, Sergey Kravchuk. http://forextools.com.ua"
#property link      "http://forextools.com.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);
  }
  // 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); 
}


//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
int start() 
{ 
  double Profit=0,P1,P2; int CntProfit=0,CntLoose=0,i1,i2;
  // coefficient for conversion of points to the deposit currency
  double ToCurrency = MarketInfo(Symbol(),MODE_TICKVALUE)*LotForCalc;    
  
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // delete all markers in case the indicator is redrawn
  ClearMarkers(); 
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // prepare signal counters
  sBuyCnt=0; sSellCnt=0; sBuyCloseCnt=0; sSellCloseCnt=0; 
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // allocate memory for signal arrays and zero out their values
  DisplayBars=Bars; // number of bars displayed
  ArrayResize(sBuy,DisplayBars);  ArrayInitialize(sBuy,0);
  ArrayResize(sSell,DisplayBars); ArrayInitialize(sSell,0);
  ArrayResize(sCloseBuy,DisplayBars);  ArrayInitialize(sCloseBuy,0);
  ArrayResize(sCloseSell,DisplayBars); ArrayInitialize(sCloseSell,0);

  // prepare balance calculation
  ArrayInitialize(bBallance,0); 
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // creating (if necessary) and getting parameters of the control triangle
  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);
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


  // 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++;}
  }

 
        //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // delete repeated signals leaving only the first ones, on the left of the chart
  for(i=0;i<DisplayBars;i++) 
  {
    if(sBuy[i]==sBuy[i+1]) sBuy[i]=0;
    if(sSell[i]==sSell[i+1]) sSell[i]=0;
    if(sCloseBuy[i]==sCloseBuy[i+1]) sCloseBuy[i]=0;
    if(sCloseSell[i]==sCloseSell[i+1]) sCloseSell[i]=0;
  }
  // delete signals outside the specified date range
  for(i=0;i<DisplayBars;i++) 
  {
    if(Time[i]<DateStart || DateEnd<Time[i]) { sBuy[i]=0; sSell[i]=0; sCloseBuy[i]=0; sCloseSell[i]=0; }
  }
  // add forced closing at the range limit
  if(DateEnd<=Time[0]) { i=iBarShift(Symbol(),Period(),DateEnd); sBuy[i]=0; sSell[i]=0; sCloseBuy[i]=1; sCloseSell[i]=1; }
  if(DateEnd >Time[0]) { sCloseBuy[0]=1; sCloseSell[0]=1; }
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // count the number of signals
  for(i=0;i<DisplayBars;i++) 
  {
    if(sBuy [i]!=0) sBuyCnt++;  if(sCloseBuy [i]!=0) sBuyCloseCnt++;
    if(sSell[i]!=0) sSellCnt++; if(sCloseSell[i]!=0) sSellCloseCnt++;
  }
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // set markers, draw ZZ and calculate profit
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // process Buys
  for(i=DisplayBars-1;i>=0;i--) // get the points
  {
    // find the next point of opening and save its position and price
    for(i1=i;i1>=0;i1--) if(sBuy[i1]!=0) break; 

    // find the next Buy closing point and save its position and price
    for(i2=i1-1;i2>=0;i2--) if(sCloseBuy[i2]!=0) break;
    if(i2<0) i2=0; // for the last non-closed position, calculate closing on the current price
    i=i2; // new bar to continue the search for points of opening

    // determine prices for drawing 
    P1=Open[i1]; P2=Open[i2];  
    
    P1/=Point; P2/=Point; // convert prices to points
    
    // determine the profit and fill the corresponding buffer
    if(i1>=0) 
    { 
      Profit=Profit+P2-P1; // get the cumulative profit
      if(P2-P1>=0) CntProfit++; else CntLoose++; // count the number of orders
      DrawLine(i1,i2,OP_BUY); // draw the order line
      PlaceMarker(i1,OP_BUY); 
      PlaceMarker(i2,OP_CLOSE_BUY); 
      
      bBallance[i2] += P2-P1;
    }
  }
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // process sells
  for(i=DisplayBars-1;i>=0;i--) // get the points
  {
    // find the next point of opening and save its position and price
    for(i1=i;i1>=0;i1--) if(sSell[i1]!=0) break; 

    // find the next Buy closing point and save its position and price
    for(i2=i1-1;i2>=0;i2--) if(sCloseSell[i2]!=0) break;
    if(i2<0) i2=0; // for the last non-closed position, calculate closing on the current price
    i=i2; // new bar to continue the search for points of opening

    // determine prices for drawing depending on the Optimizm parameter
    P1=Open[i1]; P2=Open[i2]; 
    
    P1/=Point; P2/=Point; // convert prices to points
    
    // if both points exist, determine the profit and fill the corresponding buffer
    if(i1>=0) 
    { 
      Profit=Profit+P1-P2; // get the cumulative profit
      if(P1-P2>=0) CntProfit++; else CntLoose++; // count the number of orders
      DrawLine(i1,i2,OP_SELL); // draw the order line
      PlaceMarker(i1,OP_SELL); 
      PlaceMarker(i2,OP_CLOSE_SELL);
      
      bBallance[i2] += P1-P2;
    }
  }

  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // 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);

  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // calculation of the final values for the comment  
  int Cnt=CntProfit+CntLoose; // total number of operations
  string msg="Period: "+TimeToStr(MathMax(DateStart,Time[Bars-1]))+" - "+TimeToStr(MathMin(DateEnd,Time[0]))+"\n\n";
  
  msg=msg + 
  "RSI period: " + RSIPeriod + "\n" +
  "RSI level: " + RSILevel + "\n" +
  "МА period: " + MAPeriod + "\n\n" +
  sBuyCnt+" Buys and "+sBuyCloseCnt+" their closings\n"+
  sSellCnt+" Sells and "+sSellCloseCnt+" their closings\n\n";
  
  // total time in days
  int TotalDays = (MathMin(DateEnd,Time[0])-MathMax(DateStart,Time[Bars-1]))/60/60/24; //convert seconds to days
  if(TotalDays<=0) TotalDays=1; // to avoid zero divide for an incomplete day
  
  if(Cnt==0) msg=msg+("No operation");
  else msg=msg+
  (
    DoubleToStr(Profit,0)+" points for "+Cnt+" operations over "+TotalDays+" days\n"+
    DoubleToStr(Profit/Cnt,1)+" points per operation ("+
    DoubleToStr(Profit/TotalDays,1)+" per day)\n\n"+
    "When trading the lot "+DoubleToStr(LotForCalc,2)+" get in "+AccountCurrency()+":\n"+
    DoubleToStr(Profit*ToCurrency,0)+" total, "+
    DoubleToStr(Profit/Cnt*ToCurrency,1)+" per operation ("+
    DoubleToStr(Profit/TotalDays*ToCurrency,1)+" per day)\n\n"+
    CntProfit+" profitable ("+DoubleToStr(((CntProfit)*1.0/Cnt*1.0)*100.0,1)+"%)\n"+
    CntLoose+" losing ("+DoubleToStr(((CntLoose)*1.0/Cnt*1.0)*100.0,1)+"%)"
  );
  Comment(msg);
  
}
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————



//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// deleting all objects from our chart
void ClearMarkers() 
{ 
  for(int i=ObjectsTotal()-1;i>=0;i--) if(StringFind(ObjectName(i),MrakerPrefix)==0) ObjectDelete(ObjectName(i)); 
}
void ClearSliders() 
{ 
  for(int i=ObjectsTotal()-1;i>=0;i--) if(StringFind(ObjectName(i),SliderPrefix)==0) ObjectDelete(ObjectName(i)); 
}
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// setting a vertical line - the op_type operation marker
void PlaceMarker(int i, int op_type)
{
  if(!ShowMARKERS) return; // displaying markers is disabled

  color MarkerColor; string MarkName; 

  // __ so that the closing line is drawn below the opening line by sorting 
  if(op_type==OP_CLOSE_SELL) { MarkerColor=ColorClose; MarkName=MrakerPrefix+"__SELL_"+i; }
  if(op_type==OP_CLOSE_BUY)  { MarkerColor=ColorClose; MarkName=MrakerPrefix+"__BUY_"+i;  }  
  if(op_type==OP_SELL)       { MarkerColor=ColorSell;  MarkName=MrakerPrefix+"_SELL_"+i;  }
  if(op_type==OP_BUY)        { MarkerColor=ColorBuy;   MarkName=MrakerPrefix+"_BUY_"+i;   }

  ObjectCreate(MarkName,OBJ_VLINE,0,Time[i],0); 
  ObjectSet(MarkName,OBJPROP_WIDTH,1); 
  if(op_type==OP_CLOSE_BUY || op_type==OP_CLOSE_SELL) ObjectSet(MarkName,OBJPROP_STYLE,STYLE_SOLID); 
  else ObjectSet(MarkName,OBJPROP_STYLE,STYLE_DOT);
  ObjectSet(MarkName,OBJPROP_BACK,True);  

  ObjectSet(MarkName,OBJPROP_COLOR,MarkerColor);
}
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// setting the line on the chart for the op_type operation type using the Optimizm parameter values
void DrawLine(int i1,int i2, int op_type)
{
  if(!ShowZZ) return; // displaying ZZ is disabled
  
  color СurColor;
  string MarkName=MrakerPrefix+"_"+i1+"_"+i2;
  double P1,P2;
  
  // determine prices for drawing depending on the Optimizm parameter
  P1=Open[i1]; P2=Open[i2];  

  ObjectCreate(MarkName,OBJ_TREND,0,Time[i1],P1,Time[i2],P2);
  
  ObjectSet(MarkName,OBJPROP_RAY,False); // draw segments instead of lines
  ObjectSet(MarkName,OBJPROP_BACK,False);
  ObjectSet(MarkName,OBJPROP_STYLE,STYLE_SOLID);
  ObjectSet(MarkName,OBJPROP_WIDTH,2);

  // set the line color depending on profitability of the operation
  if(op_type==OP_BUY) 
  { 
    if(P1 <P2) СurColor = ColorProfitBuy;
    if(P1==P2) СurColor = ColorZeroBuy;
    if(P1 >P2) СurColor = ColorLossBuy;
  }
  if(op_type==OP_SELL) 
  { 
    if(P1 >P2) СurColor = ColorProfitSell;
    if(P1==P2) СurColor = ColorZeroSell;
    if(P1 <P2) СurColor = ColorLossSell;
  }
  ObjectSet(MarkName,OBJPROP_COLOR,СurColor);
}
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

可在我的上一篇文章中找到算法及其操作的详细说明。 在这里应注意,我移除了优化参数的运算。 在此指标版本中,基于之前彻底完成的条柱值计算当前条柱的信号。 建议使用基于条柱分析的经典交易方法:在分析了新完成的条柱的结果之后,在当前条柱的开头处建立仓位。 现在我们可以说,获得的计算结果非常可靠。



运行的优化程序

在编译指标并将指标添加到图表之后,我们可以开始分析。 可详细介绍此过程,但如果你观看了展示指标的实际操作的小动画,一切都变得很明显。

在这里我们必须注意一件重要的事情:指标在每次出现新的价格变动时重新计算它的值,因此在移动三角形滑块之后,我们应等待新价格或手动刷新图表或使用价格变动模拟器。

使用优化程序现在变成了一个名为“假如...将会怎样?”的奇特游戏。 你只需选择三角形滑块并开始向上/向下移动它们的中点。 三角形顶点的水平位置不重要;重要的只是顶点的垂直位置。 因此,你应移动三角形中点并尝试领会这些移动带来的结果。 你的直觉和实际经验肯定会提醒你要移动的对象和要移动到的位置。 如果你缺乏经验,给自己一个试错的机会:寻找合适的变量,然后基于确定的参数进行交易。



如何使用指标。 第一部分: 不能做什么

应像使用我之前的指标一样使用此指标,同时了解你要做什么以及将获得怎样的结果。 你可以将信号计算块替换为你自己的信号计算块,同时增加或减少使用的信号数量。 但在查看结果时应牢记,如果你成功地优化了 EURUSD М30 的指标参数,而且它显示了一个良好的获利曲线,这并不意味着,当交易另一个货币对或不同的时间范围或者交易另一个货币对与不同的时间范围时,结果也会这么好。 要了解为什么会如此,只需查看 М1 和 D1 图表。 条柱变化和条柱大小的本质差异如此明显,因此无需进行进一步注释。

尽管货币对之间的差异并不是很明显,但是可使用诸如我的文章“通过脉冲进行市场诊断”中介绍的技巧轻松识别此差异。 该文章中提供的屏幕截图将表明为什么你不应期望所有货币对都获得相同的结果,即便信号计算块的设置都相同。



如何使用指标。 第二部分: 必须做什么

然而,它没有看起来那么糟。 正如一些哲学家所说的,“缺点是优点的延续”。 如果我们看到了缺陷但尚未发现优点,并不意味着它们不存在。 查看 D1 图表,它具有相当长的稳定上升趋势。 你真的认为已基于此周期的起点进行优化的指标不会在此趋势的中期产生收益?!

如果你决定在交易中就像我使用此工具一样使用它,你将只需要不时地针对某个货币对和时间范围对它进行调整。 你可以使用 Helen 提议的同一个 Expert Advisor 并在自动交易中尝试使用它,同时不时地(尤其当指标允许一天多次轻松地运行此进程时)将它的参数调整为优化结果,从而甚至在分钟图表(М5、М15 和 М30)上使用它进行交易。



总结

从根本上说,市场是不可预测的。 任何 100% 可靠和经证实的信号可能会由于重要新闻的发布而转眼变得完全相反。 在讨论本文的过程中,一些交易可能会试图说服你相信,如果不考虑诸如基本信号等的相关信息或者没有交易量数据,你将永远无法了解将出现价格波动的位置,因而永远无法获得稳定的收益。 这是它们的观点,这是它们进行交易和获得成功的方式。 可以使用这种交易方法管理一千美元以上的资金。

我认为,对于较小的保证金,如果你愿意的话,此指标可以成为最简单可靠的工具之一。 你永远无法了解价格变动的全局,因此,寻找反转点以试图从市场中挤出每一分钱的做法是没有用的。 数天和数周的长期趋势可能具有一些一般的模式,如果你可以了解长期趋势的起点并据此对信号计算系统进行优化,你将能够成功获得很可能并不算短时段的优化参数。 但即使“风向发生改变”,你也不再会认为这是个大问题,因为现在的优化过程使用起来比之前使用此指标时轻松得多。