![扩充策略构建器功能](https://c.mql5.com/i/articles/overlay.png)
扩充策略构建器功能
内容目录
概述
在系列文章的第一部分中,我们分析了 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; } }
在参数中有两个值:
- points 用于图表的新值。 所接收的值是以点数为单位成交平仓结果。 这既可以是止盈,亦或是止损。
- 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 文件夹
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/7361
![轻松快捷开发 MetaTrader 程序的函数库(第 二十五部分):处理交易服务器返回的错误](https://c.mql5.com/2/37/MQL5-avatar-doeasy__12.png)
![轻松快捷开发 MetaTrader 程序的函数库(第 二十四部分):基准交易类 - 无效参数自动纠正](https://c.mql5.com/2/37/MQL5-avatar-doeasy__6.png)
![利用箱形图(Boxplot)探索金融时间序列的季节性形态](https://c.mql5.com/2/37/MQL5-avatar-season_research.png)
![继续迈进优化(第一部分):操控优化报告](https://c.mql5.com/2/37/MQL5-avatar-continuous_optimization.png)