使用 MQL5 和 MQL4 开发的选择与导航工具: 增加自动模式搜索和显示侦测到的交易品种·综合运用
(3/3)· 从抛物线到自定义选项卡,十类日内形态一键筛出,告别手动翻数百张图表
「抓 M5 上的抛物线弧形起点」
一段单向行情之后,价格反向走动且每根新柱的低点逐级抬高(或高点逐级压低),这种形态就是抛物线弧形的开端。经验上,这类结构出现后价格倾向于延续新方向,止损可以放在前一根柱的低点附近,仓位风险相对可控。 原文给出的判定很具体:以多头为例,最近两根 M5 柱低点连续高于前一根,而再往前三根低点依次持平或更低,即「三跌两涨」算作多头弧形触发。外汇与贵金属杠杆高,这类信号只是概率优势,不是确定性反转。 下面这段代码在 M5 周期取 6 根 K 线,分别判断低点上移和高点下移两种弧形。直接在 MT5 脚本里跑,能扫出当前品种是否处于弧形初段。 代码逐行拆解:CopyRates 取 M5 最近 6 根 rates;第一重 if 判多头——rates[0].low>rates[1].low 且 rates[1].low>rates[2].low,同时 rates[2]~rates[5] 低点逐根不大于前一根(即前三根走平或下行);符合条件且非跳过品种则记录「品种 (M5)」与标记 0。else if 对称处理空头:高点连续两降、前三根走高或持平,同样入列。
if(CopyRates(addonName, PERIOD_M5, class="num">0, class="num">6, rates)==class="num">6){ if( rates[class="num">0].low>rates[class="num">1].low && rates[class="num">1].low>rates[class="num">2].low && rates[class="num">2].low<=rates[class="num">3].low && rates[class="num">3].low<=rates[class="num">4].low && rates[class="num">4].low<=rates[class="num">5].low ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(class="num">0); } }else if( rates[class="num">0].high<rates[class="num">1].high && rates[class="num">1].high<rates[class="num">2].high && rates[class="num">2].high>=rates[class="num">3].high && rates[class="num">3].high>=rates[class="num">4].high && rates[class="num">4].high>=rates[class="num">5].high ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(class="num">0); } } }
用跳空百分比筛出异动品种
做股票策略时若依赖向上或向下跳空,直接看跳空扫描页比手动翻行情快得多。该页只列出当日(D1)较前一交易日收盘出现明显裂口的标的,默认阈值设为至少 1% 的跳空幅度。 阈值由 Minimum gap size 参数控制,单位是当前开盘价的百分比。把数值调小能看到更多毛刺,调大则只留真正夸张的缺口,外汇和贵金属跳空虽少但开盘裂口风险极高,参数别设太松。 下面这段逻辑就是扫描核心:取两个日线柱,比较今开与昨收的偏离是否超过设定百分比,且不在跳过列表里就收集标的。 别把 1% 当通用标准 股票波动和汇金品种完全不同,做贵金属跨节跳空时 1% 可能天天命中,建议先在 MT5 里把 gap_min 从 1 改到 3 跑一周观察噪音量。
if(CopyRates(addonName, PERIOD_D1, class="num">0, class="num">2, rates)==class="num">2){ if( rates[class="num">0].open>rates[class="num">1].close+(rates[class="num">0].open*(gap_min/class="num">100)) || rates[class="num">0].open<rates[class="num">1].close-(rates[class="num">0].open*(gap_min/class="num">100)) ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(class="num">0); } } }
◍ 四周高低点的扫描逻辑
做横截面扫描时,把视野拉到周线级别能避开日线噪音。这里的核心动作是:取交易品种最近 4 根周线柱(PERIOD_W1,索引 0 到 3),判断当前周收盘价是否落在 4 周区间的最高或最低端。 代码先用 CopyRates 拉 4 根周线,返回值不等于 4 就直接跳过。若本周 close 既不等于 high 也不等于 low,说明价格只是在区间内晃,newMin 和 newMax 同时置 false,不进入候选。 只有当本周收在最高或最低时,才进 for 循环拿前 3 周做比对:只要本周 high 低于任一周 high,newMax 废掉;只要本周 low 高于任一周 low,newMin 废掉。双重过滤后若仍为真,且不被 skip_symbol 排除,就塞进 addonArr 并标 " (M5)"、arrTT 加 0。 在 MT5 里跑这段,你能立刻得到一份「价格刚触碰四周边界」的品种清单。外汇与贵金属波动受杠杆与消息驱动,边界突破后回撤概率不低,真要跟单请先开模拟验证。
if(CopyRates(addonName, PERIOD_W1, class="num">0, class="num">4, rates)==class="num">4){ class="type">bool newMin=true; class="type">bool newMax=true; if( rates[class="num">0].close!=rates[class="num">0].high && rates[class="num">0].close!=rates[class="num">0].low ){ newMin=false; newMax=false; }else{ for( class="type">int j=class="num">1; j<class="num">4; j++ ){ if( rates[class="num">0].high < rates[j].high ){ newMax=false; } if( rates[class="num">0].low > rates[j].low ){ newMin=false; } } } if( newMin || newMax ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(class="num">0); } } }
「用周线 52 根判年高年低」
年度高低点的判定,本质是拿当前周线收盘位置去和过去一年的周线极值比。代码取的是 PERIOD_W1 周期、从 0 号柱往前数 52 根(约一年交易周),用 CopyRates 拉满这 52 根 K 线数据,返回数不等于 52 就直接跳过。 先盯当前周线本体:若 close 既不是 high 也不是 low,说明这周没创年极值的相,newMin 和 newMax 直接双否。只有 close 顶到 high 或 low 时,才进循环拿 0 号柱和 1~51 号柱逐根比——只要历史上任一周 high 更高就推翻 newMax,任一周 low 更低就推翻 newMin。 两个标志任一为真且不被 skip_symbol 过滤,就把品种名加 '(M5)' 塞进数组。外汇和贵金属波动受宏观事件驱动,年高低破位后反向插针概率不低,真要跟单得先在 MT5 里把这段跑一遍看信号密度。 下面这段是核心判定逻辑,逐行拆一下: if(CopyRates(addonName, PERIOD_W1, 0, 52, rates)==52) // 向品种 addonName 取周线最近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{ // 收线触到 high 或 low 才值得比 for( int j=1; j<52; j++ ){ // 遍历前51周 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, class="num">0, class="num">52, rates)==class="num">52){ class="type">bool newMin=true; class="type">bool newMax=true; if( rates[class="num">0].close!=rates[class="num">0].high && rates[class="num">0].close!=rates[class="num">0].low ){ newMin=false; newMax=false; }else{ for( class="type">int j=class="num">1; j<class="num">52; j++ ){ if( rates[class="num">0].high < rates[j].high ){ newMax=false; } if( rates[class="num">0].low > rates[j].low ){ newMin=false; } } } if( newMin || newMax ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(class="num">0); } } }
用取整价筛出潜在磁吸品种
不少交易者把以 00 或 50 结尾的价格(如 125.00、79.50)视作天然的支撑阻力带,认为价格靠近这些整数位时容易出现停顿或反转。这种心理价位在外汇和贵金属上同样存在,但波动杠杆高、滑点风险大,靠近并不代表一定会回头。 下面这段逻辑只处理两位小数品种(如多数外汇对),通过 M5 收盘价小数部分判断是否在取整区间:0.46~0.54 算贴近 .50,小于 0.04 或大于 0.96 算贴近 .00。命中且未被 skip_symbol 排除的,就加入监控列表。 代码逐行拆解:
- switch((int)SymbolInfoInteger(addonName, SYMBOL_DIGITS)):取品种小数位数,按位数分支。
- case 0: break;:无小数位的品种直接跳过。
- case 2::两位小数品种进入判断。
- if(CopyRates(addonName, PERIOD_M5, 0, 1, rates)==1):取 M5 最新 1 根 K 线数据。
- double tmpRound=rates[0].close - (int)rates[0].close:取收盘价小数部分。
| - if((tmpRound>0.46 && tmpRound<0.54) | tmpRound>0.96 | tmpRound<0.04):判定是否落在 .50 或 .00 邻近带。 |
|---|
- if(!skip_symbol(addonName)):确认该品种未被手动跳过。
- addonArr.Add(addonName+" (M5)"); arrTT.Add(0);:加入监控数组并置计时。
如果你跑 EURUSD 这类两位品种,把这段接进自己的扫描器,就能在 MT5 里实时捞出“价格正贴着整数口”的标的,再结合手动价态确认。
class="kw">switch((class="type">int) SymbolInfoInteger(addonName, SYMBOL_DIGITS)){ case class="num">0: break; case class="num">2: if(CopyRates(addonName, PERIOD_M5, class="num">0, class="num">1, rates)==class="num">1){ class="type">class="kw">double tmpRound=rates[class="num">0].close - (class="type">int) rates[class="num">0].close; if( (tmpRound>class="num">0.46 && tmpRound<class="num">0.54) || tmpRound>class="num">0.96 || tmpRound<class="num">0.04 ){ if(!skip_symbol(addonName)){ addonArr.Add(addonName+" (M5)"); arrTT.Add(class="num">0); } } } break; }
◍ 单向碾压的标的怎么捞出来
有些交易品种在一段时期内几乎只朝一个方向走,这种单向性强的资产对外汇、贵金属交易者来说值得单独盯——但符合这样特征的品种通常不多,属于小众筛选需求。 逻辑上用两个参数卡住:mostly_count 控制回看天数(默认 15,即在 D1 周期看最近 15 根日线),mostly_percent 控制单向占比阈值(默认 90,意味着 15 天里至少有 90% 的天数收阳或收阴才入选)。实际跑下来,90% 太严,能命中的符号极少,把百分比调到 70~80 区间更可能出结果。 代码里先 CopyRates 拉日线,逐根比 close 和 open 统计 mostlyLong / mostlyShort。若某一方向占比 ≥ mostly_percent 就加入监控数组。外汇与贵金属波动受杠杆和事件驱动影响大,这类单边品种反转风险高,参数验证请在 MT5 策略测试器或自定义脚本里实跑。
sinput class="type">class="kw">string delimeter_08=""; class=class="str">"cmt">// --- 额外的多数上涨/下跌选项卡 --- input class="type">int mostly_count=class="num">15; class=class="str">"cmt">// Check the last specified number of days(检查最近指定的天数) input class="type">int mostly_percent=class="num">90; class=class="str">"cmt">// Specified percentage in one direction exceeded(指定在一个方向上超过的百分比) if(CopyRates(addonName, PERIOD_D1, class="num">1, mostly_count, rates)==mostly_count){ class="type">int mostlyLong=class="num">0; class="type">int mostlyShort=class="num">0; for( class="type">int j=class="num">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(class="num">0); }else if( ((mostlyLong*class="num">100)/(mostlyLong+mostlyShort)) >= mostly_percent ){ addonArr.Add(addonName); arrTT.Add(class="num">0); }else if( ((mostlyShort*class="num">100)/(mostlyLong+mostlyShort)) >= mostly_percent ){ addonArr.Add(addonName); arrTT.Add(class="num">0); } }
「用逐柱高低点扫出慢速积累」
积累行情的典型特征是价格朝一个方向挪动得很慢,但每一根都在悄悄抬高低点或者压低高点。这种结构持续一段时间后,往往倾向出现一根同向大柱完成突破,外汇与贵金属市场里这类形态伴随的滑点和反转风险都偏高。 下面这段逻辑就是在一组柱里逐根比对高低点:只要存在连续新高或连续新低,就判定该品种处于单向积累。默认取日线最近 15 根做检查,你可以直接改 alltime_count 看不同样本长度下的命中率。 代码逐行拆解: sinput string delimeter_09=""; // 分隔符占位,无实际过滤作用 input ENUM_TIMEFRAMES alltime_period=PERIOD_D1; // 取日线周期 input int alltime_count=15; // 检查最近 15 根柱 if(CopyRates(addonName, alltime_period, 1, alltime_count, rates)==alltime_count){ // 成功拷贝 15 根汇率数据才继续 bool alltimeHigh=true; // 先假设全是新高 bool alltimeLow=true; // 先假设全是新低 for(int j=1; j<alltime_count; j++){ // 从第 2 根开始比前一根 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); // 标记类型 0 } } 把 alltime_period 换成 PERIOD_H4 再跑一遍,能更早发现日内积累,但假信号概率会上升,需要配合成交量或 ATR 过滤。
sinput class="type">class="kw">string delimeter_09=""; class=class="str">"cmt">// --- 额外的 总有新高/低选项卡 --- input ENUM_TIMEFRAMES alltime_period=PERIOD_D1; class=class="str">"cmt">// 时段 input class="type">int alltime_count=class="num">15; class=class="str">"cmt">// Check the last specified number of bars(检查最近指定的柱数) if(CopyRates(addonName, alltime_period, class="num">1, alltime_count, rates)==alltime_count){ class="type">bool alltimeHigh=true; class="type">bool alltimeLow=true; for( class="type">int j=class="num">1; j<alltime_count; j++ ){ if(rates[j].high>rates[j-class="num">1].high){ alltimeHigh=false; } if(rates[j].low<rates[j-class="num">1].low){ alltimeLow=false; } } if( alltimeHigh || alltimeLow ){ addonArr.Add(addonName); arrTT.Add(class="num">0); } }
收盘钉在极值边上的品种怎么捞
做品种扫描时,最后一个实用维度是「交易日收在当日高/低附近」。逻辑很直接:若收盘价贴着当天极值,说明对手盘没来得及把计划打完,次日沿原方向延续的概率倾向更高——外汇和贵金属波动受 session 切换影响大,这类形态只是候选而非信号,实盘仍属高风险。 实际写 EA 扫描别死磕绝对触碰。价格离高点差几个点、甚至十几个点都算「没离开极值区」,硬等 close==high 会漏掉一堆。用偏移容差参数把边界放宽,才是能跑的过滤条件。 下面这段是日线级别判定的核心:取 D1 的 close 与 high/low 比,加容差后命中就塞进待观察数组。highclose_offset 默认 0,建议 EURUSD 这类点值小的给 3~5 点,XAUUSD 给 10~15 点更稳。 别把收盘贴极值当圣经 容差设太大(比如 50 点)会把普通震荡收盘也收编,扫描列表膨胀后反而没法盯。先在 MT5 用单品种手动验一遍再批量跑。
<span class="keyword">sinput</span> <span class="keyword">class="type">class="kw">string</span> delimeter_10=<span class="class="type">class="kw">string">""</span>; <span class="comment">class=class="str">"cmt">// --- 额外的 最高价=收盘价 选项卡 ---</span> <span class="keyword">input</span> <span class="macro">ENUM_TIMEFRAMES</span> highclose_period=<span class="macro">PERIOD_D1</span>; <span class="comment">class=class="str">"cmt">// 时段</span> <span class="keyword">input</span> <span class="keyword">class="type">int</span> highclose_offset=<span class="number">class="num">0</span>; <span class="comment">class=class="str">"cmt">// 最高价/最低价偏移点数</span> <span class="keyword">if</span>(<span class="functions">CopyRates</span>(addonName, highclose_period, <span class="number">class="num">0</span>, <span class="number">class="num">1</span>, rates)==<span class="number">class="num">1</span>){ <span class="keyword">if</span>( rates[<span class="number">class="num">0</span>].close+highclose_offset >= rates[<span class="number">class="num">0</span>].high ){ addonArr.Add(addonName); arrTT.Add(<span class="number">class="num">0</span>); }<span class="keyword">else</span> <span class="keyword">if</span>( rates[<span class="number">class="num">0</span>].close-highclose_offset <= rates[<span class="number">class="num">0</span>].low ){ addonArr.Add(addonName); arrTT.Add(<span class="number">class="num">0</span>); } }
◍ 往面板里塞自己的筛选选项卡
这套工具没把模式写死,懂 MQL5 的人完全可以自己扩。想在面板里多一个自动挑品种的标签页,不用动整体架构,两处改动就能跑起来。 第一步,把新标签的名字填进 panelNamesAddon 这个名称数组,同时把数组声明的大小加 1,否则越界读不到。第二步,到 show_symbols 函数的 switch 里补一个 case,编号用现有最大数字加 1,把品种遍历和判定逻辑写进这个 case 内。 下面这段是 case 骨架,直接照着填判定条件即可:遍历临时品种池 tmpSymbols,逐个取名字做检查,不合条件就跳过,合条件自然留在结果里。 外汇与贵金属波动剧烈、杠杆风险高,自建筛选只解决「找品种」,不解决「何时下手」,上线前请在 MT5 策略测试器用历史数据验一遍命中率。
case index: class=class="str">"cmt">// 选项卡名称 for( class="type">int i=class="num">0; i<tmpSymbols.Total(); i++ ){ addonName=tmpSymbols[i]; class=class="str">"cmt">// 检查代码 } break;
「跨平台实用工具的取舍思路」
这套查找工具给出了 ex5 与 ex4 两套产物:finder.ex5 体积 270.41 KB、finder.mq5 源码 186.66 KB,finder4.ex4 为 178.96 KB、finder4.mq4 同源 186.66 KB。同一套逻辑在 MQL4 与 MQL5 下都能编译运行,说明绕开 MQL5 特有的类体系、用两版语言都支持的基础写法,确实能把工具覆盖面拉到两个平台。 外汇与贵金属交易本身杠杆高、滑点跳空频繁,任何工具只降低操作成本,不替你过滤风险。 如果接下来要改这类跨平台脚本,先把用到的数组和文件读写换成两版都兼容的写法,再分别用 MT4、MT5 各编译一次,比急着上 CIndicator 之类 MQL5 专属类更实在。