MQL5 酷客宝典 - 轴点交易信号·综合运用
📘

MQL5 酷客宝典 - 轴点交易信号·综合运用

第 3/3 篇

◍ 给轴点策略加趋势过滤器与动态手数

看图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 和日均线开盘价与轴点的比较才是触发前提:

MQL5 / C++
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 是否随枢轴接触触发。

MQL5 / C++
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 的价位。 外汇与贵金属杠杆高、滑点大,这类边界判定只降低无效触发概率,不保证信号一定有效,实盘前请在策略测试器用历史数据跑一遍确认。

MQL5 / C++
  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 这个容差参数,就能看不同触碰宽松度下信号频率怎么变,枢轴策略的边界自己跑一遍最清楚。

MQL5 / C++
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;
}

常见问题

用均线或 ADX 做方向门槛,只在价格站在均线同侧且 ADX 大于 20 时才允许轴点信号入场,逆势触发直接跳过。
可以设手数系数 = 基础手数 × (ATR / 参考ATR),波动放大就加仓、缩小就减仓,控制单笔风险一致。
小布可接入你的品种页,按你设的顶部底部边界自动标红无效轴点形态,只推送通过过滤的买卖触发点。
在代码里加价格必须低于近期顶部阈值且高于底部阈值的条件,触发行价出圈就判为无效不下单。
买条件放 OnTick 的 buy 分支、卖条件放 sell 分支,独立成函数后改一侧不影响另一侧逻辑。