繁簡切換您正在訪問的是FX168財經網,本網站所提供的內容及信息均遵守中華人民共和國香港特別行政區當地法律法規。

FX168财经网>人物频道>帖子

扩充策略构建器功能

作者/醒掌天下权 2020-02-07 21:00 0 来源: FX168财经网人物频道

内容目录

  • 概述
  • 附加概览
  • 新功能实现阶段
  • 策略构建器测试和演示
  • 结束语

概述

在系列文章的第一部分中,我们分析了 Merrill 形态并将其应用于不同的数据数组,例如价格和基于价格的振荡器 ATR、CCI 和 WPR,等等。 本文目的在于探索和评估在外汇和其他市场里使用指定形态的前景。 第二部分则致力于策略构建器的创建,将先前讨论的形态汇总成简单策略。 在第三部分中,我们将扩充策略创建,并测试功能。 我们还将增加按点数操控手数的可能性,以及用于查看测试结果的功能。

附加概览

在研究新功能之前,我们先回顾一下之前的部分。 所有测试结果均显示在摘要报告中,而报告内的盈利/亏损值则按所研究的指定金融产品的点数表述。 然而,这无法针彻底评估策略的所有潜在功能。 所以,主要目标是扩充测试器功能,然后扩充交易报告参数。

实施改进时,我们将坚持以下计划:

  • 初始存款以帐户币种表示。
  • 盈利计算选项:“按点数”或“按存款币种”。
  • 如果选择了“按存款币种”,则会出现另外两个输入字段:“手数类型”和“手数大小”。
  • 手数类型可以是常数,也可以基于余额。
  • 手数大小。 可用于常量手数类型。

在交易报告中执行了以下修改:

  • 净利润总额。 该参数仅适用于利润类型“按存款币种”。
  • 余额绝对回撤。 该参数仅适用于利润类型“按存款币种”。
  • 余额最大回撤。 该参数仅适用于利润类型“按存款币种”。
  • 除了空头和多头交易的数量外,还显示两种类型的交易胜率百分比。
  • 策略盈利能力。 利润总额与亏损总额的比率。
  • 恢复因子。 该参数仅适用于利润类型“按存款币种”。

您可以在 MetaTrader 5 帮助的测试报告章节中了解有关新参数的更多信息。 上述功能的原型如图例 1 所示。

图例 1 新测试工具的原型。

另一个新功能是可以直观地查看任何策略的测试结果。 这意味着您可以查看测试结果图。 我们将在应用程序的“报告”部分添加“打开图表”按钮(如图例 1 所示)。

图例 2 图形外观。

如图例 2 所示,可以在图表上直观地评估存款的走势特征和交易结果。 为方便起见,标题显示出所测试品种、其时间帧,以及执行测试的时间段。

新功能实现阶段

我们来定义主要元素和实现方法,类似于开发策略构建器初期时所做的工作。 在创建界面的 CreateGUI() 主要方法中,添加了两个方法。 我们看一下这些方法,以及针对现有方法的补充。

//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//--- Create a panel
   if(!CreateWindow("Merrill Constructor"))
      return(false);
//--- Create a dialog window
   if(!CreateDateSetting())
      return(false);
//--- Create a chart window
   if(!CreateGraphWindow())
      return(false);
//--- Create a load window
   if(!CreateLoading())
      return(false);
//--- Finish the creation of GUI
   CWndEvents::CompletedGUI();
   return(true);
}

我们观察主窗口创建方法 CreateWindow() 中的更改:为实现新的测试工具,添加了如图例 1 所示的新界面元素。

//---- CONSTRUCTOR tab
....
//---
   if(!CreateProfitType(int(0.35*(m_window[0].XSize()-150)-120),50+35*6+ygap))
      return(false);
   if(!CreateLotType(int(0.6*(m_window[0].XSize()-150)-120),50+35*6+ygap))
      return(false);
   if(!CreateBaseLotValue(int(0.85*(m_window[0].XSize()-150)-120),50+35*6+ygap))
      return(false);
   if(!CreateInitialDeposit(int(0.35*(m_window[0].XSize()-150)-120),50+35*7+ygap))
      return(false);
//---
   if(!CreateReportFrame(m_frame[2],int(0.35*(m_window[0].XSize()-150)-120),110+35*7+ygap))
      return(false);
//--- Graph opening button
   if(!CreateIconButton(int(0.35*(m_window[0].XSize()-150)-50),100+35*7+ygap))
      return(false);
//--- Report lines
   for(int i=0; i<11; i++)
   {
      if(i<5)
         if(!CreateTextLabel(m_report_text[i],int(0.37*(m_window[0].XSize()-150)-120),380+25*i+ygap,"",0))
            return(false);
      if(i>=5 && i<9)
         if(!CreateTextLabel(m_report_text[i],int(0.63*(m_window[0].XSize()-150)-120),380+25*(i-5)+ygap,"",0))
            return(false);
      if(i>=9)
         if(!CreateTextLabel(m_report_text[i],int(0.89*(m_window[0].XSize()-150)-120),380+25*(i-9)+ygap,"",0))
            return(false);
      m_report_text[i].IsCenterText(false);
      m_report_text[i].FontSize(10);
   }
....

视觉更改仅在“构造器”选项卡中实现。 附件和上一篇文章中提供了完整的选项卡实现代码,而此处仅展示新的方法。 我们来逐一研究它们。 

CreateProfitType(). 该方法为测试中用到的利润类型创建一个下拉列表:存款币种,或点数。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateProfitType(const int x_gap,const int y_gap)
{
//--- Pass the object to the panel
   m_profit_type.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_profit_type);
//--- Array of the item values in the list view
   string pattern_names[2]=
   {
      "Pips","Currency"
   };
//--- Set properties before creation
   m_profit_type.XSize(200);
   m_profit_type.YSize(25);
   m_profit_type.ItemsTotal(2);
   m_profit_type.FontSize(12);
   m_profit_type.LabelColor(C'0,100,255');
   m_profit_type.GetButtonPointer().FontSize(10);
   m_profit_type.GetButtonPointer().XGap(80);
   m_profit_type.GetButtonPointer().XSize(100);
   m_profit_type.GetButtonPointer().BackColor(clrAliceBlue);
   m_profit_type.GetListViewPointer().FontSize(10);
   m_profit_type.GetListViewPointer().YSize(44);
//--- Save the item values in the combobox list view
   for(int i=0; i<2; i++)
      m_profit_type.SetValue(i,pattern_names[i]);
//--- Get the list view pointer
   CListView *lv=m_profit_type.GetListViewPointer();
//--- Set the list view properties
   lv.LightsHover(true);
   m_profit_type.SelectItem(1);
//--- Create a control
   if(!m_profit_type.CreateComboBox("Profit Type",x_gap,y_gap))
      return(false);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_profit_type);
   return(true);
}

CreateLotType(). 该方法为手数类型创建一个下拉列表,常量,或依据余额。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateLotType(const int x_gap,const int y_gap)
{
//--- Pass the object to the panel
   m_lot_type.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_lot_type);
//--- Array of the item values in the list view
   string pattern_names[2]=
   {
      "Balance","Constant"
   };
//--- Set properties before creation
   m_lot_type.XSize(200);
   m_lot_type.YSize(25);
   m_lot_type.ItemsTotal(2);
   m_lot_type.FontSize(12);
   m_lot_type.LabelColor(C'0,100,255');
   m_lot_type.GetButtonPointer().FontSize(10);
   m_lot_type.GetButtonPointer().XGap(65);
   m_lot_type.GetButtonPointer().XSize(100);
   m_lot_type.GetButtonPointer().BackColor(clrAliceBlue);
   m_lot_type.GetListViewPointer().FontSize(10);
   m_lot_type.GetListViewPointer().YSize(44);
//--- Save the item values in the combobox list view
   for(int i=0; i<2; i++)
      m_lot_type.SetValue(i,pattern_names[i]);
//--- Get the list view pointer
   CListView *lv=m_lot_type.GetListViewPointer();
//--- Set the list view properties
   lv.LightsHover(true);
   m_lot_type.SelectItem(1);
//--- Create a control
   if(!m_lot_type.CreateComboBox("Lot Type",x_gap,y_gap))
      return(false);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_lot_type);
   return(true);
}

CreateBaseLotValue(). 该方法为手数值创建输入字段。

/+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateBaseLotValue(const int x_gap,const int y_gap)
{
//--- Save the pointer to the main control
   m_base_lot.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_base_lot);
//--- Properties
   m_base_lot.XSize(210);
   m_base_lot.YSize(24);
   m_base_lot.LabelColor(C'0,100,255');
   m_base_lot.FontSize(12);
   m_base_lot.MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX));
   m_base_lot.MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
   m_base_lot.StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP));
   m_base_lot.SetDigits(2);
   m_base_lot.SpinEditMode(true);
   m_base_lot.GetTextBoxPointer().AutoSelectionMode(true);
   m_base_lot.GetTextBoxPointer().XGap(100);
//--- Create a control
   if(!m_base_lot.CreateTextEdit("Base Lot Size",x_gap,y_gap))
      return(false);
   m_base_lot.SetValue((string)SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_base_lot);
   return(true);
}

CreateInitialDeposit(). 在“利润 —币种”模式下进行测试时,为初始存款创建一个输入字段。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateInitialDeposit(const int x_gap,const int y_gap)
{
//--- Save the pointer to the main control
   m_init_deposit.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_init_deposit);
//--- Properties
   m_init_deposit.XSize(210);
   m_init_deposit.YSize(24);
   m_init_deposit.LabelColor(C'0,100,255');
   m_init_deposit.FontSize(12);
   m_init_deposit.MinValue(10);
   m_init_deposit.SetDigits(2);
   m_init_deposit.GetTextBoxPointer().AutoSelectionMode(true);
   m_init_deposit.GetTextBoxPointer().XGap(125);
//--- Create a control
   if(!m_init_deposit.CreateTextEdit("Initial Deposit",x_gap,y_gap))
      return(false);
   m_init_deposit.SetValue((string)1000);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_init_deposit);
   return(true);
}

CreateIconButton(). 创建一个按钮,点击后会打开图表窗口。

//+------------------------------------------------------------------+
//| Creates a button with an image                                   |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_gray.bmp"
//---
bool CProgram::CreateIconButton(const int x_gap,const int y_gap)
{
//--- Save the pointer to the main control
   m_graph_button.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_graph_button);
//--- Properties
   m_graph_button.XSize(150);
   m_graph_button.YSize(22);
   m_graph_button.FontSize(11);
   m_graph_button.IconXGap(3);
   m_graph_button.IconYGap(3);
   m_graph_button.IsHighlighted(false);
   m_graph_button.IsCenterText(true);
   m_graph_button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp");
   m_graph_button.IconFileLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_gray.bmp");
   m_graph_button.IconFilePressed("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp");
   m_graph_button.IconFilePressedLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_gray.bmp");
   m_graph_button.BorderColor(C'0,100,255');
   m_graph_button.BackColor(clrAliceBlue);
//--- Create a control
   if(!m_graph_button.CreateButton("",x_gap,y_gap))
      return(false);
//--- Add the element pointer to the data base
   CWndContainer::AddToElementsArray(0,m_graph_button);
   return(true);
}

CreateWindow() 中修改了报告特征的输出结构。 它追加并实现为三列,取代了原先的两列。 所有这些就是在主窗口创建方法 CreateWindow() 中进行的修改。

接下来,我们继续实现图表窗口和报表图形的方法 — CreateGraphWindow()

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateGraphWindow(void)
{
//--- Add the pointer to the window array
   CWndContainer::AddWindow(m_window[2]);
//--- Properties
   m_window[2].XSize(750);
   m_window[2].YSize(450);
   m_window[2].FontSize(9);
   m_window[2].WindowType(W_DIALOG);
   m_window[2].IsMovable(true);
//--- Create the form
   if(!m_window[2].CreateWindow(m_chart_id,m_subwin,"",75,75))
      return(false);
   //--- Charts
   if(!CreateGraph(22,22))
      return(false);
//---
   return(true);
}

创建方法很小巧。 请注意其中包含的 CreateGraph() 方法

//+------------------------------------------------------------------+
//| Create a chart                                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGraph(const int x_gap,const int y_gap)
{
//--- Save the pointer to the main control
   m_graph1.MainPointer(m_window[2]);
//--- Properties
   m_graph1.AutoXResizeMode(true);
   m_graph1.AutoYResizeMode(true);
   m_graph1.AutoXResizeRightOffset(10);
   m_graph1.AutoYResizeBottomOffset(10);
//--- Create element
   if(!m_graph1.CreateGraph(x_gap,y_gap))
      return(false);
//--- Chart properties
   CGraphic *graph=m_graph1.GetGraphicPointer();
   graph.BackgroundColor(::ColorToARGB(clrWhiteSmoke));
   graph.XAxis().Min(0);
   graph.BackgroundMainSize(20);
   graph.HistoryNameSize(0);
   graph.HistorySymbolSize(0);
   graph.HistoryNameWidth(0);
//--- Add the element pointer to the data base
   CWndContainer::AddToElementsArray(2,m_graph1);
   return(true);
}

它创建一个空图形,并设置其视觉特征。 图形数据将在以后添加。

在主要的 CreateGUI() 中的另一个方法是 CreateLoading(),其会创建加载窗口。 它以指标形式负责加载应用程序、更改语言设置、以及处理数据和测试。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp"
bool CProgram::CreateLoading(void)
{
//--- Add the pointer to the window array
   CWndContainer::AddWindow(m_window[3]);
//--- Properties
   m_window[3].XSize(100);
   m_window[3].YSize(50);
   m_window[3].LabelYGap(50/2-16/2);
   m_window[3].IconYGap(50/2-16/2);
   m_window[3].FontSize(9);
   m_window[3].WindowType(W_DIALOG);
   m_window[3].IsMovable(false);
   m_window[3].CloseButtonIsUsed(false);
   m_window[3].CaptionColorLocked(C'0,130,225');
   m_window[3].LabelColor(clrWhite);
   m_window[3].LabelColorLocked(clrWhite);
   m_window[3].CaptionHeight(51);
   m_window[3].IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   m_window[3].IconFileLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   m_window[3].IconFilePressed("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   m_window[3].IconFilePressedLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   int x=int(m_window[0].XSize()/2);
   int y=int(m_window[0].YSize()/2);
//--- Create the form
   if(!m_window[3].CreateWindow(m_chart_id,m_subwin,"Working...",x,y))
      return(false);
   return(true);
}

我们已研究了策略构建器的视觉元素。 我们进入测试算法,并观察如何利用新的视觉控件在其中显示信息。

您也许还记得上一篇文章当中,策略测试的启动和处理是通过 GetResult() 方法实现的。 我们添加了新数据,因此需要修改此方法。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::GetResult(const string symbol)
{
//--- Get the date range
   m_start_date=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00");
   m_end_date=StringToTime(TimeToString(m_calendar2.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit2.GetHours()+":"+(string)m_time_edit2.GetMinutes()+":00");
//--- Check specified dates
   if(m_start_date>m_end_date || m_end_date>TimeCurrent())
   {
      if(m_lang_index==0)
         MessageBox("Неправильно выбран диапазон дат!","Ошибка",MB_OK);
      else if(m_lang_index==1)
         MessageBox("Incorrect date range selected!","Error",MB_OK);
      return;
   }
//--- Check if patterns are specified correctly
   if(m_combobox1.GetListViewPointer().SelectedItemIndex()==m_combobox2.GetListViewPointer().SelectedItemIndex())
   {
      if(m_lang_index==0)
         Messagebox("Паттерны не могут быть одинаковыми!","Ошибка",MB_OK);
      else if(m_lang_index==1)
         MessageBox("Patterns cannot be the same!","Error",MB_OK);
      return;
   }
//---
   m_window[3].OpenWindow();
//---
   m_counter=0;
   m_all_losses=0;
   m_all_profit=0;
   AddDeal(0,m_counter);
   ZeroMemory(m_report);
   MqlRates rt[];
   datetime cur_date=m_start_date;
   string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
   int applied1=m_applied1.GetListViewPointer().SelectedItemIndex();
   int applied2=m_applied2.GetListViewPointer().SelectedItemIndex();
   int applied3=m_applied3.GetListViewPointer().SelectedItemIndex();
   int applied4=m_applied4.GetListViewPointer().SelectedItemIndex();
   int applied5=m_applied5.GetListViewPointer().SelectedItemIndex();
   int applied6=m_applied6.GetListViewPointer().SelectedItemIndex();
//---
   while(cur_date<m_end_date)
   {
      //---
      if(
         applied1>7 || applied2>7 || applied3>7 ||
         applied4>7 || applied5>7 || applied6>7)
      {
         if(m_custom_path.GetValue()=="")
         {
            if(m_lang_index==0)
               MessageBox("Не установлен путь к индикатору!","Ошибка",MB_OK);
            else if(m_lang_index==1)
               MessageBox("The indicator path is not set!","Error",MB_OK);
            break;
         }
         if(m_custom_param.GetValue()=="")
         {
            if(m_lang_index==0)
               MessageBox("Не установлены параметры индикатора!","Ошибка",MB_OK);
            else if(m_lang_index==1)
               MessageBox("Indicator parameters not set!","Error",MB_OK);
            break;
         }
      }
      //---
      if(
         BuySignal(symbol,m_start_date,applied1,1) ||
         BuySignal(symbol,m_start_date,applied2,2) ||
         BuySignal(symbol,m_start_date,applied3,3))
      {
         CalculateBuyDeals(symbol,m_start_date);
         cur_date=m_start_date;
         continue;
      }
      if(
         SellSignal(symbol,m_start_date,applied4,1) ||
         SellSignal(symbol,m_start_date,applied5,2) ||
         SellSignal(symbol,m_start_date,applied6,3))
      {

         CalculateSellDeals(symbol,m_start_date);
         cur_date=m_start_date;
         continue;
      }
      m_start_date+=PeriodSeconds(StringToTimeframe(tf));
      cur_date=m_start_date;
   }
//--- Output the report
   PrintReport();
//---
   m_window[3].CloseDialogBox();
}

实现了以下修改:

  • 添加检查,验证形态与买入和卖出信号是否匹配。
  • 为先前创建的“下载”窗口添加具体操作。
  • 添加了 AddDeal() 方法,该方法负责将图形所使用的测试结果写入数据数组。 在此处设置图表的初始值。 对于“利润”类型为“货币”,初始值为“货币”。 对于 "点数" 则为零。

现在我们来查看 AddDeal() 数据添加方法:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::AddDeal(int points,int index)
{
//--- In points
   if(m_profit_type.GetListViewPointer().SelectedItemIndex()==0)
   {
      if(index==0)
      {
         ArrayResize(data,index+1);
         data[index]=0;
         return;
      }
      ArrayResize(data,index+1);
      data[index]=data[index-1]+points;
   }
//--- In deposit currency
   else if(m_profit_type.GetListViewPointer().SelectedItemIndex()==1)
   {
      if(index==0)
      {
         ArrayResize(data,index+1);
         data[index]=StringToDouble(m_init_deposit.GetValue());
         return;
      }
      ArrayResize(data,index+1);
      //--- Get a selected symbol
      string symbol=m_table_symb.GetValue(0,m_table_symb.SelectedItem());
      string basesymbol=AccountInfoString(ACCOUNT_CURRENCY);
      string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
      double lot=StringToDouble(m_base_lot.GetValue());
      if(m_lot_type.GetListViewPointer().SelectedItemIndex()>0)
      {
         lot*=data[index-1];
         lot=GetLotForOpeningPos(symbol,POSITION_TYPE_BUY,lot);
      }

      double pip_price=1;
      int shift=0;
      // --- Direct pair
      if(StringSubstr(symbol,3,3)==basesymbol)
      {
         pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot,2);
      }
      //--- Reverse pair
      else if(StringSubstr(symbol,0,3)==basesymbol)
      {
         shift=iBarShift(symbol,StringToTimeframe(tf),m_start_date,false);
         pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot/iOpen(symbol,StringToTimeframe(tf),shift),2);
      }
      else
      {
         //--- Cross pair
         StringConcatenate(symbol,StringSubstr(symbol,3,3),basesymbol);
         if(SymbolInfoDouble(symbol,SYMBOL_BID)!=0)
         {
            shift=iBarShift(symbol,StringToTimeframe(tf),m_start_date,false);
            pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot/iOpen(symbol,StringToTimeframe(tf),shift),2);
         }
         //---
         StringConcatenate(symbol,basesymbol,StringSubstr(symbol,0,3));
         if(SymbolInfoDouble(symbol,SYMBOL_BID)!=0)
         {
            shift=iBarShift(symbol,StringToTimeframe(tf),m_start_date,false);
            pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot*iOpen(symbol,StringToTimeframe(tf),shift),2);
         }
      }
      //---
      if(points>0)
         m_all_profit+=pip_price*points;
      else
         m_all_losses+=pip_price*-points;
      //---
      data[index]=data[index-1]+pip_price*points;
   }
}

在参数中有两个值:

  1. points 用于图表的新值。 所接收的值是以点数为单位成交平仓结果。 这既可以是止盈,亦或是止损。
  2. index 是交易的索引。

根据所选的利润显示模式,将在方法中计算相应的数据,并添加到数据数组中。 对于“点数”利润模式,数组中的每个新元素都是以点数为单位的先前值与交易结果之和。 在“货币”模式下,收到的以点数为单位的成交结果将转换为存款币种。 此操作考虑了测试货币对的类型:直盘,颠倒盘或交叉盘。

其他两个修改过的方法是 CalculateBuyDeals()CalculateSellDeals()。 它们处理找到的信号,并在必要时虚拟开仓。 我们观察其中一个方法(第二种方法的修改类似):

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::CalculateBuyDeals(const string symbol,datetime start)
{
   MqlRates rt[];
   int TP=int(m_takeprofit1.GetValue());
   int SL=int(m_stoploss1.GetValue());
   string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
   int copied=CopyRates(symbol,StringToTimeframe(tf),m_start_date,m_end_date,rt);
   double deal_price=iOpen(symbol,StringToTimeframe(tf),copied);
   for(int j=0; j<copied; j++)
   {
      //--- Take Profit trigger
      if((iHigh(symbol,StringToTimeframe(tf),copied-j)-deal_price)/SymbolInfoDouble(symbol,SYMBOL_POINT)>=TP)
      {
         m_counter++;
         AddDeal(TP,m_counter);
         m_report.profit_trades++;
         m_report.profit+=TP;
         m_report.profit_pips+=TP;
         m_report.long_trades++;
         m_report.profit_long++;
         m_report.total_trades++;
         m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j);
         return;
      }
      //--- Stop Loss trigger
      else if((deal_price-iLow(symbol,StringToTimeframe(tf),copied-j))/SymbolInfoDouble(symbol,SYMBOL_POINT)>=SL)
      {
         m_counter++;
         AddDeal(-SL,m_counter);
         m_report.loss_trades++;
         m_report.profit-=SL;
         m_report.loss_pips+=SL;
         m_report.long_trades++;
         m_report.total_trades++;
         m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j);
         return;
      }
   }
   m_start_date=m_end_date;
}

修改涉及止盈和止损触发。 此处,上述 AddDeal() 方法实际处理成交平仓。此外,新参数 profit_pips 和 loss_pips 已添加到 REPORT m_report 结构中。 这些参数允许计算报告中的新特征。

最后一个方法经过重大修改,处理接收到的数据,并将结果输出到报告中。 同样,这是通过 PrintReport() 方法执行的:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::PrintReport(void)
{
   string report_label[11];
   if(m_lang_index==0)
   {
      report_label[0]="Всего трейдов: ";
      report_label[1]="Чистая прибыль: ";
      report_label[2]="Прибыль в пунктах: ";
      report_label[3]="Абс.просадка баланса: ";
      report_label[4]="Макс.просадка баланса: ";
      report_label[5]="Корот.трейды/% выигр: ";
      report_label[6]="Приб.трейды/% от всех: ";
      report_label[7]="Прибыльность: ";
      report_label[8]="Фактор восстановления: ";
      report_label[9]="Длин.трейды/% выигр: ";
      report_label[10]="Убыт.трейды/% от всех: ";
   }
   else
   {
      report_label[0]="Total trades: ";
      report_label[1]="Total profit: ";
      report_label[2]="Total profit(pips): ";
      report_label[3]="Balance Drawdown Abs: ";
      report_label[4]="Balance Drawdown Max: ";
      report_label[5]="Short trades/won %: ";
      report_label[6]="Profit trades/% of all: ";
      report_label[7]="Profit Factor: ";
      report_label[8]="Recovery Factor: ";
      report_label[9]="Long trades/won %: ";
      report_label[10]="Loss trades/% of all: ";
   }
   //---
   m_report_text[0].LabelText(report_label[0]+string(m_report.total_trades));
   //---
   if(m_report.total_trades==0)
      return;
   //---
   if(m_profit_type.GetListViewPointer().SelectedItemIndex()==1)
   {
      double maxprofit=0.0,maxdd=0.0;
      for(int i=1; i<ArraySize(data); i++)
      {
         if(data[i]>maxprofit)
            maxprofit=data[i];
         if(maxdd<maxprofit-data[i])
            maxdd=maxprofit-data[i];
      }
      m_report_text[1].LabelText(report_label[1]+DoubleToString(data[ArraySize(data)-1],2));
      m_report_text[3].LabelText(report_label[3]+string(data[0]-data[ArrayMinimum(data)]));
      m_report_text[4].LabelText(report_label[4]+DoubleToString(maxdd/maxprofit*100,2)+"%");
      m_report_text[7].LabelText(report_label[7]+DoubleToString(m_all_profit/m_all_losses,2));
      m_report_text[8].LabelText(report_label[8]+DoubleToString((data[ArraySize(data)-1]-data[0])/maxdd,2));
   }
   else
   {
      m_report_text[1].LabelText(report_label[1]+"-");
      m_report_text[3].LabelText(report_label[3]+"-");
      m_report_text[4].LabelText(report_label[4]+"-");
      m_report_text[7].LabelText(report_label[7]+DoubleToString(m_report.profit_pips/(double)m_report.loss_pips,2));
   }
   m_report_text[2].LabelText(report_label[2]+string(m_report.profit));
   m_report_text[5].LabelText(report_label[5]+string(m_report.short_trades)+"/"+DoubleToString(m_report.profit_short/(double)m_report.short_trades*100,1)+"%");
   m_report_text[6].LabelText(report_label[6]+string(m_report.profit_trades)+"/"+DoubleToString(m_report.profit_trades/(double)m_report.total_trades*100,1)+"%");
   m_report_text[9].LabelText(report_label[9]+string(m_report.long_trades)+"/"+DoubleToString(m_report.profit_long/(double)m_report.long_trades*100,1)+"%");
   m_report_text[10].LabelText(report_label[10]+string(m_report.loss_trades)+"/"+DoubleToString(m_report.loss_trades/(double)m_report.total_trades*100,1)+"%");
//---
   for(int i=0; i<11; i++)
      m_report_text[i].Update(true);
   m_reported=true;
}

如概述中所述,某些参数(例如“总利润”或“恢复因子”)不适用于“点数”测试模式。


策略构建器测试和演示

在应用程序中实现改进和更新之后,接着需要更新用户手册。

步骤 1、 设置用户界面语言。 

此步骤是可选的,仅在您希望更改语言时才需要。

步骤 2、 设置指标参数。

该应用程序已有默认参数,因此无需配置所有指标。 仅更改所需的参数。 如有必要,您可以随时更改设置。

步骤 3、 设置品种表格。

默认情况下,市场观察里所有名称中存在 USD 的品种均被筛选。 您只需取消选中复选框即可显示所有可用品种。

步骤 4-5\ 选择测试时间期和时间帧 - 这些步骤没有更改。

步骤 6、 启用卖出/买入信号,并选择测试模式。

信号设置步骤未更改。 不过,针对视觉显示进行的修改:如果禁用了其中一种信号类型,则所有相关设置都将被隐藏,如图例 3 所示。

图例 3 禁用买入或卖出信号。

步骤 7、 止盈和止损设置未更改。

步骤 8、 选择利润类型。

  • 选择“点数”后,“报告”部分中的测试结果将以所选货币品种的点数显示。 
  • 如果选择了“货币”,则会出现其他设置:手数类型,手数和初始存款。 手数类型会在测试中影响手数的计算。 它可以是常量,也可以基于余额。

步骤 9、 在步骤 1-8 之后,通过在表中单击鼠标左键选择测试产品。 

测试完成后,结果将显示在“报告”部分中。 之后,您可以单击打开图形。

下一个视频显示了按照上述算法进行的测试。



测试美林形态的建议:

  • 为了令应用程序正常工作,我们需要下载指定交易品种的历史数据来进行测试。
  • 自定义指标参数时要小心:缓冲区编号,名称或参数应以逗号分隔。 请注意,仅支持数字值。 自定义指标名称是相对于指标根目录(MQL5/Indicators/)的路径。 如果指标位于子目录中,例如 MQL5/Indicators/Examples,则相应名称应看起来:“ Examples\\indicator_name”(始终用双反斜杠替代单反斜杠作为分隔符)。
  • 可能会引起困难的最常见情况会带有提示。 这些包括同一形态触发两种信号,尝试在不执行先前测试的情况下打开图形,错误的测试开始日期和结束日期。
  • 在本文的开头,我提到的链接,指向该报告中用到的特征描述。 跟随链接阅读有关参数描述和计算方法的更多信息。

结束语

下面的文档包含所有描述过的文件,这些文件应排列在相应的文件夹中。 为了正确操作,请将 MQL5 文件夹放置在终端的根目录中。 若要打开 MQL5 文件夹所在的终端根目录,请按 MetaTrader 5 终端中的 Ctrl+Shift+D 组合键,或使用关联菜单,如下面的图例 5 中所示。


图例 5. 在 MetaTrader 5 终端根目录中打开 MQL5 文件夹


分享到:
举报财经168客户端下载

全部回复

0/140

投稿 您想发表你的观点和看法?

更多人气分析师

  • 金泰铬J

    人气2320文章3925粉丝51

    投资问答解咨询金泰铬V/信tgtg67即可获取每日的实时资讯、行情...

  • 李冉晴

    人气2296文章3821粉丝34

    李冉晴,专业现贷实盘分析师。

  • 金帝财神

    人气4728文章8329粉丝118

    本文由资深分析师金帝财神微信:934295330,指导黄金,白银,...

  • 刘钥钥1

    人气2016文章3119粉丝34

    专业从事现货黄金、现货白银模似实盘操作分析指导

  • 指导老师

    人气1856文章4423粉丝52

    暂无个人简介信息

  • 张亦巧

    人气2144文章4145粉丝45

    暂无个人简介信息

  • 张迎妤

    人气1896文章3305粉丝34

    个人专注于行情技术分析,消息面解读剖析,给予您第一时间方向...

  • 梁孟梵

    人气2152文章3177粉丝39

    qq:2294906466 了解群指导添加微信mfmacd

  • 金算盘

    人气2696文章7761粉丝125

    高级分析师,混过名校,厮杀于股市和期货、证券市场多年,专注...