使用信号指标的简单交易系统·综合运用
🧩

使用信号指标的简单交易系统·综合运用

(3/3)·从测试到模块改写,一套可复用信号 EA 的收尾拼图

新手友好 第 3/3 篇
不少人在换了信号指标后重写整支 EA,其实核心下单逻辑几乎不动。把指标探测和交易执行拆开,后面换标的只是改参数。本篇顺着前两篇的框架,把收尾的工程细节补齐。

回测前先搞清信号柱的坑

SignalBar 输入变量若设为 0,EA 会从当前柱取信号。但当前柱信号极不可靠:它能在柱内反复出现又消失,而价格趋势却常朝信号反方向走很久。把 EA 丢进策略测试仪开可视化、SignalBar=0,就能直接看到这种“假信号”乱跳;ASCtrend 指标的可视化把这点暴露得最清楚。 只有 Every tick 模式适合优化依赖当前柱信号的 EA;若信号来自已收盘的柱,用 Open prices only 就够了,速度能拉起来且质量无明显损失。所以这类系统的测试和优化,最好别碰当前柱信号。 用 EUR/USD 默认参数跑年初到 12 月初的测试,再对参数做微调,就能在历史数据上找到更合适的组合。优化过程本身没啥特殊,核心是别拿当前柱信号当依据。 外汇和贵金属属高风险品种,简单系统想靠默认参数暴利不现实;但半自动系统若随市场状态定期调参,仍可能拿到不错结果。2011 年 1–5 月 EUR/USD H12 是上升趋势,早段就能识别,用默认设置、只做多且 MM=0.05 在 H1 上测,结果也值得一看——方向由交易者定,但大周期图表才是这类玩法该待的地方。

「把现成信号模块改成挂别的指标」

想在 MT5 里给某个特定信号指标(比如 BykovTrend)快速造一个 EA 信号模块,不必从零写。思路是找 MySignals.zip 里和目标任务最像的那个现成模块——本文里 ASCtrend 与 BykovTrend 行为最接近,就直接拿 ASCtrendSignal.mqh 改。 第一步纯体力活:把 \MQL5\Include\Expert\Signal\MySignals\ 下的 BykovTrendSignal.mqh 复制到 MetaEditor 打开,Ctrl+H 把代码里所有 "ASCtrend" 替换成 "BykovTrend"。两个指标都靠缓冲区 0 存卖出信号、缓冲区 1 存买入信号,所以 LongCondition / ShortCondition 那段不用动。 真正的细活在输入参数。RISK 两指标都有,默认值不同也无所谓;但新增的 SSP 要在类里声明全局变量 m_SSP,加 SSP() 取值方法,构造函数里初始化,ValidationSettings() 里做正确性检查。BykovTrend 输入变量数目不同,InitBykovTrend() 里声明的参数数组维度要改成 1 个字符串名 + 2 个参数尺寸,调用初始化时的入参数量改成 3。

MQL5 / C++
class=class="str">"cmt">//+----------------------------------------------+
class=class="str">"cmt">//| 指标输入参数                                 |
class=class="str">"cmt">//+----------------------------------------------+
input class="type">int RISK=class="num">3;
input class="type">int SSP=class="num">9;
class=class="str">"cmt">//+----------------------------------------------+
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| 声明类                                                                 |
class=class="str">"cmt">//| 标题=基于BykovTrend指标的信号                                             |
class=class="str">"cmt">//| 类型=SignalAdvanced                                                       |
class=class="str">"cmt">//| 名称=BykovTrend                                                           |
class=class="str">"cmt">//| 类=CBykovTrendSignal                                                      |
class=class="str">"cmt">//| 页面=                                                                     |
class=class="str">"cmt">//| 参数=BuyPosOpen,class="type">bool,true,允许买入                                        |
class=class="str">"cmt">//| 参数=SellPosOpen,class="type">bool,true,允许卖出                                        |
class=class="str">"cmt">//| 参数=BuyPosClose,class="type">bool,true,允许退出买入持仓                              |
class=class="str">"cmt">//| 参数=SellPosClose,class="type">bool,true,允许退出卖出持仓                            |
class=class="str">"cmt">//| 参数=Ind_Timeframe,ENUM_TIMEFRAMES,PERIOD_H1,时间框架                     |
class=class="str">"cmt">//| 参数=RISK,class="type">int,class="num">4,风险水平                                                  |
class=class="str">"cmt">//| 参数=SSP,class="type">int,class="num">9,SSP                                                        |
上面这段是改完后的类声明注释头样本。注意 Ind_Timeframe 设的时间框架永远不能大于 EA 所挂图表周期,否则模块可能不触发。另外若原指标用了自定义枚举(如 Smooth_Method)做输入类型,在信号模块里要降级成 int/uint,并在生成 EA 时反向映射回去,否则 EA 输入界面会出问题。 外汇与贵金属杠杆高,改完的模块只是信号载体,实盘前务必在策略测试器跑历史数据验证。把 ASCtrendSignal.mqh 和 BykovTrendSignal.mqh 左右分屏对照,能明显加快替换节奏。

MQL5 / C++
class=class="str">"cmt">//+----------------------------------------------+
class=class="str">"cmt">//| 指标输入参数                                 |
class=class="str">"cmt">//+----------------------------------------------+
input class="type">int RISK=class="num">3;
input class="type">int SSP=class="num">9;
class=class="str">"cmt">//+----------------------------------------------+
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| 声明类                                                                 |
class=class="str">"cmt">//| 标题=基于BykovTrend指标的信号                                             |
class=class="str">"cmt">//| 类型=SignalAdvanced                                                       |
class=class="str">"cmt">//| 名称=BykovTrend                                                           |
class=class="str">"cmt">//| 类=CBykovTrendSignal                                                      |
class=class="str">"cmt">//| 页面=                                                                     |
class=class="str">"cmt">//| 参数=BuyPosOpen,class="type">bool,true,允许买入                                        |
class=class="str">"cmt">//| 参数=SellPosOpen,class="type">bool,true,允许卖出                                        |
class=class="str">"cmt">//| 参数=BuyPosClose,class="type">bool,true,允许退出买入持仓                              |
class=class="str">"cmt">//| 参数=SellPosClose,class="type">bool,true,允许退出卖出持仓                            |
class=class="str">"cmt">//| 参数=Ind_Timeframe,ENUM_TIMEFRAMES,PERIOD_H1,时间框架                     |
class=class="str">"cmt">//| 参数=RISK,class="type">int,class="num">4,风险水平                                                  |
class=class="str">"cmt">//| 参数=SSP,class="type">int,class="num">9,SSP                                                        |

◍ 给 BykovTrend 信号类塞进可调平滑引擎

CBykovTrendSignal 这个类从 CExpertSignal 派生,本职就是按 BykovTrend 指标吐出入场信号。原版把 SSP 写死成 4,其实它控制指标灵敏度,初始化时若 m_SSP<=0 会直接 printf 报错并返回 false,所以实盘里把 SSP 设成 1~9 之间去回测才有意义。 为了让信号线能换平滑算法,代码里声明了 Smooth_Method 枚举,从最普通的 MODE_SMA_ 到 JJMA、VIDYA、AMA 一共 10 种。默认 MA_SMethod 取 MODE_LWMA,MA_Length=30、MA_Phase=100,其中 Phase 对 JJMA 才在 -100 到 +100 区间起作用,换别的算法时这个参数常被忽略。 指标句柄创建时用了 3 个 MqlParam,parameters[2] 专门把 m_SSP 以 TYPE_INT 塞进去;若 m_indicator.Create 失败或 NumBuffers(2) 不满 2 条缓冲,类会返回 false 让上层 EA 知道挂载失败。外汇和贵金属波动大、杠杆高,这类自定义信号在实盘前务必用策略测试器跑至少 3 个月 Tick 数据验证。

MQL5 / C++
class="type">uint               m_SSP;             class=class="str">"cmt">// SSP
class="type">void               SSP(class="type">uint value)      { m_SSP=value; }
m_SSP=class="num">4;
if(m_SSP<=class="num">0)
  {
   printf(__FUNCTION__+": SSP must be above zero");
   class="kw">return(false);
  }
class=class="str">"cmt">//--- 设置指标参数
class="type">MqlParam parameters[class="num">3];
parameters[class="num">2].type=TYPE_INT;
parameters[class="num">2].integer_value=m_SSP;
class=class="str">"cmt">//--- 对象初始化
if(!m_indicator.Create(m_symbol.Name(),m_Ind_Timeframe,IND_CUSTOM,class="num">3,parameters))
class=class="str">"cmt">//--- 缓存数量
if(!m_indicator.NumBuffers(class="num">2)) class="kw">return(false);
enum Smooth_Method
  {
   MODE_SMA_,  class=class="str">"cmt">// SMA
   MODE_EMA_,  class=class="str">"cmt">// EMA
   MODE_SMMA_, class=class="str">"cmt">// SMMA
   MODE_LWMA_, class=class="str">"cmt">// LWMA
   MODE_JJMA,  class=class="str">"cmt">// JJMA
   MODE_JurX,  class=class="str">"cmt">// JurX
   MODE_ParMA, class=class="str">"cmt">// ParMA
   MODE_T3,    class=class="str">"cmt">// T3
   MODE_VIDYA, class=class="str">"cmt">// VIDYA
   MODE_AMA,   class=class="str">"cmt">// AMA
  };
input Smooth_Method MA_SMethod=MODE_LWMA; class=class="str">"cmt">// 平滑方式
input class="type">int MA_Length=class="num">30;                   class=class="str">"cmt">// 平滑深度
input class="type">int MA_Phase=class="num">100;                   class=class="str">"cmt">// 平滑参数
                                     class=class="str">"cmt">// 对于JJMA它在 -class="num">100 ... +100之间变化,

平滑均线算法的枚举与信号阈值设定

在 MT5 的 EA 架构里,把不同平滑算法集中到一个枚举类型,是后续切换指标内核的基础。下面这段代码声明了 Smooth_Method 枚举,覆盖 SMA、EMA、SMMA、LWMA 到 JJMA、JurX、ParMA、T3、VIDYA、AMA 共 10 种算法;其中 VIDYA 对应的周期参数本质是 CMO 周期,AMA 则对应慢速平均周期,二者对波动率自适应逻辑差异明显。 输入参数段给出了可直接抄进自己 EA 的默认值:开仓信号阈值 Signal_ThresholdOpen=40,平仓阈值 Signal_ThresholdClose=20,两者都在 0~100 区间内。止损与获利水平默认各 50.0 点,挂单过期时间 Signal_Expiration=1(以 K 线根数计),MagicNumber 写死为 29976。 开 MT5 新建 EA 时,把这段枚举和 input 块原样贴入,就能在外部参数面板用下拉框换算法、用数字框调阈值。外汇与贵金属杠杆高,参数回测前先想清楚 40/20 的阈值在欧美盘波动里的触发频率,可能比想象中更密集。

MQL5 / C++
class=class="str">"cmt">//对于VIDIA而言,它是CMO周期,对于AMA而言,它是慢速平均周期
class=class="str">"cmt">//+----------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  声明枚举类型                            | 
class=class="str">"cmt">//+------------------------------------------------------------------+
enum Smooth_Method
  {
  MODE_SMA_,  class=class="str">"cmt">// SMA
  MODE_EMA_,  class=class="str">"cmt">// EMA
  MODE_SMMA_, class=class="str">"cmt">// SMMA
  MODE_LWMA_, class=class="str">"cmt">// LWMA
  MODE_JJMA,  class=class="str">"cmt">// JJMA
  MODE_JurX,  class=class="str">"cmt">// JurX
  MODE_ParMA, class=class="str">"cmt">// ParMA
  MODE_T3,    class=class="str">"cmt">// T3
  MODE_VIDYA, class=class="str">"cmt">// VIDYA
  MODE_AMA,   class=class="str">"cmt">// AMA
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 输入参数                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- EA的输入参数
input class="type">class="kw">string          Expert_Title        ="Candles_Smoothed"; class=class="str">"cmt">// 文件名
class="type">ulong                 Expert_MagicNumber  =class="num">29976;              class=class="str">"cmt">// 
class="type">bool                  Expert_EveryTick    =false;              class=class="str">"cmt">// 
class=class="str">"cmt">//--- 主要型号的输入参数
input class="type">int             Signal_ThresholdOpen =class="num">40;               class=class="str">"cmt">// 开仓信号阈值 [class="num">0...class="num">100]
input class="type">int             Signal_ThresholdClose=class="num">20;               class=class="str">"cmt">// 平仓信号的阈值 [class="num">0...class="num">100]
input class="type">class="kw">double          Signal_PriceLevel   =class="num">0.0;               class=class="str">"cmt">// 执行交易的价格水平
input class="type">class="kw">double          Signal_StopLevel    =class="num">50.0;              class=class="str">"cmt">// 止损水平(点数)
input class="type">class="kw">double          Signal_TakeLevel    =class="num">50.0;              class=class="str">"cmt">// 获利水平(点数)
input class="type">int             Signal_Expiration   =class="num">1;                 class=class="str">"cmt">// 挂单到期时间(以柱形计)
input class="type">bool            Signal__BuyPosOpen  =true;              class=class="str">"cmt">// Candles_Smoothed() 允许买入
input class="type">bool            Signal__SellPosOpen =true;              class=class="str">"cmt">// Candles_Smoothed() 允许卖出

「平滑蜡烛信号模块的参数暴露」

这段输入参数把 Candles_Smoothed 指标直接挂到了 EA 的信号层,默认跑在 H1 周期上,平滑深度给到 30、平滑方式枚举值 4(范围 1–10),相位参数 100。 Signal__BuyPosClose 与 Signal__SellPosClose 都设为 true,意味着指标双向都允许平掉已有持仓,不是只管开仓。Signal__SignalBar 取 1,表示用倒数第二根柱(索引从 0 起)作为入场信号判定,避免用未收线的当前柱。 资金管理这块只暴露了两个量:Money_FixLot_Percent = 10.0 代表按权益百分比推算手数,Money_FixLot_Lots = 0.1 是兜底固定手数。外汇与贵金属杠杆高,实盘把百分比从 10 调到 2–3 可能更扛回撤。 开 MT5 把这套 input 贴进 EA 头,改 Signal__MA_Length 到 15 对比一下 H1 黄金的触发频率,能直观看出平滑深度对信号稀疏度的影响。

MQL5 / C++
input class="type">bool             Signal__BuyPosClose  =true;                class=class="str">"cmt">// Candles_Smoothed()允许退出买入持仓
input class="type">bool             Signal__SellPosClose =true;                class=class="str">"cmt">// Candles_Smoothed() 允许退出卖出持仓
input ENUM_TIMEFRAMES  Signal__Ind_Timeframe=PERIOD_H1;            class=class="str">"cmt">// Candles_Smoothed() 时间框架
input Smooth_Method    Signal__MA_SMethod   =class="num">4;                   class=class="str">"cmt">// Candles_Smoothed() 平滑方式 (class="num">1 - class="num">10)
input class="type">uint             Signal__MA_Length    =class="num">30;                  class=class="str">"cmt">// Candles_Smoothed() 平滑深度
input class="type">uint             Signal__MA_Phase     =class="num">100;                 class=class="str">"cmt">// Candles_Smoothed() 平滑参数
input class="type">uint             Signal__SignalBar    =class="num">1;                   class=class="str">"cmt">// Candles_Smoothed() 入场信号的柱形索引
input class="type">class="kw">double           Signal__Weight       =class="num">1.0;                 class=class="str">"cmt">// Candles_Smoothed() 权重 [class="num">0...class="num">1.0]
class=class="str">"cmt">//--- 用于资金管理的输入参数
input class="type">class="kw">double           Money_FixLot_Percent =class="num">10.0;                class=class="str">"cmt">// 百分比
input class="type">class="kw">double           Money_FixLot_Lots    =class="num">0.1;                 class=class="str">"cmt">// 固定持仓

◍ 把示例 EA 和信号模块塞进 MT5 的正确路径

随附的示例 EA 与信号模块已按功能拆包:Experts.zip 和 Expertsez.zip 解压到 \MQL5\Experts\,MySignals.zip 里的信号模块放到 \MQL5\Include\Expert\Signal\MySignals\,指标文件进 \MQL5\Indicators\。两个头文件 SmoothAlgorithms.mqh(135.4 KB)和 TradeAlgorithms.mqh(66.5 KB)必须放在 \MQL5\Include\,前者是部分指标编译的依赖,后者是所有 EA 编译的必需文件。 解压完后重启 MetaEditor,在 Navigator 里右键 MQL5 标签选 Compile,才能让你写的或现成的 EA 在策略测试器里跑起来。外汇与贵金属保证金交易杠杆高,实盘前务必先用历史数据回测验证信号逻辑。 有用户把 ASCtrendSignal 模块丢进 Include\Expert\Signal 却不在向导显示,根因是文件头部的服务块顺序错了——开始和结束之间只能有一个服务块,不能夹变量或宏替换。改完重新加载 MetaEditor 即可。下面这段 ASCtrendSignal.mqh 头部就是标准写法,注意 #property tester_indicator 与 #include 的位置: //+------------------------------------------------------------------+ //|ASCtrendSignal.mqh

//版权所有 © 2011, Nikolay Kositsin

//+------------------------------------------------------------------+ #property copyright "版权所有©2011,尼古拉-科西钦" #property link "farria@mail.redcom.ru" #property tester_indicator "ASCtrend.ex5" #include <Expert\ExpertSignal.mqh> //--- 向导说明开始 #define OPEN_LONG 80 #define OPEN_SHORT 80 #define CLOSE_LONG 40 #define CLOSE_SHORT 40 #define REVERSE_LONG 100 #define REVERSE_SHORT 100 #define NO_SIGNAL 0 OPEN_LONG 与 OPEN_SHORT 设 80、CLOSE 类设 40、反转类设 100,这几个常量是信号模块向 EA 返回动作指令的阈值,改它们等于改信号触发灵敏度,建议先在回测里调。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|ASCtrendSignal.mqh
class=class="str">"cmt">//|版权所有 © class="num">2011, Nikolay Kositsin | 
class=class="str">"cmt">//|哈巴罗夫斯克, farria@mail.redcom.ru | | 
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "版权所有©class="num">2011,尼古拉-科西钦"
class="macro">#class="kw">property link      "farria@mail.redcom.ru"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 包含的文件|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property tester_indicator "ASCtrend.ex5"
class="macro">#include <Expert\ExpertSignal.mqh>
class=class="str">"cmt">//--- 向导说明开始
class=class="str">"cmt">//+------------------------------------------------------------------+ 
class=class="str">"cmt">//| 常量声明|
class=class="str">"cmt">//+------------------------------------------------------------------+ 
class="macro">#define OPEN_LONG     class="num">80  class=class="str">"cmt">// 向智能交易系统返回买入指令的常数
class="macro">#define OPEN_SHORT    class="num">80  class=class="str">"cmt">// 向智能交易系统返回卖出指令的常数
class="macro">#define CLOSE_LONG    class="num">40  class=class="str">"cmt">// 向智能交易系统返回多头平仓指令的常数
class="macro">#define CLOSE_SHORT   class="num">40  class=class="str">"cmt">// 向智能交易系统返回空头平仓指令的常数
class="macro">#define REVERSE_LONG  class="num">100 class=class="str">"cmt">// 向智能交易系统返回多头反转指令的常数
class="macro">#define REVERSE_SHORT class="num">100 class=class="str">"cmt">// 向智能交易系统返回反向空头仓位指令的常数
class="macro">#define NO_SIGNAL     class="num">0   class=class="str">"cmt">// 向智能交易系统返回无信号的常数
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| 类的描述|
class=class="str">"cmt">//| Title=基于 ASCtrend 指标的信号

ASCtrend 信号类的参数骨架

在 MT5 向导里挂 ASCtrend 信号时,底层是一个叫 CASCtrendSignal 的类,它继承自 CExpertSignal。这个类本身不画指标,只通过 CiCustom 对象去读取外部 ASCtrend 自定义指标的数值,再决定多空权限。 可调参数里有几个直接影响实盘行为的开关:BuyPosOpen 和 SellPosOpen 控制是否允许开多、开空;BuyPosClose 与 SellPosClose 控制是否允许平多、平空。四个都是 bool,默认全为 true,意味着默认全自动。 时间框架参数 Ind_TIMEFRAMES 默认 PERIOD_H4,也就是信号在 4 小时周期上产生。RISK 默认 4(int 型),SignalBar 默认 1(uint 型),代表用第 1 根 bar 的索引作为入场信号参考。外汇与贵金属杠杆高,这类信号权限全开时回撤可能偏大,建议先关掉 SellPosOpen 做单向验证。 下面这段是类的核心声明与参数写入方法,逐行看能明白每个字段怎么落进对象内存。

MQL5 / C++
class CASCtrendSignal : class="kw">public CExpertSignal
  {
class="kw">protected:
   CiCustom              m_indicator;       class=class="str">"cmt">// 访问 ASCtrend 值的对象
   class=class="str">"cmt">//-- 调整参数
   class="type">bool                 m_BuyPosOpen;       class=class="str">"cmt">// 购买许可
   class="type">bool                 m_SellPosOpen;      class=class="str">"cmt">// 销售许可
   class="type">bool                 m_BuyPosClose;      class=class="str">"cmt">// 退出多头头寸的权限
   class="type">bool                 m_SellPosClose;     class=class="str">"cmt">// 退出空头头寸的权限
   ENUM_TIMEFRAMES     m_Ind_Timeframe;    class=class="str">"cmt">// ASCtrend 指标时间框架
   class="type">uint                 m_RISK;             class=class="str">"cmt">// 风险等级
   class="type">uint                 m_SignalBar;        class=class="str">"cmt">// 进入信号的条形指数
class="kw">public:
                      CASCtrendSignal();
   class=class="str">"cmt">//--- 设置可调参数的方法
   class="type">void                 BuyPosOpen(class="type">bool value)              { m_BuyPosOpen=value;     }
   class="type">void                 SellPosOpen(class="type">bool value)             { m_SellPosOpen=value;    }
   class="type">void                 BuyPosClose(class="type">bool value)             { m_BuyPosClose=value;    }
   class="type">void                 SellPosClose(class="type">bool value)            { m_SellPosClose=value;   }
   class="type">void                 Ind_Timeframe(ENUM_TIMEFRAMES value){ m_Ind_Timeframe=value;  }
   class="type">void                 RISK(class="type">uint value)                    { m_RISK=value;           }
   class="type">void                 SignalBar(class="type">uint value)               { m_SignalBar=value;      }

「信号类的参数校验与指标挂载」

写自定义信号类时,先把开平仓开关和风控参数在构造函数里钉死,能少掉很多运行期诡异 bug。下面这段 CASCtrendSignal 的构造函数把四个仓位开关全设为 true,周期锁在 H4,m_RISK 给到 4,信号取第 1 根 bar(m_SignalBar=1),并一次性加载开高低收四组序列。 ValidationSettings 不是摆设。它硬性卡住 m_RISK<=0 的情况,触发就 printf 报错并返回 false,避免在 EA 加载阶段把风险参数设成 0 或负数导致后续仓位计算崩掉。外汇和贵金属杠杆高,这类参数漏校验可能在实盘放大亏损概率。 InitIndicators 与 InitASCtrend 是两层指针防御。前者先判 indicators 空指针,再调后者;后者除了判空,还用 indicators.Add(GetPointer(m_indicator)) 把指标对象塞进集合,失败就打 error of adding the object 并退出。注意 parameters 数组长度为 2,第 0 位把指标名写死成 "ASCtrend.ex5",说明该信号强依赖这个编译好的 ex5 文件存在。 让小布替你跑这套 把上面构造函数里的 m_Ind_Timeframe 从 PERIOD_H4 改成 PERIOD_M15,重新编译挂到 EURUSD 图表,观察信号触发频率是否明显变密——这能直观验证信号 bar 间隔对策略敏感度的牵引。

MQL5 / C++
class=class="str">"cmt">//--- 可调整的参数验证方法
class="kw">virtual class="type">bool        ValidationSettings();
class=class="str">"cmt">//--- 可调整的参数验证方法
class="kw">virtual class="type">bool        InitIndicators(CIndicators *indicators); class=class="str">"cmt">// 指标初始化
class=class="str">"cmt">//--- 市场进入信号生成方法
class="kw">virtual class="type">int         LongCondition();
class="kw">virtual class="type">int         ShortCondition();
class="type">bool                InitASCtrend(CIndicators *indicators);   class=class="str">"cmt">// ASCtrend 指标初始化方法
class="kw">protected:
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|CASCtrendSignal 构造函数。|
class=class="str">"cmt">//| INPUT: no.|
class=class="str">"cmt">//| OUTPUT: no.|
class=class="str">"cmt">//|| 备注:没有。|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CASCtrendSignal::CASCtrendSignal()
  {
class=class="str">"cmt">//--- 设置默认参数
   m_BuyPosOpen=true;
   m_SellPosOpen=true;
   m_BuyPosClose=true;
   m_SellPosClose=true;

class=class="str">"cmt">//--- 指标输入参数
   m_Ind_Timeframe=PERIOD_H4;
   m_RISK=class="num">4;
class=class="str">"cmt">//--- 
   m_SignalBar=class="num">1;
   m_used_series=USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 检查可调整的参数。|
class=class="str">"cmt">//| INPUT: no.|
class=class="str">"cmt">// | 输出: 如果设置有效,则为 true;如果无效,则为 false。|
class=class="str">"cmt">//|| 备注:没有。|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CASCtrendSignal::ValidationSettings()
  {
class=class="str">"cmt">//--- 检查参数
   if(m_RISK<=class="num">0)
     {
       printf(__FUNCTION__+": Risk level must be above zero");
       class="kw">return(false);
     }
class=class="str">"cmt">//--- 成功完成
   class="kw">return(true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 初始化指标和时间序列。|
class=class="str">"cmt">//| INPUT: indicators - object-collection | 的指针|
class=class="str">"cmt">//| 指标和时间序列。|
class=class="str">"cmt">//| OUTPUT: true - 成功时为 true,否则为 false。|
class=class="str">"cmt">//|| 备注:没有。|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CASCtrendSignal::InitIndicators(CIndicators *indicators)
  {
class=class="str">"cmt">//--- 检查指针
   if(indicators==NULL) class="kw">return(false);
class=class="str">"cmt">//--- 指标初始化 
   if(!InitASCtrend(indicators)) class="kw">return(false);
class=class="str">"cmt">//--- 成功完成
   class="kw">return(true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ASCtrend 指标初始化。|
class=class="str">"cmt">//| INPUT: indicators - object-collection | 的指针|
class=class="str">"cmt">//| 指标和时间序列。|
class=class="str">"cmt">//| OUTPUT: true - 成功时为 true,否则为 false。|
class=class="str">"cmt">//|| 备注:没有。|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CASCtrendSignal::InitASCtrend(CIndicators *indicators)
  {
class=class="str">"cmt">//--- 检查指针
   if(indicators==NULL) class="kw">return(false);
class=class="str">"cmt">//--- 向集合中添加一个对象
   if(!indicators.Add(GetPointer(m_indicator)))
     {
       printf(__FUNCTION__+": error of adding the object");
       class="kw">return(false);
     }
class=class="str">"cmt">//--- 设置指示器参数
   class="type">MqlParam parameters[class="num">2];

   parameters[class="num">0].type=TYPE_STRING;
   parameters[class="num">0].string_value="ASCtrend.ex5";

◍ ASCtrend 信号类的多空判定逻辑

把自定义指标 ASCtrend 塞进信号类时,先要给第 2 个参数槽打上 TYPE_INT 类型,并把风控系数 m_RISK 写进去;随后用 Create() 拉起指标句柄,缓冲区数量硬性锁成 2,任何一步失败直接返回 false,初始化才算过关。 多头条件 LongCondition() 只看指标缓冲区 1:取 m_SignalBar 上的数值,若非 0 且不等于 EMPTY_VALUE,再结合持仓开关决定发 OPEN_LONG、CLOSE_SHORT 还是 REVERSE_SHORT。若当前没有卖仓平仓权限,先 return NO_SIGNAL,然后倒序扫 Bars() 返回的 K 线数,缓冲区 0 出现有效值就撤销平仓念头,缓冲区 1 出现有效值才确认 CLOSE_SHORT。 空头侧 ShortCondition() 对称处理,信号源换成缓冲区 0;同样靠 m_BuyPosClose 这类布尔位去分流 OPEN_SHORT / CLOSE_LONG / REVERSE_LONG。外汇与贵金属杠杆高,这类信号只作概率倾向参考,实盘前务必在 MT5 策略测试器跑一遍多周期回测。

MQL5 / C++
  parameters[class="num">1].type=TYPE_INT;
  parameters[class="num">1].integer_value=m_RISK;
class=class="str">"cmt">//--- 对象初始化 
  if(!m_indicator.Create(m_symbol.Name(),m_Ind_Timeframe,IND_CUSTOM,class="num">2,parameters))
    {
      printf(__FUNCTION__+": object initialization error");
      class="kw">return(false);
    }
   
class=class="str">"cmt">//--- 缓冲区的数量
  if(!m_indicator.NumBuffers(class="num">2)) class="kw">return(false);

class=class="str">"cmt">//--- ASCtrend 指标初始化成功
  class="kw">return(true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 检查建立多头头寸的条件,并检查多头头寸的有效性。
class=class="str">"cmt">//| 并关闭一个简短的|
class=class="str">"cmt">//| INPUT:无|
class=class="str">"cmt">//| 输出:从 class="num">0 到 class="num">100 的投票权重|
class=class="str">"cmt">//|| 备注:没有。|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CASCtrendSignal::LongCondition()
  {
class=class="str">"cmt">//--- 买入信号由 ASCtrend 指标的缓冲区 class="num">1 决定
  class="type">class="kw">double Signal=m_indicator.GetData(class="num">1,m_SignalBar);
class=class="str">"cmt">//--- 获得交易信号 
  if(Signal && Signal!=EMPTY_VALUE)
    {
      if(m_BuyPosOpen)
        {
         if(m_SellPosClose) class="kw">return(REVERSE_SHORT);
         else class="kw">return(OPEN_LONG);
        }
      else
        {
         if(m_SellPosClose) class="kw">return(CLOSE_SHORT);
        }
    }
class=class="str">"cmt">//--- 搜索关闭空头仓位的信号
  if(!m_SellPosClose) class="kw">return(NO_SIGNAL);
  class="type">int Bars_=Bars(m_symbol.Name(),m_Ind_Timeframe);
  for(class="type">int bar=class="type">int(m_SignalBar); bar<Bars_; bar++)
    {
      Signal=m_indicator.GetData(class="num">0,bar);
      if(Signal && Signal!=EMPTY_VALUE) class="kw">return(NO_SIGNAL);
      Signal=m_indicator.GetData(class="num">1,bar);
      if(Signal && Signal!=EMPTY_VALUE) class="kw">return(CLOSE_SHORT);
    }
class=class="str">"cmt">//--- 没有交易信号
  class="kw">return(NO_SIGNAL);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 检查建立空头头寸的条件,并检查空头头寸的有效性。
class=class="str">"cmt">//| 关闭一个长的|
class=class="str">"cmt">//| INPUT:无|
class=class="str">"cmt">//| 输出:从 class="num">0 到 class="num">100 的投票权重|
class=class="str">"cmt">//|| 备注:没有。|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CASCtrendSignal::ShortCondition()
  {
class=class="str">"cmt">//--- 卖出信号由 ASCtrend 指标的缓冲区 class="num">0 决定
  class="type">class="kw">double Signal=m_indicator.GetData(class="num">0,m_SignalBar);

class=class="str">"cmt">//--- 获得交易信号
  if(Signal && Signal!=EMPTY_VALUE)
    {
      if(m_SellPosOpen)
        {
         if(m_BuyPosClose) class="kw">return(REVERSE_LONG);
         else class="kw">return(OPEN_SHORT);
        }
      else
        {
         if(m_BuyPosClose) class="kw">return(CLOSE_LONG);
        }
    }
class=class="str">"cmt">//--- 搜索多头平仓信号
  if(!m_BuyPosClose) class="kw">return(NO_SIGNAL);

信号条遍历里的平仓判定逻辑

这段信号类代码的核心,是从指定的信号条开始向右遍历当前图表 Bars 数量,逐根检查 ASCtrend 指标缓冲区的数值,决定返回哪种指令常量。 int Bars_=Bars(m_symbol.Name(),m_Ind_Timeframe); 这里把原先的 Symbol() 换成了 m_symbol.Name(),避免多品种环境下取到当前图表品种而非信号实例绑定品种,属于实盘里容易踩的坑。 for(int bar=int(m_SignalBar); bar<Bars_; bar++) { Signal=m_indicator.GetData(1,bar); if(Signal && Signal!=EMPTY_VALUE) return(NO_SIGNAL); Signal=m_indicator.GetData(0,bar); if(Signal && Signal!=EMPTY_VALUE) return(CLOSE_LONG); } return(NO_SIGNAL); 先读缓冲区 1(通常对应卖出/空信号线),有值就直接无信号;再读缓冲区 0(多信号线),有值就返回 CLOSE_LONG=40,即给 EA 发平多指令。 宏定义里 OPEN_LONG / OPEN_SHORT 都是 80,CLOSE_LONG / CLOSE_SHORT 都是 40,REVERSE_LONG 是 100,这些常数直接决定 EA 上层如何解析信号类的返回值。外汇与贵金属杠杆高,信号常量映射错了可能频繁误平仓,上 MT5 用策略测试器跑一遍 H4 周期验证最稳妥。

MQL5 / C++
class="type">int Bars_=Bars(m_symbol.Name(),m_Ind_Timeframe); class=class="str">"cmt">// Здесь код исправлен с учетом подсказки от Владимира Карпутова: Symbol() заменен на m_symbol.Name()
for(class="type">int bar=class="type">int(m_SignalBar); bar<Bars_; bar++)
  {
   Signal=m_indicator.GetData(class="num">1,bar);
   if(Signal && Signal!=EMPTY_VALUE) class="kw">return(NO_SIGNAL);
   Signal=m_indicator.GetData(class="num">0,bar);
   if(Signal && Signal!=EMPTY_VALUE) class="kw">return(CLOSE_LONG);
  }
class=class="str">"cmt">//--- 没有交易信号 
  class="kw">return(NO_SIGNAL);
}

「ASCtrend 信号类的指令常数与权限骨架」

在 MT5 里把 ASCtrend 指标接进 EA,第一步是定义一个从 CExpertSignal 派生的信号类 CASCtrendSignal。它不自己画指标,只负责把指标状态翻译成开平反的指令码,交给上层交易引擎处理。 这个类通过向导描述暴露了 7 个可调参数:买卖开关各一对(默认 true)、退出多空权限各一对(默认 true)、指标周期默认 PERIOD_H4、风险等级 RISK 默认 4、信号条索引 SignalBar 默认 1。也就是说,默认逻辑是看 H4 上第 1 根已完成 K 线产生的信号,风险档位偏激进。 指令常数才是实战里要盯死的。开多 OPEN_LONG 与开空 OPEN_SHORT 都定为 80,平多 CLOSE_LONG 与平空 CLOSE_SHORT 都是 40,而 REVERSE_LONG / REVERSE_SHORT 反转指令给到 100,NO_SIGNAL 为 0。数值越大代表优先级越高,反转压过开仓、开仓压过平仓,这个权重顺序直接决定回测里会不会频繁反手。 外汇与贵金属杠杆高,这套默认参数在 H4 上反转阈值拉满,遇震荡市可能连续吃反向单。建议先原样编译跑一遍 EURUSD 的 2020—2023 年 tick 回测,确认信号触发次数与预期一致,再动 RISK 和 SignalBar。

MQL5 / C++
class="macro">#define REVERSE_SHORT class="num">100 class=class="str">"cmt">// 向智能交易系统返回反向空头仓位指令的常数
class="macro">#define NO_SIGNAL      class="num">0   class=class="str">"cmt">// 向智能交易系统返回无信号的常数
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| CASCtrendSignal 类。|
class=class="str">"cmt">//| 用途:基于|......的交易信号生成器类别。|
class=class="str">"cmt">//| ASCtrend indicator values [MQL5官方文档] ;|
class=class="str">"cmt">//| 源自 CExpertSignal 类。|
class=class="str">"cmt">//+----------------------------------------------------------------------+
class CASCtrendSignal : class="kw">public CExpertSignal
  {
class=class="str">"cmt">// 向导描述开始
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| 类的描述|
class=class="str">"cmt">//| Title=基于 ASCtrend 指标的信号
class=class="str">"cmt">//| 类型=高级信号|
class=class="str">"cmt">//| 名称=ASCtrend|
class=class="str">"cmt">//| Class=CASCtrendSignal|
class=class="str">"cmt">//| Page=|
class=class="str">"cmt">//| 参数=BuyPosOpen,class="type">bool,true,购买权限
class=class="str">"cmt">//| 参数=SellPosOpen,class="type">bool,true,销售权限
class=class="str">"cmt">//| 参数=BuyPosClose,class="type">bool,true,退出多头头寸的权限
class=class="str">"cmt">//| 参数=SellPosClose,class="type">bool,true,退出空头头寸的权限
class=class="str">"cmt">//| 参数=Ind_TIMEFRAMES,ENUM_TIMEFRAMES,PERIOD_H4,时间帧
class=class="str">"cmt">//| 参数=RISK,class="type">int,class="num">4,风险等级|
class=class="str">"cmt">//| Parameter=SignalBar,class="type">uint,class="num">1,Bar index for entry signal || 参数=信号条,class="type">uint,class="num">1,输入信号的条形图索引
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">// 向导描述 结束
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|ASCtrendSignal.mqh
class=class="str">"cmt">//|版权所有 © class="num">2011, Nikolay Kositsin |
class=class="str">"cmt">//|哈巴罗夫斯克, farria@mail.redcom.ru | |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "版权所有©class="num">2011,尼古拉-科西钦"
class="macro">#class="kw">property link      "farria@mail.redcom.ru"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 包含的文件|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Expert\ExpertSignal.mqh>
class=class="str">"cmt">// 向导描述开始
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| 类的描述|
class=class="str">"cmt">//| Title=基于 ASCtrend 指标的信号
class=class="str">"cmt">//| 类型=高级信号|
class=class="str">"cmt">//| 名称=ASCtrend|
class=class="str">"cmt">//| Class=CASCtrendSignal|
class=class="str">"cmt">//| Page=|
class=class="str">"cmt">//| 参数=BuyPosOpen,class="type">bool,true,购买权限
class=class="str">"cmt">//| 参数=SellPosOpen,class="type">bool,true,销售权限
class=class="str">"cmt">//| 参数=BuyPosClose,class="type">bool,true,退出多头头寸的权限
class=class="str">"cmt">//| 参数=SellPosClose,class="type">bool,true,退出空头头寸的权限
class=class="str">"cmt">//| 参数=Ind_TIMEFRAMES,ENUM_TIMEFRAMES,PERIOD_H4,时间帧
class=class="str">"cmt">//| 参数=RISK,class="type">int,class="num">4,风险等级|
class=class="str">"cmt">//| Parameter=SignalBar,class="type">uint,class="num">1,Bar index for entry signal || 参数=信号条,class="type">uint,class="num">1,输入信号的条形图索引
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">// 向导描述 结束
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 常量声明|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define OPEN_LONG     class="num">80    class=class="str">"cmt">// 向智能交易系统返回买入指令的常数
class="macro">#define OPEN_SHORT    class="num">80    class=class="str">"cmt">// 向智能交易系统返回卖出指令的常数
class="macro">#define CLOSE_LONG    class="num">40    class=class="str">"cmt">// 向智能交易系统返回多头平仓指令的常数
class="macro">#define CLOSE_SHORT   class="num">40    class=class="str">"cmt">// 向智能交易系统返回空头平仓指令的常数
class="macro">#define REVERSE_LONG  class="num">100   class=class="str">"cmt">// 向智能交易系统返回多头反转指令的常数
class="macro">#define REVERSE_SHORT class="num">100   class=class="str">"cmt">// 向智能交易系统返回反向空头仓位指令的常数
class="macro">#define NO_SIGNAL      class="num">0    class=class="str">"cmt">// 向智能交易系统返回无信号的常数

◍ 把工具请下神坛

CASCtrendSignal 类直接继承 CExpertSignal,把 ASCtrend 指标值接进专家信号框架,相当于给 MT5 原生信号系统接了一根外挂管线。 在 MT5 里新建 EA 时,若想用 ASCtrend 做过滤,直接 #include 该类并实例化,比从头写指标缓冲读取省掉约 40 行样板代码。 外汇与贵金属杠杆高、滑点随机,任何信号类都只是概率过滤器,回测盈利不代表实盘能复制。 把它当校验想法的尺子,而不是替你下注的圣人;哪天你改一改阈值发现假突破少了,才是真用明白了。

MQL5 / C++
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| CASCtrendSignal 类。|
class=class="str">"cmt">//| 用途:基于|......的交易信号生成器类别。|
class=class="str">"cmt">//| ASCtrend indicator values [MQL5官方文档] ;|
class=class="str">"cmt">//| 源自 CExpertSignal 类。|
class=class="str">"cmt">//+----------------------------------------------------------------------+
class CASCtrendSignal : class="kw">public CExpertSignal
  {
让小布替你跑这套参数
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到信号标签与持仓边界,把重复劳动交给小布,你专注决策。

常见问题

信号指标多在当前柱画箭头标签,EA 读标签状态;信号趋势指标常改 K 线形态,EA 需比较指标缓冲区方向,下单代码相近但取数略有不同。
小布盯盘品种页已内置常见信号指标的图形标记与预警,不必自己挂 EA 也能先看盘口信号强弱。
零值代表不预设离场距离,挂单需要偏移价位来放置,逻辑上冲突,多数通用交易函数会直接禁用该类下单请求。
优先保留原通用交易库,只替换信号过滤条件,避免动到滑点控制和持仓权限,否则回测表现会失真。
会。若 EA 读历史柱而非当前柱,信号会慢一根;外汇贵金属波动快,这类延迟可能让进场点偏移明显,属高风险操作。