动量弹球交易策略·综合运用
📘

动量弹球交易策略·综合运用

第 3/3 篇

◍ 动量弹球策略的开关与缓冲区 wiring

动量弹球(Momentum Pinball)这类 EA 在 MT5 里先把退出方式枚举清楚:EXIT_BUY 只平多、EXIT_SELL 只平空、EXIT_ALL 全平、EXIT_NONE 不动。这个枚举直接决定信号模块反转时怎么减仓,写错一边就可能在该平空时误平多。 信号模块通过 #include <Expert\Signal\Signal_Momentum_Pinball.mqh> 挂进来,外部参数里 TS_MomPin_Take_Profit 默认 10 点,Show_1st_H1_Bar 默认开,意思是把每天第一根 H1 柱的实体范围画出来当参考。Alert 组默认弹窗和推送开、邮件关,Days_Limit=7 限定回溯 7 个日历日,外汇和贵金属波动大,这类提醒参数最好按自己盯盘习惯改,别全信默认值。 OnInit 里先把点数偏移乘 _Point 转成价格:gd_Entry_Offset = TS_MomPin_Entry_Offset * _Point,gd_Exit_Offset 同理。随后用 SetIndexBuffer 把 buff_1st_H1_Bar、buff_Entry、buff_SL、buff_TP 等逐个绑到指标槽位,PlotIndexSetDouble 设 PLOT_EMPTY_VALUE=0 让空值不画。你可以直接把这段 OnInit 粘进自定义指标验证缓冲区编号是否连续,断一个号图形就缺一块。

MQL5 / C++
  EXIT_BUY,                                                    class=class="str">"cmt">// 关闭买入仓位
  EXIT_SELL,                                                  class=class="str">"cmt">// 关闭卖出仓位
  EXIT_ALL,                                                   class=class="str">"cmt">// 关闭全部
  EXIT_NONE                                                   class=class="str">"cmt">// 不做关闭
};
class="macro">#include <Expert\Signal\Signal_Momentum_Pinball.mqh>           class=class="str">"cmt">// 动量弹球交易策略的信号模块
class="kw">input class="type">uint      TS_MomPin_Take_Profit = class="num">10;                    class=class="str">"cmt">// 动量弹球: 获利 (点数)
class="kw">input class="type">bool      Show_1st_H1_Bar = true;                        class=class="str">"cmt">// 是否显示每天第一个小时柱的范围?
class="kw">input class="type">bool      Alert_Popup = true;                            class=class="str">"cmt">// 提醒: 显示弹出窗口?
class="kw">input class="type">bool      Alert_Email = false;                           class=class="str">"cmt">// 提醒: 发送 e-mail?
class="kw">input class="type">class="kw">string    Alert_Email_Subj = "";                         class=class="str">"cmt">// 提醒: e-mail 提醒的主题
class="kw">input class="type">bool      Alert_Push = true;                             class=class="str">"cmt">// 提醒: 发送推送通知?
class="kw">input class="type">uint      Days_Limit = class="num">7;                                class=class="str">"cmt">// 历史深度 (日历天数)
ENUM_LOG_LEVEL  Log_Level = LOG_LEVEL_DEBUG;                   class=class="str">"cmt">// 记录模式
class="type">class="kw">double
  buff_1st_H1_Bar[], buff_1st_H1_Bar_Zero[],                   class=class="str">"cmt">// 用于填充每天第一个小时柱的缓冲区
  buff_Entry[], buff_Entry_Color[],                            class=class="str">"cmt">// 挂单线的缓冲区
  buff_SL[],                                                   class=class="str">"cmt">// 止损线的缓冲区
  buff_TP[],                                                   class=class="str">"cmt">// 获利线的缓冲区
  gd_Entry_Offset = class="num">0,                                         class=class="str">"cmt">// 交易品种价格的 TS_MomPin_Entry_Offset
  gd_Exit_Offset = class="num">0                                           class=class="str">"cmt">// 交易品种价格的 TS_MomPin_Exit_Offset
;
class="type">int OnInit() {
  class=class="str">"cmt">// 把点数转换为交易品种的价格:
  gd_Entry_Offset = TS_MomPin_Entry_Offset * _Point;
  gd_Exit_Offset = TS_MomPin_Exit_Offset * _Point;
  
  class=class="str">"cmt">// 指标缓冲区的设计:
  
  class=class="str">"cmt">// 每日第一个小时柱的长方形
  SetIndexBuffer(class="num">0, buff_1st_H1_Bar, INDICATOR_DATA);
    PlotIndexSetDouble(class="num">0, PLOT_EMPTY_VALUE, class="num">0);
  SetIndexBuffer(class="num">1, buff_1st_H1_Bar_Zero, INDICATOR_DATA);
    PlotIndexSetDouble(class="num">1, PLOT_EMPTY_VALUE, class="num">0);
  
  class=class="str">"cmt">// 挂单设置线
  SetIndexBuffer(class="num">2, buff_Entry, INDICATOR_DATA);
    PlotIndexSetDouble(class="num">1, PLOT_EMPTY_VALUE, class="num">0);
  SetIndexBuffer(class="num">3, buff_Entry_Color, INDICATOR_COLOR_INDEX);
  
  class=class="str">"cmt">// 止损线
  SetIndexBuffer(class="num">4, buff_SL, INDICATOR_DATA);

缓冲区绑定与首跑重算边界

指标初始化收尾时,把第 5 号缓冲区绑给 buff_TP 作 INDICATOR_DATA,并用 PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, 0) 让获利线在值为 0 处留空,避免和真实价格 0 混淆。短线名直接写成 "Momentum Pinball",小数位吃 _Digits,返回 INIT_SUCCEEDED 即完成挂载。 首次运行和后续新柱的分界靠 go_Brownie.b_First_Run 与 b_Is_New_Bar 判断。第一次跑会把六个缓冲区全 ArrayInitialize 成 0,并用 Bars(_Symbol, PERIOD_CURRENT, t_Time - t_Time%86400 - 86400*Days_Limit, t_Time) 反推重算起点;若不是新柱直接 return(rates_total) 跳过,省得每 tick 重画。 非首跑但来了新柱时,重算深度只退到当天 0 点:i_Current_TF_Bar = rates_total - Bars(... t_Time - t_Time%86400 ...)。Days_Limit 这个参数直接决定历史回溯天数,调大它 MT5 启动会明显变慢,实盘建议先设 1 验证。 入场信号、止损获利水平在循环前先置 WRONG_VALUE,D1 柱时间和挂单柱时间也清零待填。这样结构能保证只在新柱闭合后重算,外汇与贵金属波动大,这种边界控制可降低重绘造成的误判概率。

MQL5 / C++
PlotIndexSetDouble(class="num">2, PLOT_EMPTY_VALUE, class="num">0);

class=class="str">"cmt">// 获利线
SetIndexBuffer(class="num">5, buff_TP, INDICATOR_DATA);
   PlotIndexSetDouble(class="num">3, PLOT_EMPTY_VALUE, class="num">0);

IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
IndicatorSetString(INDICATOR_SHORTNAME, "Momentum Pinball");

class="kw">return(INIT_SUCCEEDED);
}
go_Brownie.f_Update(prev_calculated, prev_calculated);     class=class="str">"cmt">// “提供” 数据给 Brownie
class="type">class="kw">datetime t_Time = TimeCurrent();                        class=class="str">"cmt">// 最近知道的服务器时间
class="type">int
   i_Period_Bar = class="num">0,                                                  class=class="str">"cmt">// 辅助计数器
   i_Current_TF_Bar = class="num">0                                               class=class="str">"cmt">// 循环起始柱的索引
;
if(go_Brownie.b_First_Run) {                                         class=class="str">"cmt">// 如果这是第一次运行
   i_Current_TF_Bar = rates_total - Bars(_Symbol, PERIOD_CURRENT, t_Time - t_Time % class="num">86400 - class="num">86400 * Days_Limit, t_Time);
   class=class="str">"cmt">// clearing buffer at re-initialization:
   ArrayInitialize(buff_1st_H1_Bar, class="num">0); ArrayInitialize(buff_1st_H1_Bar_Zero, class="num">0);
   ArrayInitialize(buff_Entry, class="num">0); ArrayInitialize(buff_Entry_Color, class="num">0);
   ArrayInitialize(buff_TP, class="num">0);
   ArrayInitialize(buff_SL, class="num">0);
} else if(!go_Brownie.b_Is_New_Bar) class="kw">return(rates_total);   class=class="str">"cmt">// 等待柱的关闭
else {                                                          class=class="str">"cmt">// 新柱
   class=class="str">"cmt">// 最小重新计算深度 - 从开始的一天计算:
   i_Current_TF_Bar = rates_total - Bars(_Symbol, PERIOD_CURRENT, t_Time - t_Time % class="num">86400, t_Time);
}
ENUM_ENTRY_SIGNAL e_Entry_Signal = ENTRY_UNKNOWN;            class=class="str">"cmt">// 入场信号
class="type">class="kw">double
   d_SL = WRONG_VALUE,                                        class=class="str">"cmt">// 止损水平
   d_TP = WRONG_VALUE,                                        class=class="str">"cmt">// 获利水平
   d_Entry_Level = WRONG_VALUE,                               class=class="str">"cmt">// 入场水平
   d_Range_High = WRONG_VALUE, d_Range_Low = WRONG_VALUE      class=class="str">"cmt">// 模式的第一个柱的范围边界
;
class="type">class="kw">datetime
   t_Curr_D1_Bar = class="num">0,                                         class=class="str">"cmt">// 当前D1柱的时间 (模式的第二个柱)
   t_Last_D1_Bar = class="num">0,                                         class=class="str">"cmt">// 最近的D1柱的时间,信号可以在这个柱上
   t_Entry_Bar = class="num">0                                            class=class="str">"cmt">// 设置挂单的柱的时间
;
class=class="str">"cmt">// 确保初始重计算柱的索引在允许的范围之内:

「日内首根H1与未触发挂单的边界处理」

这段逻辑解决两个实盘里容易漏看的细节:一是把每天第一根 H1 柱的高低区间画出来做背景参考,二是给当天没触发的入场线设死期限。外汇与贵金属波动受时段流动性影响大,这类边界处理直接决定信号会不会在跨日时乱漂,属于高风险品种下的必要防护。 先算当前柱所属的 D1 起点:用 Time[i] - Time[i] % 86400,86400 就是一天的秒数。一旦 t_Last_D1_Bar < t_Curr_D1_Bar,说明撞上当天第一个带信号的柱,立刻记 t_Entry_Bar,随后若开了 Show_1st_H1_Bar,就反向回溯把当天已存在的 H1 柱高低值写进 buff_1st_H1_Barbuff_1st_H1_Bar_Zero——牛模式高线填 Zero 缓冲、低线填主缓冲,熊模式反过来。 入场线延长部分更硬:从信号柱前一柱开始向后推,只要 Time[i_Period_Bar] > t_Curr_D1_Bar + 86399 就判一天结束,把 e_Entry_SignalENTRY_NONE 并 break,意味着挂单若撑过完整 86400 秒没被 High/Low 穿过就作废。牛模式判定 d_Entry_Level <= High[i] 才算入场,熊模式对称看 Low。开 MT5 把这段接进 EA,调 Show_1st_H1_Bar 开关就能直观比对每日开盘区间对后续信号的限制效果。

MQL5 / C++
i_Current_TF_Bar = class="type">int(fmax(class="num">0, fmin(i_Current_TF_Bar, rates_total - class="num">1)));
class="kw">while(++i_Current_TF_Bar < rates_total && !IsStopped()) {            class=class="str">"cmt">// 迭代当前时段的柱
  class=class="str">"cmt">// 从信号模块接收数据:
  e_Entry_Signal = fe_Get_Entry_Signal(-Time[i_Current_TF_Bar], d_Entry_Level, d_SL, d_TP, d_Range_High, d_Range_Low);
  if(e_Entry_Signal == ENTRY_INTERNAL_ERROR) {                        class=class="str">"cmt">// 从外部指标缓冲区复制数据出错
    class=class="str">"cmt">// 计算和画图应当在下一个分时重复:
    go_Brownie.f_Reset();
    class="kw">return(rates_total);
  }
  if(e_Entry_Signal > class="num">1) class="kw">continue;                                    class=class="str">"cmt">// 这个柱上没有活动信号
t_Curr_D1_Bar = Time[i_Current_TF_Bar] - Time[i_Current_TF_Bar] % class="num">86400;        class=class="str">"cmt">// 柱所属于的当天的起始时间
if(t_Last_D1_Bar < t_Curr_D1_Bar) {                                  class=class="str">"cmt">// 这是有信号的当天的第一个柱
  t_Entry_Bar = Time[i_Current_TF_Bar];                              class=class="str">"cmt">// 记住交易开始的时间
class=class="str">"cmt">// 填充第一个小时柱的背景:
if(Show_1st_H1_Bar) {
  i_Period_Bar = i_Current_TF_Bar;
  class="kw">while(Time[--i_Period_Bar] >= t_Curr_D1_Bar && i_Period_Bar > class="num">0)
    if(e_Entry_Signal == ENTRY_BUY) {                                class=class="str">"cmt">// 牛市模式
      buff_1st_H1_Bar_Zero[i_Period_Bar] = d_Range_High;
      buff_1st_H1_Bar[i_Period_Bar] = d_Range_Low;
    } else {                                                          class=class="str">"cmt">// 熊市模式
      buff_1st_H1_Bar[i_Period_Bar] = d_Range_High;
      buff_1st_H1_Bar_Zero[i_Period_Bar] = d_Range_Low;
    }
}
class=class="str">"cmt">// 入场线,直到与柱相交叉:
 i_Period_Bar = i_Current_TF_Bar - class="num">1;
 if(e_Entry_Signal == ENTRY_BUY) {                                    class=class="str">"cmt">// 牛市模式
   class="kw">while(++i_Period_Bar < rates_total) {
     if(Time[i_Period_Bar] > t_Curr_D1_Bar + class="num">86399) {                class=class="str">"cmt">// 一天的结束
       e_Entry_Signal = ENTRY_NONE;                                  class=class="str">"cmt">// 挂单没有被触发
       class="kw">break;
     }
     class=class="str">"cmt">// 延长线:
     buff_Entry[i_Period_Bar] = d_Entry_Level;
     buff_Entry_Color[i_Period_Bar] = class="num">0;
     if(d_Entry_Level <= High[i_Period_Bar]) class="kw">break;                  class=class="str">"cmt">// 在这个柱有入场
   }
 } else {                                                            class=class="str">"cmt">// 熊市模式
   class="kw">while(++i_Period_Bar < rates_total) {
     if(Time[i_Period_Bar] > t_Curr_D1_Bar + class="num">86399) {                class=class="str">"cmt">// 一天结束

◍ 挂单未触发与止损获利线的柱内收口逻辑

这段逻辑处理的是日内挂单在当天未成交、以及成交后如何在后续柱上画 SL/TP 直到被触碰退出的细节。若 e_Entry_Signal 仍是 ENTRY_NONE,说明挂单在一天结束前都没触发,代码直接把 i_Current_TF_Bar 锁定为当前周期柱并 continue,当天剩余柱不再参与扫描。 一旦挂单被触发,先通过 fmin(i_Period_Bar, rates_total-1) 夹住边界,在 buff_SL 写入 d_SL,随后进入 while 循环向后逐柱推进。退出判定分两类:买单看 d_SL>=Low[i] 或 d_TP<=High[i],卖单看 d_SL<=High[i] 或 d_TP>=Low[i],任一条命中就 break。 有个容易忽略的边界处理——若退出发生在单根柱上(前一柱缓冲为 0),代码会用 fmax(0,i_Period_Bar-1) 向前补一根柱的 SL/TP 值,避免视觉上线条断裂。Time[] 对 86400 取模用来切分 D1 边界,后一天柱直接 break 不再画线。 循环收尾把 i_Period_Bar 重设为 i_Current_TF_Bar,并重算 t_Curr_D1_Bar 基准,内层 while 顺延同日的后续 TF 柱索引。最后 i_Period_Bar=rates_total-1 锚定当前柱,外汇与贵金属波动大,这类缓冲补柱逻辑在跳空时可能影响 SL 视觉位置,建议开 MT5 用 EURUSD 的 M15 回看一根触发即退出的 K 线验证。

MQL5 / C++
e_Entry_Signal = ENTRY_NONE; class=class="str">"cmt">// 挂单没有触发
class="kw">break;

class=class="str">"cmt">// 延长线:
buff_Entry[i_Period_Bar] = d_Entry_Level;
buff_Entry_Color[i_Period_Bar] = class="num">1;

if(d_Entry_Level >= Low[i_Period_Bar]) class="kw">break; class=class="str">"cmt">// 在这个柱有入场
}
}
if(e_Entry_Signal == ENTRY_NONE) { class=class="str">"cmt">// 挂单在一天结束之前没有触发
  i_Current_TF_Bar = i_Period_Bar; class=class="str">"cmt">// 我们对这一天的柱就不再感兴趣
  class="kw">continue;
}
class=class="str">"cmt">// 挂单被触发,查找仓位关闭的柱:
i_Period_Bar = fmin(i_Period_Bar, rates_total - class="num">1);
buff_SL[i_Period_Bar] = d_SL;
class="kw">while(++i_Period_Bar < rates_total) {
  if(TS_MomPin_Exit_Mode == CLOSE_ON_SL_TRAIL) {
    if(Time[i_Period_Bar] >= t_Curr_D1_Bar + class="num">86400) class="kw">break; class=class="str">"cmt">// 这是后一天的柱
    
    class=class="str">"cmt">// 画获利和止损线,直到它们与价格柱有交叉:
    buff_SL[i_Period_Bar] = d_SL;
    buff_TP[i_Period_Bar] = d_TP;
    
    if((
      e_Entry_Signal == ENTRY_BUY && d_SL >= Low[i_Period_Bar]
      ) || (
      e_Entry_Signal == ENTRY_SELL && d_SL <= High[i_Period_Bar]
    )) { class=class="str">"cmt">// 止损退出
      if(buff_SL[class="type">int(fmax(class="num">0, i_Period_Bar - class="num">1))] == class="num">0.) {
        class=class="str">"cmt">// 在一个柱上开始和结束,向过去延伸一个柱
        buff_SL[class="type">int(fmax(class="num">0, i_Period_Bar - class="num">1))] = d_SL;
        buff_TP[class="type">int(fmax(class="num">0, i_Period_Bar - class="num">1))] = d_TP;
      }
      class="kw">break;
    }
    
    if((
      e_Entry_Signal == ENTRY_BUY && d_TP <= High[i_Period_Bar]
      ) || (
      e_Entry_Signal == ENTRY_SELL && d_SL >= Low[i_Period_Bar]
    )) { class=class="str">"cmt">// 获利退出
      if(buff_TP[class="type">int(fmax(class="num">0, i_Period_Bar - class="num">1))] == class="num">0.) {
        class=class="str">"cmt">// 在一个柱上开始和结束,向过去延伸一个柱
        buff_SL[class="type">int(fmax(class="num">0, i_Period_Bar - class="num">1))] = d_SL;
        buff_TP[class="type">int(fmax(class="num">0, i_Period_Bar - class="num">1))] = d_TP;
      }
      class="kw">break;
    }
  }
}
i_Period_Bar = i_Current_TF_Bar;
t_Curr_D1_Bar = Time[i_Period_Bar] - Time[i_Period_Bar] % class="num">86400;
class="kw">while(
  ++i_Period_Bar < rates_total
  &&
  t_Curr_D1_Bar == Time[i_Period_Bar] - Time[i_Period_Bar] % class="num">86400
) i_Current_TF_Bar = i_Period_Bar;
i_Period_Bar = rates_total - class="num">1; class=class="str">"cmt">// 当前柱

信号柱判定与提醒推送的短路逻辑

这段 MQL5 片段是 Momentum Pinball 交易系统里负责「要不要发提醒」的守门员。先做的两个 if 都是提前 return:三个提醒开关全为 0 就直接退出,当前柱不是信号柱也退出,避免每根 K 线无脑刷弹窗。 提醒文案用 StringFormat 拼:买挂单显示 BuyStop、卖挂单显示 SellStop,入场价和止损价都按 _Digits 精度转字符串。注意原文消息体写的是俄文「ТС Momentum Pinball: 需要 %s @ %s, SL: %s」,在中文 MT5 环境里你可能要改成「动量弹球TS: 挂%s @ %s, 止损 %s」才看得顺。 最后 f_Do_Alert 的第二个 false 是「不重复提示同一条」,配合 Alert_Popup / Alert_Email / Alert_Push 三个开关决定走哪条通道。外汇与贵金属波动剧烈、滑点常见,这类挂单提醒只代表策略触发条件成立,不代表成交必然有利,实盘前请在 MT5 策略测试器用历史数据跑一遍确认提醒不漏发。

MQL5 / C++
if(Alert_Popup + Alert_Email + Alert_Push == class="num">0) class="kw">return(rates_total);      class=class="str">"cmt">// 全部禁用
if(t_Entry_Bar != Time[i_Period_Bar]) class="kw">return(rates_total);                     class=class="str">"cmt">// 这个柱没有信号
class=class="str">"cmt">// message wording:
class="type">class="kw">string s_Message = StringFormat("ТС Momentum Pinball: 需要 %s @ %s, SL: %s",
  e_Entry_Signal == ENTRY_BUY ?"BuyStop" : "SellStop",
  DoubleToString(d_Entry_Level, _Digits),
  DoubleToString(d_SL, _Digits)
);
class=class="str">"cmt">// 提醒:
f_Do_Alert(s_Message, Alert_Popup, false, Alert_Email, Alert_Push, Alert_Email_Subj);

「给弹珠策略EA补上退出信号通道」

原版交易机器人只管进场,退出逻辑是缺的。这次扩展先在入场信号枚举里补了 ENTRY_INTERNAL_ERROR,并新增一整套退出信号列表,结构和指标里的枚举一致,代码放在 Street_Smarts_Bot_MomPin.mq5 的 24–32 行,位置在标准库交易类关联代码之前。 信号模块也改了:它现在能回传平仓信号。旧版 139 行用 if 判断仓位是否新建,只为算初始止损;这版加了 else 分支去调信号模块,从 171 行到 186 行,命中条件就直接平掉。 下面这段是 171–186 行核心逻辑的摘出。它不是新仓位时,先问信号模块“该平仓了吗”;若退出信号与入场方向匹配或收到 EXIT_ALL,就调 CTrade 关掉当前符号仓位并 return。外汇和贵金属波动剧烈,这类自动平仓逻辑在滑点行情中可能不按预期成交,实盘前务必在 MT5 策略测试器跑历史数据验证。 fb_Is_Acceptable_Distance 函数(424–434 行)也动了,用来更严格约束交易水平间的最小距离,避免止损与信号位贴太近被连环触发。

MQL5 / C++
} <span class="keyword">else</span> {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// 不是新的仓位</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// 用于关闭仓位的条件准备好了吗?</span>
&nbsp;&nbsp;ENUM_EXIT_SIGNAL e_Exit_Signal = fe_Get_Exit_Signal(d_Entry_Level, <span class="keyword">class="type">class="kw">datetime</span>(<span class="functions">PositionGetInteger</span>(<span class="macro">POSITION_TIME</span>)), e_Entry_Signal, <span class="functions">TimeCurrent</span>(), TS_MomPin_Exit_Mode);
&nbsp;&nbsp;<span class="keyword">if</span>((
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e_Exit_Signal == EXIT_BUY &amp;&amp; e_Entry_Signal == ENTRY_BUY
&nbsp;&nbsp;&nbsp;&nbsp;) || (
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e_Exit_Signal == EXIT_SELL &amp;&amp; e_Entry_Signal == ENTRY_SELL
&nbsp;&nbsp;&nbsp;&nbsp;) || e_Exit_Signal == EXIT_ALL
&nbsp;&nbsp;) {
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// 它必须被关闭</span>
&nbsp;&nbsp;&nbsp;&nbsp;CTrade o_Trade;
&nbsp;&nbsp;&nbsp;&nbsp;o_Trade.LogLevel(LOG_LEVEL_ERRORS);
&nbsp;&nbsp;&nbsp;&nbsp;o_Trade.PositionClose(<span class="predefines">_Symbol</span>);
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span>;
&nbsp;&nbsp;}
  }

◍ 默认参数下的裸测结果

这套 EA 我没动任何参数,直接用书里给的默认设置跑回测,目的只有一个:先看策略骨架在当前市场还能不能站住,而不是去榨参数。作者原版策略依赖的是上世纪晚期的图表结构,这点必须时刻记着。 测试报价取自 MetaQuotes 模拟服务器 2014 年起始的数据,品种先挑了 EURJPY,周期 H1。余额曲线整体向上但回撤不浅,说明原逻辑在日元交叉盘上可能仍有正期望,但绝不是无脑躺赢。 换 EURUSD 同周期同时间段,曲线形态明显更平、波动更碎,欧系直盘对这套老策略的容纳度倾向更低。 不改设置直接丢到 XAUUSD(黄金现货)同周期测,余额图又是一副样子——贵金属的高波动把原止损结构反复洗穿,外汇和贵金属都是高风险品种,这种裸测只能说明:老系统在新市场需要重估,不能拿来当圣杯。

记住这一条就够了

动量弹球这套系统原文对仓位跟踪和离场给了多种模糊说法,我们用指标加EA把骨架搭起来了,但参数和 activity 算法仍留了大量可改空间。LBR_RSI.mq5 把 ROC 与 RSI 合并,用来判日内开局方向;TS_Momentum_Pinball.mq5 负责把人工交易的进出场水平画出来,并标出首小时区间;Signal_Momentum_Pinball.mqh 是供指标和EA共用的函数库;Street_Smarts_Bot_MomPin.mq5 才是自动跑策略的实体。 代码、编译件和库都打在 MQL5.zip(163.27 KB)里,按原目录丢进 MT5 就能编译。外汇和贵金属波动大、杠杆高,实盘前先用策略测试器跑历史数据看信号分布,别直接挂真金。 真正值得记住的只有一句:这套东西的价值不在‘跟单’,而在你拿现成源码改仓位逻辑和离场规则——方向突破或反弹不重要,关键是价格朝选定的边界移动,余下优化空间交给你自己填。

常见问题

在OnInit里用SetIndexBuffer把指标缓冲挂到全局句柄,并在首根H1用ArrayInitialize清零,防止历史数据缺失导致重算越界。
这是未触发挂单的柱内收口逻辑在起作用,需在订单未成交时禁用Trailing,等成交后再开启,否则会被误平。
可以,小布能接管信号柱判定与推送的短路逻辑,未触发挂单和边界异常会直接推送到你手机,不用自己守盘。
在信号函数里反向判定动量衰减或弹球回归中轴,触发Exit事件并下反向市价单或平掉原仓,别只写Entry。
多是短路逻辑把低动量柱过滤了,需在代码里放开最小动量阈值或单独打日志,确认是过滤而非bug。