
扩充策略构建器功能
内容目录
概述
在系列文章的第一部分中,我们分析了 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
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.




会有交易专家来试用吗?
是的,在未来的文章中,我们计划实现这样的功能,它将允许在半手动模式和全自动专家顾问模式下进行交易。