利用解析入场点为指标的技术创建新的交易策略(基础篇)
◍ 把解析入场点做成指标来搭策略
在 MT5 里搭新策略,一个直接思路是把「解析入场点」(ZigZag 类拐点、突破位等)做成自定义指标,而不是只写在 EA 里。这样拐点计算与信号展示分离,看图阶段就能先验证逻辑对不对。 官方社区 2017-12-20 发布的这篇技术文当时有 8252 次查看、6 条讨论,说明用指标封装入场解析是很常规的做法,不是偏门。 做的时候先写指标算出入场位缓冲到数组,EA 只负责读指标缓冲做下单判定,后期调参数不碰 EA 主体,回测切换更快。外汇和贵金属波动大、杠杆高,这类拐点指标在单边行情里会反复重画,实盘前务必用 MT5 策略测试器跑至少一年 tick 数据。
为什么绝大多数策略跑不赢那 5%
行业里有个老数字:长期稳定盈利的交易者大约只占 5%,但想做到这点的人接近 100%。文献和网站上能翻出几百种策略,指标信号写得明明白白,可这个 5% 从来没变成 10%,更别提 100%。 一种常见说法是市场本质不稳定,旧策略迟早失效。换句话说,照搬现成信号很难跨越那道概率墙,尤其在外汇和贵金属这种高杠杆、高波动市场,回测好看实盘吃瘪太正常。 换个思路:不依赖现成策略框架,而是把入场逻辑拆成指标原语,用「白纸法」自己拼信号组合。这样能逼自己重新审视每个指标的触发条件,而不是被说明书牵着走。 下一篇会直接给一套自定义指标模板的搭建流程,你可以在 MT5 里边抄边改参数验证。
「用虚拟订单把指标信号摊开来验」
价格一直在跳,潜意识里总觉得随时进场就能赚,但下一根蜡烛往哪走、信号怎么叠加,光靠肉眼盯指标交叉靠不住。这篇文章的做法不神秘:把开仓动作直接绑定到指标数值上,等同样的条件再现,历史盈利因子就成了一面可复盘的镜子。外汇和贵金属杠杆高,这种回测只是概率参考,不是进场就躺赢的保证。 净仓账户没法在同品种上双向真下单,所以得自己造一个虚拟订单类 CDeal 来替终端记账。初始化时只传品名、方向、开仓时间、开仓价、止损止盈,仓量故意省掉——我们只关心价格走势,利润按点值算,不按币值算。类里挂了 IsClosed、Type、GetProfit、GetTime 几个服务函数,分笔报价进来就丢给 Tick 按止损止盈价验是否平仓并累计点值盈亏。 指标侧用之前写过的类做容器,再包一层 CDealsToIndicators 统管多指标数组。EA 主体在策略测试器里跑,演示选了 ADX、Alligator、CCI、Chaikin、Force Index、MACD 共 6 个指标,每个都盯 3 个时间帧、各 3 组参数;止损止盈绑 ATR,用盈亏比反推距离。OnTick 分两块:先扫旧虚拟单看是否触发损益,再用 last_closed_deal 跳过已平部分;新柱开盘才抓 ATR 算距、双向开虚拟仓并存指标快照。 下面这段是 CDeal 的核心声明与 Tick 开头,注意它用 d_ClosePrice>0 判定已平仓,Buy 方向的 case 留了口子给后续价格判断。想验证就把它塞进 MT5 的 EA 工程,接上你自己的指标类跑一轮。
class CDeal : class="kw">public CObject { class="kw">private: class="type">class="kw">string s_Symbol; class="type">class="kw">datetime dt_OpenTime; class=class="str">"cmt">// 开仓时间 class="type">class="kw">double d_OpenPrice; class=class="str">"cmt">// 开仓价格 class="type">class="kw">double d_SL_Price; class=class="str">"cmt">// 仓位的止损位 class="type">class="kw">double d_TP_Price; class=class="str">"cmt">// 仓位的止盈位 class="type">ENUM_POSITION_TYPE e_Direct; class=class="str">"cmt">// 开仓方向 class="type">class="kw">double d_ClosePrice; class=class="str">"cmt">// 平仓价格 class="type">int i_Profit; class=class="str">"cmt">// 仓位盈利的点值 class=class="str">"cmt">//--- class="type">class="kw">double d_Point; class="kw">public: CDeal(class="type">class="kw">string symbol, class="type">ENUM_POSITION_TYPE type,class="type">class="kw">datetime time,class="type">class="kw">double open_price,class="type">class="kw">double sl_price, class="type">class="kw">double tp_price); ~CDeal(); class=class="str">"cmt">//--- 检查状态 class="type">bool IsClosed(class="type">void); class="type">ENUM_POSITION_TYPE Type(class="type">void) { class="kw">return e_Direct; } class="type">class="kw">double GetProfit(class="type">void); class="type">class="kw">datetime GetTime(class="type">void) { class="kw">return dt_OpenTime; } class=class="str">"cmt">//--- class="type">void Tick(class="type">void); }; class="type">void CDeal::Tick(class="type">void) { if(d_ClosePrice>class="num">0) class="kw">return; class="type">class="kw">double price=class="num">0; class="kw">switch(e_Direct) { case POSITION_TYPE_BUY:
◍ 多单空单的平仓判定与指标容器类
回测里逐根 K 线推进时,要先按持仓方向取实时报价:多单看 SYMBOL_BID,空单看 SYMBOL_ASK,这是 MT5 跟实盘撮合一致的关键细节。 若多单设置了止损价 d_SL_Price 且报价已跌破或等于它,就以该 price 平仓,盈利点数用 (平仓价-开仓价)/d_Point 强转 int;若没触发止损但报价触及止盈 d_TP_Price,则走 else 分支算同样结构的利润。空单逻辑镜像:ASK 价上穿止损或下穿止盈才收单,利润公式换成 (开仓价-平仓价)/d_Point。 下面这段 CDealsToIndicators 类负责把成交记录映射到各类指标实例。它用指针数组挂了 ADX、Alligator、MACD 及对应的 Static 版本,并暴露 AddADX / AddAlligator 等模板化注册接口,方便把不同品种周期的指标动态塞进回测框架。外汇与贵金属回测请务必留意点差和滑点可能让上述触发价与实际成交偏离,属于高风险验证环节。
price=SymbolInfoDouble(s_Symbol,SYMBOL_BID); if(d_SL_Price>class="num">0 && d_SL_Price>=price) { d_ClosePrice=price; i_Profit=(class="type">int)((d_ClosePrice-d_OpenPrice)/d_Point); } else { if(d_TP_Price>class="num">0 && d_TP_Price<=price) { d_ClosePrice=price; i_Profit=(class="type">int)((d_ClosePrice-d_OpenPrice)/d_Point); } } break; case POSITION_TYPE_SELL: price=SymbolInfoDouble(s_Symbol,SYMBOL_ASK); if(d_SL_Price>class="num">0 && d_SL_Price<=price) { d_ClosePrice=price; i_Profit=(class="type">int)((d_OpenPrice-d_ClosePrice)/d_Point); } else { if(d_TP_Price>class="num">0 && d_TP_Price>=price) { d_ClosePrice=price; i_Profit=(class="type">int)((d_OpenPrice-d_ClosePrice)/d_Point); } } break; } } class CDealsToIndicators { class="kw">private: CADX *ADX[]; CAlligator *Alligator[]; COneBufferArray *OneBuffer[]; CMACD *MACD[]; CStaticOneBuffer *OneBufferStatic[]; CStaticMACD *MACD_Static[]; CStaticADX *ADX_Static[]; CStaticAlligator *Alligator_Static[]; class="kw">template<class="kw">typename T> class="type">void CleareArray(T *&array[]); class="kw">public: CDealsToIndicators(); ~CDealsToIndicators(); class=class="str">"cmt">//--- class="type">bool AddADX(class="type">class="kw">string symbol, ENUM_TIMEFRAMES timeframe, class="type">int period, class="type">class="kw">string name); class="type">bool AddADX(class="type">class="kw">string symbol, ENUM_TIMEFRAMES timeframe, class="type">int period, class="type">class="kw">string name, class="type">int &handle); class="type">bool AddAlligator(class="type">class="kw">string symbol,ENUM_TIMEFRAMES timeframe,class="type">uint jaw_period, class="type">uint jaw_shift, class="type">uint teeth_period, class="type">uint teeth_shift, class="type">uint lips_period, class="type">uint lips_shift, ENUM_MA_METHOD method, ENUM_APPLIED_PRICE price, class="type">class="kw">string name);
把指标句柄塞进多周期框架
这段代码展示了在 MT5 里把鳄鱼、MACD 这类经典指标挂到不同周期与品种上的封装思路。AddAlligator 和 AddMACD 都提供了带 handle 引用参数的重载,意味着你能在 EA 初始化阶段拿到指标句柄,后续直接靠 CopyBuffer 取数,不必每次重算。 注意输入参数段:ATR_Period 默认 288,对应 M5 图表上约一个交易日的波动采样;三个时间框架分别锁死 PERIOD_M5、PERIOD_H1、PERIOD_D1,构成短中长三层过滤。ADX 周期设了 14 / 28 / 56 三档,鳄鱼颌部周期 13、偏移 8,都是原版比尔·威廉姆斯参数的直接落地。 外汇与贵金属杠杆高、滑点跳空频繁,多周期共振也只是提高概率,不代表信号必中。把下面接口原样贴进你的类定义,编译后开 MT5 用符号变量面板改 ADX_Period3 到 89,观察日线层趋势阈值是否更钝化。
class="type">bool AddAlligator(class="type">class="kw">string symbol,ENUM_TIMEFRAMES timeframe,class="type">uint jaw_period, class="type">uint jaw_shift, class="type">uint teeth_period, class="type">uint teeth_shift, class="type">uint lips_period, class="type">uint lips_shift, ENUM_MA_METHOD method, ENUM_APPLIED_PRICE price, class="type">class="kw">string name, class="type">int &handle); class="type">bool AddMACD(class="type">class="kw">string symbol, ENUM_TIMEFRAMES timeframe, class="type">uint fast_ema, class="type">uint slow_ema, class="type">uint signal, ENUM_APPLIED_PRICE applied_price, class="type">class="kw">string name); class="type">bool AddMACD(class="type">class="kw">string symbol, ENUM_TIMEFRAMES timeframe, class="type">uint fast_ema, class="type">uint slow_ema, class="type">uint signal, ENUM_APPLIED_PRICE applied_price, class="type">class="kw">string name, class="type">int &handle); class="type">bool AddOneBuffer(class="type">int handle, class="type">class="kw">string name); class=class="str">"cmt">//--- class="type">bool SaveNewValues(class="type">long ticket); class=class="str">"cmt">//--- class="type">bool Static(CArrayObj *deals); }; class=class="str">"cmt">//--- 输入参数 input class="type">class="kw">double Reward_Risk = class="num">1.0; input class="type">int ATR_Period = class="num">288; input ENUM_TIMEFRAMES TimeFrame1 = PERIOD_M5; input ENUM_TIMEFRAMES TimeFrame2 = PERIOD_H1; input ENUM_TIMEFRAMES TimeFrame3 = PERIOD_D1; input class="type">class="kw">string s1 = "ADX" ; class=class="str">"cmt">//--- input class="type">uint ADX_Period1 = class="num">14 ; input class="type">uint ADX_Period2 = class="num">28 ; input class="type">uint ADX_Period3 = class="num">56 ; input class="type">class="kw">string s2 = "Alligator" ; class=class="str">"cmt">//--- input class="type">uint JAW_Period1 = class="num">13 ; input class="type">uint JAW_Shift1 = class="num">8 ;
「三周期鳄鱼嘴的参数落地」
把鳄鱼指标叠三层周期,本质是用不同钝化程度的平滑移动平均捕捉多级别趋势共振。下面这组 input 直接定义了三套 JAW/TEETH/LIPS 的period与shift,分别对应快、中、慢三组视角。 第一组快周期里,TEETH 取 8 根 K 线周期、偏移 5;LIPS 取 5 周期、偏移 3;JAW 则放到 26 周期、偏移 16。第二组中 TEETH 拉长到 16(偏移10),LIPS 为 10(偏移6),JAW 为 42(偏移32)。第三组最慢:TEETH 32/偏移20,LIPS 20/偏移12,JAW 42/偏移32 实际在代码里写成 JAW_Period3=42、JAW_Shift3=32。 所有均线方法统一走 MODE_SMMA(平滑移动平均),也就是常说的 鳄鱼默认算法。外汇与贵金属杠杆高、滑点随机,多层参数只是过滤噪音,共振信号也仅代表趋势倾向延续,不代表胜率保证。 开 MT5 把这段直接贴进 EA 的 input 区,改 Alligator_Method 试 MODE_EMA,看慢组 JAW 在 XAUUSD 日线会不会比 SMMA 更早张口。
input class="type">uint TEETH_Period1 = class="num">8 ; input class="type">uint TEETH_Shift1 = class="num">5 ; input class="type">uint LIPS_Period1 = class="num">5 ; input class="type">uint LIPS_Shift1 = class="num">3 ; input class="type">uint JAW_Period2 = class="num">26 ; input class="type">uint JAW_Shift2 = class="num">16 ; input class="type">uint TEETH_Period2 = class="num">16 ; input class="type">uint TEETH_Shift2 = class="num">10 ; input class="type">uint LIPS_Period2 = class="num">10 ; input class="type">uint LIPS_Shift2 = class="num">6 ; input class="type">uint JAW_Period3 = class="num">42 ; input class="type">uint JAW_Shift3 = class="num">32 ; input class="type">uint TEETH_Period3 = class="num">32 ; input class="type">uint TEETH_Shift3 = class="num">20 ; input class="type">uint LIPS_Period3 = class="num">20 ; input class="type">uint LIPS_Shift3 = class="num">12 ; input ENUM_MA_METHOD Alligator_Method = MODE_SMMA ;