某个周二下午,我盯着EURUSD的H1图表,眼看着价格在一个关键支撑位附近走出一根教科书式的锤子线。长下影、小实体、基本没有上影线——教科书上说的要素它全占了。但说实话,我之前被锤子线坑过好几次。复盘的时候看着都挺像那么回事,实盘一跑,大半都是噪音。从那时候起我就决定,不能再靠眼睛去盯了,得自己写个靠谱的识别程序。
新手写MQL5形态识别最容易踩的坑,就是用死板固定的点数去定义形态。实体小于10个点?下影线是实体的两倍?这个参数在伦敦盘EURUSD上可能还行,换到亚洲盘的GBPJPY,或者从5分钟图切到日线图,效果就完全没法看了。我之前随手写的一个检测器回测下来,假信号差不多占了四成,问题就出在这儿。
ATR这个思路是怎么来的
MQL5官方文档(docs.mql5.com)里对
iATR()函数的用法写得很清楚,计算平均真实波幅就是几行代码的事。但文档里没告诉你的是,ATR真正的价值在于用它替代固定点数阈值,来做K线形态的动态过滤。折腾了三周之后,我最终定下来的逻辑是这样的:不写"实体<10点",而是写"实体<0.3倍ATR(14)";不写"下影线>2倍实体",而是写"下影线>=2倍实体且下影线>0.5倍ATR(14)"。后面这个条件是为了防止在波动率极低的市场里,即使影线和实体的比例凑够了2:1,但绝对值只有几个点——那种情况基本就是噪音,不值得当成信号。实测跑通的代码
下面这个版本在我在模拟盘上跑了两个月,效果还挺稳定:
``
mql5
//+------------------------------------------------------------------+
//| 锤子线形态检测函数 |
//+------------------------------------------------------------------+
bool IsHammer(int shift, double atrValue)
{
// 用显式指定品种和周期的方式获取OHLC数据
double open = iOpen(_Symbol, _Period, shift);
double high = iHigh(_Symbol, _Period, shift);
double low = iLow(_Symbol, _Period, shift);
double close = iClose(_Symbol, _Period, shift);
double body = MathAbs(close - open);
double lowerShadow = MathMin(open, close) - low;
double upperShadow = high - MathMax(open, close);
double range = high - low;
// 基础结构检查
if(body == 0 || range == 0) return false;
// 用ATR做的动态条件
if(body > 0.3 atrValue) return false; // 实体太大
if(lowerShadow < 2 body) return false; // 下影线太短
if(lowerShadow < 0.5 atrValue) return false; // 下影线绝对值太小
if(upperShadow > 0.3 body) return false; // 存在上影线
// 实体必须位于K线的上半部分
double lowerBody = MathMin(open, close);
if(lowerBody > low + 0.3 range) return false;
return true;
}
`
我想特别提一下第三个条件——lowerShadow < 0.5 atrValue。这个条件是我在复盘GBPJPY低波动时段那些假信号之后加上去的。MQL5文档里关于ATR的说明没有提到这个用法,但从实际效果来看,有没有这个条件,直接决定了检测器是靠谱还是瞎报。
趋势背景是个硬门槛
就算加了ATR过滤,在横盘震荡市里我还是会收到一堆锤子信号,但走出来的行情根本不买账。后来我加了趋势验证。MQL5社区那几篇讲K线编程的文章里反复强调过一点:锤子线只有在下跌趋势末端出现才有实际意义。在盘整行情里,锤子就是一根普通的长影线K线,别想太多。
我的趋势过滤长这样:
`mql5
bool IsDowntrend(int shift)
{
double maCurrent = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE, shift);
double maPrev = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE, shift + 5);
return (maCurrent < maPrev);
}
`
这里的shift + 5是我自己试出来的,比用shift + 1判断均线斜率要平滑一些,能过滤掉一些小级别的毛刺,少很多假信号。
搞一个可复用的形态库
折腾完这个锤子线之后,我学乖了——与其每次写一个单例函数,不如直接攒一个形态识别库。MQL5那篇《从新手到专家:对K线进行编程》里也推荐了这个做法。我现在有一个独立的include文件,里面集中放了锤子线、射击之星、吞没、十字星、晨星的检测函数,每个函数都接收ATR值作为参数,所有形态共用同一套波动率校准逻辑。
这么做的好处是,做策略回测的时候,形态识别的规则是统一的。比如我调了锤子线的ATR倍数但忘记同步更新吞没形态的参数,那回测出来的结果就是一笔糊涂账,完全没法用。
文档里不会写的坑
最后说一个官方文档里完全没有提到的事:用策略测试器做历史回测时,iATR()的行为和实盘图表上不一样。测试器里ATR是基于回测时刻可用的历史数据计算的,但如果开了多线程优化,不同线程之间的ATR值可能会串。我自己的解决方式是,在同一个循环里用CopyRates()`手动计算ATR,跟形态检测放在一起算,确保两组计算用的是同一份数据。参考来源:MetaQuotes MQL5文档(docs.mql5.com)中关于iATR和iMA函数的说明;MQL5社区文章《从新手到专家:对K线进行编程》(www.mql5.com)。
本文首发于FXEAR.com,原创内容,未经授权禁止转载。