MQL5 酷客宝典 - 轴点交易信号·综合运用
◍ 给轴点策略加趋势过滤器与动态手数
看图6的余额曲线能直接发现,利润积累极不均衡:有的片段稳定向上,有的横盘不动,还有明显回撤。原策略在2014到2015年初的EURUSD下跌段表现最好,说明它本质上吃趋势,平盘期就容易亏。 作者写了个 MaTrendCatcher 指标做过滤器:快、慢均线差为正时柱图标1(涨势),为负标-1(跌势);差比前一根扩大柱变绿(趋势加速),否则红;差值过小按参数 Cutoff, pp 隐藏不画,避免弱趋势噪音。 EA 里从 CExpertSignal 派生自定义类,新增子模式「trend-flat-countertrend」作为模型0补充。买入确认逻辑变成:不考虑子模式走模型0权重;考虑后分四种——趋势加速顺势、趋势非加速顺势、反趋势加速、反趋势非加速。信号强就多开仓,CPivotsExpert::LotCoefficient() 里信号120级手数调1.2,70级调0.7。 另外加 Width limit, pp 参数,市场波动小、轴点挨太近时禁止交易。Direction() 里卡这道关,没过会在日志提示缺了26个点之类的信号被弃用。 优化跑下来(Width limit / Tolerance / Fast MA / Slow MA / Cut-off 五个参数),EURUSD 2013-2016 曲线平滑些但仍失败,2015年没明显利润;USDJPY 同段略好;XAUUSD 因贵金属窄幅震荡给不出好结果;GBPUSD 趋势段好但2015年回调把利润毁了。结论很直白:这思路有趋势才管用。外汇和贵金属波动剧烈、杠杆高风险大,过滤只能降频不能消灭亏损。 下面这段是买入条件检查的 MQL5 代码骨架,注意 m_pattern_0_done 和日均线开盘价与轴点的比较才是触发前提:
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 检查买入条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CSignalPivots::LongCondition(class="type">void) { class="type">int result=class="num">0; class=class="str">"cmt">//--- 如果没有使用模式 class="num">0 if(IS_PATTERN_USAGE(class="num">0)) class=class="str">"cmt">//--- 如果模式 class="num">0 没有完成 if(!m_pattern_0_done) { m_is_signal=class="kw">false; class=class="str">"cmt">//--- 如果一天的开盘价低于轴点 if(m_daily_open_pr<m_pivot_val) {
「枢轴点触发的买信号与手数系数」
这段逻辑展示了 EA 如何在上一根柱最高价接触枢轴位上方阈值时,把模式 0 标记为有效买信号,并写入日志。判定条件是 last_high 必须大于等于 m_pivot_val 减去 m_pnt_near 的容差,且价格本身处于合法区间(大于 WRONG_VALUE 且小于 DBL_MAX)。 模式 1 作为叠加项参与评分:仅当趋势值 m_trend_val 为正且非 EMPTY_VALUE 时才计入。若此时 m_trend_color 等于 0(视为加速),结果加上 m_pattern_1 与 m_speedup_allowance 之和;否则减去该容差,反映无加速时的弱化权重。 手数比例函数 LotCoefficient 直接取信号方向的绝对值代入:lot_coeff = NormalizeDouble(MathAbs(dir_val/100.), 2),意味着方向值每偏离 100 点,系数放大 1.0 倍并经两位小数裁剪。外汇与贵金属杠杆品种波动剧烈,该系数会直接放大敞口,实盘前务必在 MT5 策略测试器用历史数据核对 dir_val 分布。 OpenLong 入口先拦截 price==EMPTY_VALUE,避免以无效报价发单。你可以把这段源码贴进 MT5 的 EA 头文件,编译后观察日志里 Print 输出的 last_high 与 ORDER_TYPE_BUY 是否随枢轴接触触发。
class="type">class="kw">double last_high=m_high.GetData(class="num">1); if(last_high>WRONG_VALUE && last_high<DBL_MAX) if(last_high>=(m_pivot_val-m_pnt_near)) { result=m_pattern_0; m_is_signal=true; this.Print(last_high,ORDER_TYPE_BUY); } if(IS_PATTERN_USAGE(class="num">1)) { if(m_trend_val>class="num">0. && m_trend_val!=EMPTY_VALUE) { if(m_trend_color==class="num">0. && m_trend_color!=EMPTY_VALUE) result+=(m_pattern_1+m_speedup_allowance); else result+=(m_pattern_1-m_speedup_allowance); } } class="type">class="kw">double CPivotsExpert::LotCoefficient(class="type">void) { class="type">class="kw">double lot_coeff=class="num">1.; CExpertUserSignal *ptr_signal=this.Signal(); if(CheckPointer(ptr_signal)==POINTER_DYNAMIC) { class="type">class="kw">double dir_val=ptr_signal.GetDirection(); lot_coeff=NormalizeDouble(MathAbs(dir_val/class="num">100.),class="num">2); } class="kw">return lot_coeff; } class="type">bool CPivotsExpert::OpenLong(class="type">class="kw">double price,class="type">class="kw">double sl,class="type">class="kw">double tp) { if(price==EMPTY_VALUE)
用顶部与底部限制卡掉无效形态
在 MT5 的 EA 逻辑里,若给形态识别设了宽度限制 m_wid_limit>0,就要先拿枢轴值 m_pivot_val 加上或减去该宽度,用 NormalizePrice 对齐品种小数位,得到 norm_upper_limit 与 norm_lower_limit 两条边界。 随后调用 MajorResistance(0) 取一级阻力、MajorSupport(0) 取一级支撑。当阻力值落在 WRONG_VALUE 与 DBL_MAX 之间,且 res1_val < norm_upper_limit 时,说明价格没有冲破顶部限制,模式 0 直接标记完成并返回 0。 底部同理:若 sup1_val 有效且 norm_lower_limit < sup1_val,同样置 m_pattern_0_done=true 并回 0。日志会按品种 Digits 动态打印估算值与实际值,比如欧元兑美元 Digits=5 时输出形如 1.09532 的价位。 外汇与贵金属杠杆高、滑点大,这类边界判定只降低无效触发概率,不保证信号一定有效,实盘前请在策略测试器用历史数据跑一遍确认。
if(m_wid_limit>class="num">0.) { class=class="str">"cmt">//--- 评估顶部限制 class="type">class="kw">double norm_upper_limit=m_symbol.NormalizePrice(m_wid_limit+m_pivot_val); class=class="str">"cmt">//--- 实际顶部限制 class="type">class="kw">double res1_val=this.MajorResistance(class="num">0); if(res1_val>WRONG_VALUE && res1_val<DBL_MAX) { class=class="str">"cmt">//--- 如果没有超出限制 if(res1_val<norm_upper_limit) { class=class="str">"cmt">//--- 模式 class="num">0 已经完成 m_pattern_0_done=true; class=class="str">"cmt">//--- 写到日志中 Print("\n---== 没有超出顶部限制 ==---"); PrintFormat("估算: %class="num">0."+IntegerToString(m_symbol.Digits())+"f",norm_upper_limit); PrintFormat("实际: %class="num">0."+IntegerToString(m_symbol.Digits())+"f",res1_val); class=class="str">"cmt">//--- class="kw">return class="num">0.; } } class=class="str">"cmt">//--- 估算的底部限制 class="type">class="kw">double norm_lower_limit=m_symbol.NormalizePrice(m_pivot_val-m_wid_limit); class=class="str">"cmt">//--- 真正的底部限制 class="type">class="kw">double sup1_val=this.MajorSupport(class="num">0); if(sup1_val>WRONG_VALUE && sup1_val<DBL_MAX) { class=class="str">"cmt">//--- 如果没有超出限制 if(norm_lower_limit<sup1_val) { class=class="str">"cmt">//--- 模式 class="num">0 已经完成 m_pattern_0_done=true; class=class="str">"cmt">//--- 写到日志中 Print("\n---== 没有超出底部限制 ==---"); PrintFormat("估计值: %class="num">0."+IntegerToString(m_symbol.Digits())+"f",norm_lower_limit); PrintFormat("实际值: %class="num">0."+IntegerToString(m_symbol.Digits())+"f",sup1_val); class=class="str">"cmt">//--- class="kw">return class="num">0.; } }
◍ 多空触发条件的代码落点
日线开盘价与枢轴点的相对位置,是这套枢轴信号分流的第一道闸。2015.08.19 00:01:00 的日志显示评估值 1.10745、真实值 1.10719,未触发顶部限制,说明当时价格贴近枢轴但未形成有效突破,这类状态在 EURUSD 这类品种里每天都可能遇到,外汇与贵金属杠杆高,假触碰概率不低。 LongCondition 里先判 m_daily_open_pr < m_pivot_val,即日线开盘在枢轴下方才看多。上一根最高价 last_high 若大于等于 m_pivot_val - m_pnt_near,视为从下侧碰枢轴,给 Model 0 信号;再叠加趋势字段,m_trend_val>0 且 m_trend_color==0 时加 speedup_allowance,否则减掉,多头评分随之浮动。 ShortCondition 镜像处理:日线开盘高于枢轴才看空,上一根最低价 last_low 小于等于 m_pivot_val + m_pnt_near 算上侧触碰。趋势为负且加速标记成立时同样加分成,否则扣减。两个函数都返回 int 评分而非直接下单,实盘里要自己接仓位管理。 把这两段直接塞进 MT5 的 CSignalPivots 类编译,改 m_pnt_near 这个容差参数,就能看不同触碰宽松度下信号频率怎么变,枢轴策略的边界自己跑一遍最清楚。
class="type">int CSignalPivots::LongCondition(class="type">void) { class="type">int result=class="num">0; class=class="str">"cmt">//--- если Модель class="num">0 учитывается if(IS_PATTERN_USAGE(class="num">0)) class=class="str">"cmt">//--- если Модель class="num">0 не отработана if(!m_pattern_0_done) { m_is_signal=class="kw">false; class=class="str">"cmt">//--- если день открылся ниже пивота if(m_daily_open_pr<m_pivot_val) { class=class="str">"cmt">//--- максимальная цена на прошлом баре class="type">class="kw">double last_high=m_high.GetData(class="num">1); class=class="str">"cmt">//--- если цена получена if(last_high>WRONG_VALUE && last_high<DBL_MAX) class=class="str">"cmt">//--- если было касание снизу (с учётом допуска) if(last_high>=(m_pivot_val-m_pnt_near)) { result=m_pattern_0; m_is_signal=true; class=class="str">"cmt">//--- в Журнал this.Print(last_high,ORDER_TYPE_BUY); } } class=class="str">"cmt">//--- если Модель class="num">1 учитывается if(IS_PATTERN_USAGE(class="num">1)) { class=class="str">"cmt">//--- если на прошлом баре был бычий тренд if(m_trend_val>class="num">0. && m_trend_val!=EMPTY_VALUE) { class=class="str">"cmt">//--- если есть ускорение if(m_trend_color==class="num">0. && m_trend_color!=EMPTY_VALUE) result+=(m_pattern_1+m_speedup_allowance); class=class="str">"cmt">//--- если нет ускорения else result+=(m_pattern_1-m_speedup_allowance); } } } class=class="str">"cmt">//--- class="kw">return result; } class="type">int CSignalPivots::ShortCondition(class="type">void) { class="type">int result=class="num">0; class=class="str">"cmt">//--- если Модель class="num">0 учитывается if(IS_PATTERN_USAGE(class="num">0)) class=class="str">"cmt">//--- если Модель class="num">0 не отработана if(!m_pattern_0_done) { class=class="str">"cmt">//--- если день открылся выше пивота if(m_daily_open_pr>m_pivot_val) { class=class="str">"cmt">//--- минимальная цена на прошлом баре class="type">class="kw">double last_low=m_low.GetData(class="num">1); class=class="str">"cmt">//--- если цена получена if(last_low>WRONG_VALUE && last_low<DBL_MAX) class=class="str">"cmt">//--- если было касание сверху (с учётом допуска) if(last_low<=(m_pivot_val+m_pnt_near)) { result=m_pattern_0; m_is_signal=true; class=class="str">"cmt">//--- в Журнал this.Print(last_low,ORDER_TYPE_SELL); } } class=class="str">"cmt">//--- если Модель class="num">1 учитывается if(IS_PATTERN_USAGE(class="num">1)) { class=class="str">"cmt">//--- если на прошлом баре был медвежий тренд if(m_trend_val<class="num">0. && m_trend_val!=EMPTY_VALUE) { class=class="str">"cmt">//--- если есть ускорение if(m_trend_color==class="num">0. && m_trend_color!=EMPTY_VALUE) result+=(m_pattern_1+m_speedup_allowance); class=class="str">"cmt">//--- если нет ускорения else result+=(m_pattern_1-m_speedup_allowance); } } } class=class="str">"cmt">//--- class="kw">return result; }