双均线交叉:完整实现与逐行讲解
双均线交叉大概是所有量化教程里出场率最高的策略。它简单到用几行伪代码就能描述,但又完整包含了「信号产生 → 仓位计算 → 止损设置 → 平仓管理」这条完整链路,所以非常适合作为第一个真正能跑的策略来练手。
这一篇我们会从策略原理讲到一份完整可运行的 MQL5 代码,并且逐行解释为什么这么写。
策略逻辑
规则本身非常直白:
- 快速均线上穿慢速均线 → 做多
- 快速均线下穿慢速均线 → 做空
- 持仓期间用 ATR 动态止损跟踪
- 每次开仓的风险固定为账户净值的一个百分比
注意:这里说的是「策略逻辑」,不是「盈利能力」
本篇的目标是让你看懂一个完整 EA 是怎么组织起来的。至于这组参数在哪个品种、哪段时间表现如何,那是 L5 回测与验证要解决的问题 —— 在那之前讨论有效性没有意义。
先建立指标句柄
MQL5 里不直接算均线,而是先建立指标句柄,再从句柄里取数据。在 OnInit() 里建立:
//--- 建立指标句柄
hFastEMA = iMA(_Symbol, PERIOD_H1, 20, 0, MODE_EMA, PRICE_CLOSE);
hSlowEMA = iMA(_Symbol, PERIOD_H1, 50, 0, MODE_EMA, PRICE_CLOSE);
hATR = iATR(_Symbol, PERIOD_H1, 14);
if(hFastEMA == INVALID_HANDLE || hSlowEMA == INVALID_HANDLE)
{
Print("指标句柄创建失败");
return INIT_FAILED;
}
句柄的三个注意点
- 句柄只建立一次,不要在
OnTick()里反复创建,否则会内存泄漏 - 必须检查返回值。很多新手 EA 的崩溃都源于没有处理句柄创建失败的情况
- 周期写在句柄里,而不是在
OnTick()里判断,这样逻辑更干净
判断交叉
「上穿」的定义是:上一根 K 线快线在慢线之下,当前这根快线在慢线之上。所以需要取两根的数据:
double fast[2], slow[2]; CopyBuffer(hFastEMA, 0, 1, 2, fast); CopyBuffer(hSlowEMA, 0, 1, 2, slow); bool goldenCross = (fast[1] > slow[1]) && (fast[0] <= slow[0]); bool deadCross = (fast[1] < slow[1]) && (fast[0] >= slow[0]);
注意数组下标。CopyBuffer 默认按时间序列排列,[1] 是当前已收线的 K 线,[0] 是它的前一根。用已收线的那根做判断,可以避免信号重绘。
计算手数
这是新手最容易写错的一步。目标不是「固定 0.1 手」,而是「让这单的潜在亏损等于净值的固定百分比」:
double riskMoney = AccountInfoDouble(ACCOUNT_EQUITY) * RiskPercent / 100.0; double lossPerLot = (slDistance / tickSize) * tickValue; double lot = riskMoney / lossPerLot;
得到手数后还要按品种的步进值与上下限做归一化,否则下单会被经纪商拒绝。
riskMoney = 账户净值 × RiskPercent ÷ 100
lossPerLot = 止损距离 ÷ 点值单位 × 每点价值
lot = floor(riskMoney ÷ lossPerLot ÷ 步进) × 步进
手数最终还要用 MathMax 夹在最小手数与品种上限之间,否则下单会被经纪商拒绝。
完整的开仓与止损
最后把这些拼起来。完整源码(含移动止损、点差过滤、日内回撤熔断)已经在策略库里,会员可以直接下载并在 MetaEditor 里编译运行。
参数一览
下面列出本文代码里直接出现的参数与取值,方便你对照源码调整。
| 参数 | 含义 | 取值 |
|---|---|---|
| FastEMA_Period | 快线 EMA 周期 | 20 |
| SlowEMA_Period | 慢线 EMA 周期 | 50 |
| ATR_Period | ATR 周期 | 14 |
| RiskPercent | 单笔风险占净值比例 | 外部 input,由调用方传入 |