简介
许多交易策略在寻找各种市场进入模式时需要不断分析交易类型。虽然搜索和分析交易类型可能有助于进一步了解市场,但有时您可能希望向您显示具有必要参数的交易类型列表。在本文中,我们将尝试为一些日内交易模式开发此类工具。
更具体地说,今天我们将通过添加基于特定参数的贸易项目自动选择来扩展实用程序的功能。为此,我们将创建一系列选项卡来搜索具有特定事务模式的当前事务类型:抛物线、空气层(平板磁盘范围)、跳转等。我们还将学习如何添加自定义选项卡,前提是您了解MQL编程的基础知识。
如前一篇文章所述,我们的实用程序将同时在MQL4和MQL5中工作。展望未来,我应该说,在mql5中打开自动选择选项卡比在mql4中慢,当请求的事务种类未达到所需的历史记录深度时,就会发生这种情况。在这种情况下,MetaTrader 5从事务服务器请求历史记录,并构建所需的时间框架。
因此,如果在单击选项卡时什么都没有发生,或者所有按钮都消失了,不要惊慌,只是等待,15到20秒后,将显示必要的各种事务。它仍然比手动查看数百个交易的图表快。另一方面,在MetaTrader 4中,您需要独立地提供必要交易种类和时间框架的历史记录,这也需要时间和注意这些琐碎的事情。
另外,如果您的计算机有少量RAM(或者您使用的虚拟机RAM数量有限,例如1GB),那么在MQL5中,当您打开自动选择选项卡时,EA操作可能会由于内存不足错误而中断。MQL4没有这个问题,因为整个历史记录是在不同的深度独立上载的。在MQL5中,这个问题可以通过限制“图表中的最大条”参数来解决。
向自动选择页面添加功能。
首先,让我们定义如何向实用程序添加自动选择选项卡。为此,我们需要在内部了解这些选项卡的机制是如何设计的。
我们已经在前一篇文章中实现了添加选项卡的功能,您可能还记得我们是在一个循环中完成的。添加的选项卡的名称保存在数组中,自动选择的选项卡的名称也保存在单独的数组中:
string panelNamesAddon[9]={"Air Level", "Parabolic", "Gap", "4 weeks Min/Max", "365 days Min/Max", "Round levels", "Mostly Up/Down", "All time high/low", "High=Close"};
换句话说,要添加自定义选项卡,我们需要将数组大小增加1,并在选项卡列表的末尾添加选项卡名称。之后,新的选项卡将出现在实用程序中。
显示面板按钮功能负责显示选项卡。其代码已修改,所有自动选择选项卡显示在原始选项卡之后:
void show_panel_buttons(){ int btn_left=0; // 定义可以显示选项卡的最大 x 坐标。 int btn_right=(int) ChartGetInteger(0, CHART_WIDTH_IN_PIXELS)-77; string tmpName=""; for( int i=0; i<ArraySize(panelNames); i++ ){ // 如果新按钮的开始坐标超过了最大可能坐标, // 转到新的一行 if( btn_left>btn_right-BTN_WIDTH ){ btn_line++; btn_left=0; } // 如果 "homework" 选项卡含有交易品种,就把它们的编号 // 加到选项卡名称中 tmpName=panelNames[i]; if(i>0 && arrPanels[i].Total()>0 ){ tmpName+=" ("+(string) arrPanels[i].Total()+")"; } // 显示选项卡按钮 ObjectCreate(0, exprefix+"panels"+(string) i, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_COLOR,clrBlack); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_BGCOLOR,clrSilver); ObjectSetString(0,exprefix+"panels"+(string) i,OBJPROP_TEXT,tmpName); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_SELECTABLE,false); // 如果按钮选项卡当前激活, // 把它设为按下状态 if( cur_panel == i ){ ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_STATE, true); } btn_left+=BTN_WIDTH; } // 如果输入参数中允许显示自动挑选选项卡,就显示它们 if(useAddonsLevels){ for( int i=0; i<ArraySize(panelNamesAddon); i++ ){ if( btn_left>btn_right-BTN_WIDTH ){ btn_line++; btn_left=0; } tmpName=panelNamesAddon[i]; // 如果选项卡称为 Gap, 就显示当前输入参数的值 // 定义跳空百分比 if(tmpName=="Gap"){ StringAdd(tmpName, " ("+(string) gap_min+"%)"); } ObjectCreate(0, exprefix+"panels"+(string) (i+ArraySize(panelNames)), OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_COLOR,clrBlack); ObjectSetInteger(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_BGCOLOR,clrLightSteelBlue); ObjectSetString(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_TEXT,tmpName); ObjectSetInteger(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_SELECTABLE,false); if( cur_panel == i+ArraySize(panelNames) ){ ObjectSetInteger(0,exprefix+"panels"+(string) (i+ArraySize(panelNames)),OBJPROP_STATE, true); } btn_left+=BTN_WIDTH; } } // 如果设置了注释,显示: if(StringLen(cmt)>0){ string tmpCMT=cmt; if(from_txt){ StringAdd(tmpCMT, ", from txt"); } ObjectCreate(0, exprefix+"title", OBJ_EDIT, 0, 0, 0); ObjectSetInteger(0,exprefix+"title",OBJPROP_XDISTANCE,btn_left+11); ObjectSetInteger(0,exprefix+"title",OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"title",OBJPROP_XSIZE,133); ObjectSetInteger(0,exprefix+"title",OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"title",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"title",OBJPROP_COLOR,clrGold); ObjectSetInteger(0,exprefix+"title",OBJPROP_BGCOLOR,clrNONE); ObjectSetInteger(0,exprefix+"title",OBJPROP_BORDER_COLOR,clrBlack); ObjectSetString(0,exprefix+"title",OBJPROP_TEXT,tmpCMT); ObjectSetInteger(0,exprefix+"title",OBJPROP_SELECTABLE,false); } }
从功能代码中可以看到,只有在输入参数中使用了加载项的InfoWatch许可证时,才会显示自动选择选项卡。此外,我们添加了两个附加参数来配置自动选择选项卡:
sinput string delimeter_05=""; // --- 另外的选项卡 --- input bool useAddonsLevels=true; // 显示额外的选项卡 input bool addons_infowatch=true;// 如果不在市场报价中则隐藏该交易品种 input int addon_tabs_scale=3; // 额外选项卡的序号 (0-5)
我相信这里只需要解释addons_InfoWatch参数。如果设置此参数,则只选择市场报价中显示的交易品种页签。否则,将选择您的经纪人提供的所有贸易品种。
这样,当运行新版本的工具时,您可以看到新的自动选择选项卡:
但让我们回到我们的代码。现在,我们知道了如何添加自动选择选项卡。但是,由于贸易项目的显示尚未实现,因此当我们单击它们时,没有显示贸易项目。当前打开选项卡的所有按钮都使用“显示符号”功能显示。我们将在此处添加自动选择选项卡的显示。因此,函数如下所示:
void show_symbols(){ // 初始化用于定义 X 和 Y 坐标的变量 int btn_left=0; int btn_right=(int) ChartGetInteger(0, CHART_WIDTH_IN_PIXELS)-77; btn_line++; if( cur_panel==0 ){ ObjectCreate(0, exprefix+"clear_"+(string) cur_panel, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_COLOR,clrBlack); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_BGCOLOR,clrPaleTurquoise); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_SELECTABLE,false); ObjectSetString(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_TEXT,"Clear All"); btn_left+=BTN_WIDTH; ObjectCreate(0, exprefix+"showpos", OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"showpos",OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"showpos",OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"showpos",OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"showpos",OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"showpos",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"showpos",OBJPROP_COLOR,clrBlack); ObjectSetInteger(0,exprefix+"showpos",OBJPROP_BGCOLOR,clrPaleTurquoise); ObjectSetInteger(0,exprefix+"showpos",OBJPROP_SELECTABLE,false); ObjectSetString(0,exprefix+"showpos",OBJPROP_TEXT,"Show Pos"); btn_left+=BTN_WIDTH; }else if( cur_panel<ArraySize(arrPanels) && arrPanels[cur_panel].Total()>0 ){ ObjectCreate(0, exprefix+"clear_"+(string) cur_panel, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_COLOR,clrBlack); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_BGCOLOR,clrPaleTurquoise); ObjectSetInteger(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_SELECTABLE,false); ObjectSetString(0,exprefix+"clear_"+(string) cur_panel,OBJPROP_TEXT,"Clear"); btn_left+=BTN_WIDTH; ObjectCreate(0, exprefix+"new_"+(string) cur_panel, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"new_"+(string) cur_panel,OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"new_"+(string) cur_panel,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"new_"+(string) cur_panel,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"new_"+(string) cur_panel,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"new_"+(string) cur_panel,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"new_"+(string) cur_panel,OBJPROP_COLOR,clrBlack); ObjectSetInteger(0,exprefix+"new_"+(string) cur_panel,OBJPROP_BGCOLOR,clrPaleTurquoise); ObjectSetInteger(0,exprefix+"new_"+(string) cur_panel,OBJPROP_SELECTABLE,false); ObjectSetString(0,exprefix+"new_"+(string) cur_panel,OBJPROP_TEXT,"Open All"); btn_left+=BTN_WIDTH; } // 如果当前打开的选项卡索引超过了 homework 选项卡数组元素的元素数 // 加上所有交易品种的选项卡 // 则这就是自动挑选选项卡 if(cur_panel>(ArraySize(arrPanels)-1)){ MqlRates rates[]; ArraySetAsSeries(rates, true); int tmpNumAddon=cur_panel-ArraySize(arrPanels); addonArr.Resize(0); arrTT.Resize(0); string addonName; CArrayString tmpSymbols; if( tmpNumAddon==0 && air_only_home ){ for( int j=1; j<ArraySize(arrPanels); j++ ){ for( int k=0; k<arrPanels[j].Total(); k++ ){ string curName=arrPanels[j].At(k); bool isYes=false; for( int r=0; r<tmpSymbols.Total(); r++ ){ if(tmpSymbols.At(r)==curName){ isYes=true; break; } } if(!isYes){ tmpSymbols.Add(arrPanels[j].At(k)); } } } }else{ if( ArraySize(result)>1 ){ for(int j=0;j<ArraySize(result);j++){ StringReplace(result[j], " ", ""); if(StringLen(result[j])<1){ continue; } tmpSymbols.Add(onlySymbolsPrefix+result[j]+onlySymbolsSuffix); } }else{ for( int i=0; i<SymbolsTotal(addons_infowatch); i++ ){ tmpSymbols.Add(SymbolName(i, addons_infowatch)); } } } switch(tmpNumAddon){ case 0: // 水平 // 选项卡内容显示代码 break; case 1: // 抛物线 // 选项卡内容显示代码 break; case 2: // 跳空 // 选项卡内容显示代码 break; case 3: //4周最小值/最大值 // 选项卡内容显示代码 break; case 4: //365 天最小值/最大值 // 选项卡内容显示代码 break; case 5: // 近似水平 // 选项卡内容显示代码 break; case 6: // 最上升/下降 // 选项卡内容显示代码 break; case 7: // 所有时间最高价/最低价 // 选项卡内容显示代码 break; case 8: // 最高价=收盘价 // 选项卡内容显示代码 break; } // 数组中的每个交易品种都在图表中显示一个按钮 // 按钮上加上交易品种名称 for( int i=0; i<addonArr.Total(); i++ ){ if( btn_left>btn_right-BTN_WIDTH ){ btn_line++; btn_left=0; } ObjectCreate(0, exprefix+"btn"+(string) i, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_COLOR,clrBlack); ObjectSetString(0,exprefix+"btn"+(string) i,OBJPROP_TEXT,addonArr.At(i)); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_SELECTABLE,false); if( arrTT.At(i)>0 ){ ObjectSetString(0,exprefix+"btn"+(string) i,OBJPROP_TOOLTIP,(string) arrTT.At(i)); } if( checkSYMBwithPOS(addonArr.At(i)) ){ ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_BGCOLOR,clrPeachPuff); } btn_left+=BTN_WIDTH; } // 因为按钮已经显示,退出函数 return; } // 在图表上显示数组中每个交易品种的按钮 // 当前活动选项卡 // 按钮上加上交易品种名称 for( int i=0; i<arrPanels[cur_panel].Total(); i++ ){ if( btn_left>btn_right-BTN_WIDTH ){ btn_line++; btn_left=0; } ObjectCreate(0, exprefix+"btn"+(string) i, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_COLOR,clrBlack); ObjectSetString(0,exprefix+"btn"+(string) i,OBJPROP_TEXT,arrPanels[cur_panel].At(i)); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_SELECTABLE,false); if( !noSYMBwithPOS || cur_panel>0 ){ if( checkSYMBwithPOS(arrPanels[cur_panel].At(i)) ){ ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_BGCOLOR,clrPeachPuff); } } btn_left+=BTN_WIDTH; } }
我们可以看到显示选项卡内容的代码在switch操作符中,数组元素的索引包含在选项卡的名称中,并在case操作符中指示。为了添加自定义选项卡,在将其名称添加到数组后,将使用最后一个索引中大于1的值对新事例进行收费。
在考虑特定的选项卡时,我们将了解更多关于根据特定参数自动选择贸易品种的代码示例。但是我们已经看到所有的标签代码都是以类似的方式开始的。
要选择的所有交易的列表已经存在于tmpsymbols数组中,因此每个选项卡的代码从for循环开始:
for( int i=0; i<tmpSymbols.Total(); i++ ){ addonName=tmpSymbols[i]; // 如果交易品种应当显示,定义的代码 }
空气水平(合并范围)
当从一个水平进行交易时,所有的交易都是在一个统一的范围内进行的,也就是说,当每个水平的价格与它的高或低触及相同或几乎相同的价格时。示例如下图所示:
也许不是一个很好的例子,因为成就水平经常会遇到错误的突破。另一方面,人们认为,虚假突破的存在将加强这一水平。=)
搜索这样的情况是一项非常繁琐的任务,所以让我们尝试将其自动化。
平面磁盘范围的搜索通常在M5图表上执行,尽管这不是一个强制条件,一些交易员在M15或M30图表上执行。据信,在平面范围内探测时间越长,潜在进入越好。
因此,让我们添加输入参数,以便定义所需的时间范围:
sinput string delimeter_06=""; // --- 额外的空中水平选项卡 --- input bool air_level_m5=true; // 在 M5 上寻找空中水平 input bool air_level_m15=true; // 在 M15 上寻找空中水平 input bool air_level_m30=false; // 在 M30 上寻找空中水平 input bool air_level_h1=false; // 在 H1 上寻找空中水平 input bool air_level_h4=false; // 在 H4 上寻找空中水平 input bool air_level_d1=false; // 在 D1 上寻找空中水平
应该记住,选择时间越长,搜索过程就越慢。因此,最好将搜索限制在一个或两个时间范围内。
另外,让我们加上一些其它的输入参数:
input uchar air_level_count=4; // Number of level bars(水平的柱数) input uchar air_level_offset=3; // Offset relative to level in points(相对水平的点数偏移) input bool air_level_cur_day=true; // Only levels in the current day's direction(只有当天方向的水平) input bool air_level_prev_day=true; // Only levels in the previous day's direction(只有前一天方向上的水平) input bool air_only_home=false; // Search only in "homework"(只在 "homework" 中搜索)
“级别条数”参数允许我们指定接触最近价格的列数,以假定这是平面磁盘的范围。此参数的最佳值是四列。列数越大,平板的质量范围越大,但找到它们的频率越低。
相对于“点中水平”参数的偏移量允许我们指定点的范围,也就是说,价格被认为是“非常接近”,换句话说,如果参数为0,则列应该接触到完全相同的价格。一般来说,这种情况很少见,尤其是在较高的时间范围内。在大多数情况下,价格不能达到指定的水平,所以默认的参数值是3点。
如果只按当日方向和/或前一日方向进行交易,只有当日方向的水平和前一日方向的水平才能选择平坦区域仅与价格变化方向相反的交易品种。
价格变动的方向由收盘价与开盘价的比率决定。如果前一天栏目的收盘价超过开盘价,只搜索多向平台,即“低价”触及附近价格。
最后,让我们看看下面的最后一个参数–“仅在”家庭作业中搜索。如果您只在交易日交易添加到“作业”选项卡的各种交易,则可以将此参数设置为仅在当前添加到“作业”选项卡的各种交易中搜索平面区域。
现在让我们最后看选择当前包含有平板区域交易品种类的代码,在M5时间框架中的代码提供如下:
if(air_level_m5 && CopyRates(addonName, PERIOD_M5, 0, air_level_count+1, rates)==air_level_count+1){ if( (!air_level_cur_day || getmeinfoday_symbol(addonName, 0)<=0) && (!air_level_prev_day || getmeinfoday_symbol(addonName, 1)<=0) && rates[0].high<rates[1].high ){ bool isOk=true; for( int j=1; j<air_level_count; j++ ){ if( MathAbs(rates[1].high-rates[j+1].high) <= air_level_offset*SymbolInfoDouble(addonName, SYMBOL_POINT) ){ }else{ isOk=false; } } if(isOk && !skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(rates[1].high); } }else if( (!air_level_cur_day || getmeinfoday_symbol(addonName, 0)>=0) && (!air_level_prev_day || getmeinfoday_symbol(addonName, 1)>=0) && rates[0].low>rates[1].low ){ bool isOk=true; for( int j=1; j<air_level_count; j++ ){ if( MathAbs(rates[1].low-rates[j+1].low) <= air_level_offset*SymbolInfoDouble(addonName, SYMBOL_POINT) ){ }else{ isOk=false; } } if(isOk && !skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(rates[1].low); } } }
与自动选择选项卡的所有其他代码示例类似,这里我们首先执行copyrates函数调用,然后在执行后立即调用结果,这并不完全正确。mql帮助文档建议等待一段时间,以便工具接收数据并将其写入数组。但是,即使我们等待50毫秒,检查100个交易意味着延迟5秒,许多经纪人提供数百个股票市场交易。这样,在使用延迟时显示选项卡的内容可能需要更多的时间。
所以我们立即开始操作结果,在实践中,这是没有问题的,只是它可能是由没有延误造成的。
事实上,由于copyrates函数的存在,实际数据并不总是传输到数组中。有时您选择使用旧数据。在这种情况下,只需更新选项卡(R键)即可获取相关事务的列表。
让我们回顾一下代码。如果决定添加自定义自动排序选项卡,请注意如何选择事务类型。
如果交易项符合我们的条件,我们将其名称放入addonarr数组中。此外,我们可以指定打开贸易项目图表时要使用的时间范围,而不是括号中的默认时间范围。
我们还需要将值输入arrtt数组,如果我们将数组中的值设置为零,则不会发生任何事情。但是,如果我们添加一些价格,当我们打开相应的符号图时,水平线将以指定的价格水平构建。这样做是为了方便,这样你就可以立即看到平范围内检测到的价格。
抛物线弧
当价格开始向相反的方向移动时,每一新列的最高或最低价格高于或低于前一列的价格时,在向一个方向移动后会出现抛物线。人们相信,在这种情况下,价格更有可能上涨或下跌。在这种情况下,可以使用小挡块。
换言之,价格下跌后,价格开始上涨,每列的低点高于前一列。在这种情况下,输入多头交易,并将止损至少置于前一列的低点。
以下图表可作为示例:
抛物线弧的示例用箭头
标记。
我们将使用以下代码搜索M5中的抛物线弧:
if(CopyRates(addonName, PERIOD_M5, 0, 6, rates)==6){ if( rates[0].low>rates[1].low && rates[1].low>rates[2].low && rates[2].low<=rates[3].low && rates[3].low<=rates[4].low && rates[4].low<=rates[5].low ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(0); } }else if( rates[0].high<rates[1].high && rates[1].high<rates[2].high && rates[2].high>=rates[3].high && rates[3].high>=rates[4].high && rates[4].high>=rates[5].high ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(0); } } }
换言之,如果当前列的最低价格超过了前一列的最低价格,并且该列的最低价格也超过了前一列的最低价格,并且其余三个前列或其最低价格相同或各自较小。换句话说,如果所有三根柱子都倒下了,两根柱子也跟着升起,这就是多头抛物线的开始。
差距
如果您使用的交易策略适用于在一个方向或另一个方向出现短跳的股票,则“差距(短跳)”选项卡将帮助您选择必要的股票。这表明,目前有一天的短线交易。您可以使用最小间隙大小参数修改最小跳数(作为当前价格的百分比)。
默认情况下,它只显示了至少1%个跳跃的各种交易。
选项卡的源代码很简单:
if(CopyRates(addonName, PERIOD_D1, 0, 2, rates)==2){ if( rates[0].open>rates[1].close+(rates[0].open*(gap_min/100)) || rates[0].open<rates[1].close-(rates[0].open*(gap_min/100)) ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(0); } } }
4周内最高/最低
“四周最大值/最小值”选项卡提供其当前价格在四周内处于最高/最低水平的交易类型列表。其代码如下:
if(CopyRates(addonName, PERIOD_W1, 0, 4, rates)==4){ bool newMin=true; bool newMax=true; if( rates[0].close!=rates[0].high && rates[0].close!=rates[0].low ){ newMin=false; newMax=false; }else{ for( int j=1; j<4; j++ ){ if( rates[0].high < rates[j].high ){ newMax=false; } if( rates[0].low > rates[j].low ){ newMin=false; } } } if( newMin || newMax ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(0); } } }
年度最高/最低
与上一个选项卡类似,当前选项卡显示交易项目的价格处于一年中的最高/最低水平。
if(CopyRates(addonName, PERIOD_W1, 0, 52, rates)==52){ bool newMin=true; bool newMax=true; if( rates[0].close!=rates[0].high && rates[0].close!=rates[0].low ){ newMin=false; newMax=false; }else{ for( int j=1; j<52; j++ ){ if( rates[0].high < rates[j].high ){ newMax=false; } if( rates[0].low > rates[j].low ){ newMin=false; } } } if( newMin || newMax ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(0); } } }
接近合并水平的价格
人们认为,股票的合并价格是支持/阻力的“自然”水平。因此,一些交易系统将重点放在当前以整数级别交易的工具上。
全价是以0或50美分结束的价格,例如125美元、0美分或79美元、50美分。
因此,我们将获得以下代码:
switch((int) SymbolInfoInteger(addonName, SYMBOL_DIGITS)){ case 0: break; case 2: if(CopyRates(addonName, PERIOD_M5, 0, 1, rates)==1){ double tmpRound=rates[0].close - (int) rates[0].close; if( (tmpRound>0.46 && tmpRound<0.54) || tmpRound>0.96 || tmpRound<0.04 ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(0); } } } break; }
换言之,我们将只为具有两位小数的符号定义取整价格。如果您使用其他工具,只需以类似的方式为它们添加您自己的检验。
大部分时间上/下
资产工具的上升或下降大部分时间也可以特别有趣。让我们添加以下输入参数来查找它们:
sinput string delimeter_08=""; // --- 额外的多数上涨/下跌选项卡 --- input int mostly_count=15; // Check the last specified number of days(检查最近指定的天数) input int mostly_percent=90; // Specified percentage in one direction exceeded(指定在一个方向上超过的百分比)
检查最后一个指定的天数参数可以定义查找所需资产工具的天数,换句话说,我们大多数人都会在d1中查找方向的更改。
指定的百分比在一个方向上超过参数可以指定最小百分比,就是相互超过的百分比。默认值是90,这意味着为了实现这个标签,价格在指定的时间段内的变化应该在某个方向上变化超过90%的天。
一般来说,此类交易的数量很少。因此,有必要降低这个百分比。
标签代码如下:
if(CopyRates(addonName, PERIOD_D1, 1, mostly_count, rates)==mostly_count){ int mostlyLong=0; int mostlyShort=0; for( int j=0; j<mostly_count; j++ ){ if(rates[j].close>rates[j].open){ mostlyLong++; }else if(rates[j].close<rates[j].open){ mostlyShort++; } } if( !mostlyLong || !mostlyShort ){ addonArr.Add(addonName); arrTT.Add(0); }else if( ((mostlyLong*100)/(mostlyLong+mostlyShort)) >= mostly_percent ){ addonArr.Add(addonName); arrTT.Add(0); }else if( ((mostlyShort*100)/(mostlyLong+mostlyShort)) >= mostly_percent ){ addonArr.Add(addonName); arrTT.Add(0); } }
每列都有一个新的高/低
此页签可以检测交易品种的累积过程,即价格在一个方向上缓慢但连续变化。总的来说,不断的积累将导致其方向上的突破。
以下输入参数帮助我们检测单个方向的变化:
sinput string delimeter_09=""; // --- 额外的 总有新高/低选项卡 --- input ENUM_TIMEFRAMES alltime_period=PERIOD_D1; // 时段 input int alltime_count=15; // Check the last specified number of bars(检查最近指定的柱数)
选择代码如下:
if(CopyRates(addonName, alltime_period, 1, alltime_count, rates)==alltime_count){ bool alltimeHigh=true; bool alltimeLow=true; for( int j=1; j<alltime_count; j++ ){ if(rates[j].high>rates[j-1].high){ alltimeHigh=false; } if(rates[j].low<rates[j-1].low){ alltimeLow=false; } } if( alltimeHigh || alltimeLow ){ addonArr.Add(addonName); arrTT.Add(0); } }
交易日以当日的高点/低点结束。
这是我们想添加的最后一个标签。根据交易时间,它允许你检测:
- 在交易开始前——前一天以高/低水平收盘的交易品种;
- 在一个交易日开始后——目前处于当日的高/低交易品种。
人们认为,如果价格在当天的高位/低位关闭,那么买方/卖方就没有时间来执行他们的计划。这就是说,价格将在第二天走到同一个方向。
以下参数将帮助我们寻找正确的交易:
sinput string delimeter_10=""; // --- 额外的 最高价=收盘价 选项卡 --- input ENUM_TIMEFRAMES highclose_period=PERIOD_D1; // 时段 input int highclose_offset=0; // 最高价/最低价偏移点数
在寻找一天的高点/低点时,价格可能不一定直接接近一个极限点,而且可能会从边境价格上退几个甚至十几个点。“点的高低误差范围”参数允许定义允许的回滚点。
标签代码如下:
if(CopyRates(addonName, highclose_period, 0, 1, rates)==1){ if( rates[0].close+highclose_offset >= rates[0].high ){ addonArr.Add(addonName); arrTT.Add(0); }else if( rates[0].close-highclose_offset <= rates[0].low ){ addonArr.Add(addonName); arrTT.Add(0); } }
添加自定义选项卡
当然,我们还没有探索所有可能的模式。如果您使用其他模式并具有MQL中的编程技能,则可以轻松地将自己的选项卡添加到实用程序中。欢迎您为自己的选项卡发布代码,并在注释中解释它们的搜索模式。
我们也非常感谢对上述规格的改进建议。
最后,让我提醒您如何将自动选择选项卡添加到实用程序中。这可以分两步完成。
首先,将新选项卡的名称添加到面板名称“加载项”选项卡名称数组中,不要忘记将数组的大小增加1。
第二步是向show_symbols函数的switch操作符添加一个新的case,并向最后一个数字添加1。检查当前事务种类是否满足要求的代码在case操作符内部实现。代码模板如下:
case index: // 选项卡名称 for( int i=0; i<tmpSymbols.Total(); i++ ){ addonName=tmpSymbols[i]; // 检查代码 } break;
结论
我们进一步扩展了实用程序的功能。我相信它对交易者更有用。
在本文中,我们没有根据mql语言版本重写代码,因为它在mql4和mql5中都有效。
如您所见,在MQL中开发跨平台实用程序并不是一个很大的挑战,而且MQL4以类似的方式支持大多数MQL5函数。因此,可能值得暂时忘记MQL5的各种类和其他独特功能,并允许尽可能多的交易者使用您的工作。
当然,我不是在向班级宣战。类已经存在于MQL4中,并且已经出现在代码基中。我只建议将MQL5的这个功能推迟一段时间。
本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/5517。
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。