使用信号指标的简单交易系统·综合运用
(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。
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 |
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 数据验证。
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 的阈值在欧美盘波动里的触发频率,可能比想象中更密集。
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 黄金的触发频率,能直观看出平滑深度对信号稀疏度的影响。
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 返回动作指令的阈值,改它们等于改信号触发灵敏度,建议先在回测里调。
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 做单向验证。 下面这段是类的核心声明与参数写入方法,逐行看能明白每个字段怎么落进对象内存。
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 间隔对策略敏感度的牵引。
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 策略测试器跑一遍多周期回测。
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 周期验证最稳妥。
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。
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 行样板代码。
外汇与贵金属杠杆高、滑点随机,任何信号类都只是概率过滤器,回测盈利不代表实盘能复制。
把它当校验想法的尺子,而不是替你下注的圣人;哪天你改一改阈值发现假突破少了,才是真用明白了。
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 {