简介:锁仓顺势加码EA源码是一份基于MetaTrader平台的自动交易系统实现,面向外汇交易者与EA开发者,用于理解锁仓、顺势加码、自动止盈及对冲单管理的一体化策略。资源包为单个doc文档,大小45KB,内含完整源代码与知识点总结,覆盖系统功能概述、ATR指标应用、核心函数解析及参数设置说明,方便读者直接查阅或移植到MT4平台。已有1379人学习下载。文档从Hedge_Assistante函数、start入口、订单选择和平仓逻辑等维度展开,具体展示了如何通过ATR动态判断波动区间、对手动订单设置止盈,并对对冲单进行自动化处理;其中也涉及代码注释和版本迭代记录,有助于初学者理解EA开发的常见结构,也可作为二次开发的基础模板。整体内容精炼,适合希望快速上手外汇EA编写或优化交易策略的读者。
1. 锁仓顺势加码EA:它到底在解决什么交易问题
持仓浮亏几百点,平掉怕反弹,拿着怕继续跌——这是做趋势交易时最纠结的时刻。锁仓顺势加码EA就是把这种纠结变成一套可执行的程序规则:浮亏时用反向单把风险暂时冻结,等趋势信号重新确认后再顺势加码,让利润跑起来。它既不是马丁EA那种逆势摊平,也不是网格EA的来回刷单;它做的是「先锁风险、再选方向、最后放大盈利」的组合动作。适合已经有一定趋势交易经验、受够手动锁仓/解锁反复折腾的人;不适合把EA当印钞机、追求每单必赢的用户。理解了盈亏同源,再谈实盘投入,才是这套策略的正确打开方式。
2. 为什么「锁仓 + 顺势 + 加码」三者缺一不可:先想清楚逻辑再动笔写代码
2.1 锁仓不是解套工具:它只负责把浮亏「冻结」成等待成本
「锁仓」最容易引起误解。在MT4/MT5里,锁仓就是在同一品种上建一张方向相反、数量相当的订单,让现有浮亏不再随行情扩大。注意,锁仓没有消除亏损,它只是把浮亏的「记账」暂停,让你不用在情绪最差的时候被迫做决策。代价也很明确:双向持仓会占用更多保证金,每次开平都要付点差,等于花钱买「决策时间」。
这个策略为什么需要锁仓?因为「顺势加码」天然会面临单子浮亏——趋势判断错了一次,或者行情暂时回调,你的加码仓就是亏的。传统做法是挂止损,但坏处是当你方向只错一次、趋势其实还在时,止损会把你扫出去,后面那段利润与你无关。锁仓的做法是:等趋势信号反转时,平掉错误方向的订单,保留或重开正确方向的单子。相当于把「硬止损」换成了「换向止损」,成本是多付一些点差和隔夜利息,收益是保留了重新站上顺风侧的机会。
所以在源码设计上,锁仓不是「扛单」的别名,而是有时间上限的等待。我的习惯是给每个锁仓组设一个最长持续时间,默认6小时,超过时间不管方向是否明确,都要强制处理:要么全部平仓离场,要么解锁后按当时信号重开首单。没有时间约束的锁仓,最后都会变成慢性失血。
2.2 顺势的判断标准:均线方向与 ADX 阈值怎么搭配才不容易被骗线
「顺势」的「势」必须有个能写进代码的定义,否则EA就谈不上自动。我常用的判断是双EMA加上ADX:快速EMA(默认21)在慢速EMA(默认55)之上,同时ADX(默认周期14)高于阈值25,才认为趋势向上。
为什么不用单一的「价格在均线上方」?因为在震荡市里,价格经常上穿下穿均线,给出大量假信号。ADX低于25时,就算均线方向朝上,也大概率是横盘的一段,这时候加码很容易被回调打脸。再加一个「快慢均线的相对距离」约束(比如快线必须比慢线高至少0.1%),可以把均线缠绕的模糊地带排除掉。这个约束值不能设太大,否则趋势启动后你会错过第一次入场。
这里要注意,趋势判断的周期选择是整套EA里最容易被回测优化带偏的地方。把快慢周期改成8和13,回测曲线可能很好看,但实盘上同一段行情可能给你完全不同的结果。我的经验是:先固定均线组合跑通「订单管理和加码逻辑」,再去动周期参数;顺序反了,你永远分不清是逻辑问题还是参数问题。
2.3 加码的节奏控制:间隔、倍数、最大层数是三个必调参数
「加码」是整个策略的进攻端,也是最容易写崩的地方。三个参数必须一起调,缺一个都会出问题。
第一个是间隔:加码单不能离上一单太近,否则行情还没走成趋势,你已经把子弹打光了。常见做法是用「ATR × 倍数」代替固定点数,默认0.8倍ATR,高波动时段自动拉大间隔,低波动时段自动收窄,多品种通用。第二个是手数递增:很多顺势加码EA用固定手数,但更常见的是逐层递增,每层乘1.5倍左右,让越远离成本的仓位越大,这与趋势交易「让利润奔跑」的思路一致。注意:递增倍数过大,比如每层乘3,三层以后的手数已经是指数级,一旦趋势末段反转,回撤会超出承受范围。第三个是最大层数,默认4层,每加一层都要重新计算「如果不幸在最后一层反转,当时整体浮亏是多少」,确保没超过账户能承受的极限。
这三个参数相互牵制:间隔越小、递增倍数越大、最大层数越多,潜在收益越高,回撤也随之升高。源码里最好把这三个参数做成可调输入项,避免每改一次就重新编译。
3. 从策略到源码:核心订单管理函数与加码触发条件的 MQL5 写法
3.1 订单分组:用一个策略ID把首单、加码单、锁仓单串成一串
EA的难点不是开单,而是「知道这些单子是一组的」。MT5里每个持仓都有魔术数字(magic)和注释(comment),我一般用magic区分策略,用comment里的一段chainId标识「同一组加码链」。一个加码链包含首单、若干顺势加码单、可能存在的锁仓单,它们必须共享同一个chainId。
下面的函数统计一组订单的总手数,方便后续计算锁仓手数、监控整体浮亏:
#define CHAIN_PREFIX "CHAIN_" double GroupVolume(const long inp_magic, const string chainId) { double volume = 0.0; for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(PositionGetInteger(POSITION_MAGIC) != inp_magic) continue; string comment = PositionGetString(POSITION_COMMENT); if(StringFind(comment, chainId) < 0) continue; volume += PositionGetDouble(POSITION_VOLUME); } return volume; }这个函数从后往前遍历当前所有持仓,先用magic过滤掉其它EA的单子,再用chainId过滤掉同策略里的其它加码链,最后累加手数。你可能会问:为什么不用订单号做分组?因为加码过程中会不断有新单进来,交易平台的动作顺序不好保证,用「注释里的组ID」是最稳定、最容易人工核对的方式。
chainId的生成我一般用「品种代码 + 策略实例序号 + 建链时间戳」,例如CHAIN_XAUUSD_20240101120000。注意每次重新挂载EA时魔术数字不能和别的EA重复,注释保持纯英文和数字,避免部分平台在订单备注显示时出现编码问题。
3.2 顺势加码的入场条件:趋势确认 + 回调到位,缺一个都不开
顺势加码最怕的是「趋势还没确认就急着开」,所以触发条件必须写成两个独立子条件都满足才动作。先看趋势判断函数:
bool IsTrendUp() { double maFast = iMA(_Symbol, PERIOD_CURRENT, MaFast, 0, MODE_EMA, PRICE_CLOSE, 0); double maSlow = iMA(_Symbol, PERIOD_CURRENT, MaSlow, 0, MODE_EMA, PRICE_CLOSE, 0); double adxValue = iADX(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE, 0); double distance = (maFast - maSlow) / maSlow; return (distance > 0.001 && adxValue > AdxFilter); }这段代码用三条规则把一个布尔值拼出来:快EMA在慢EMA之上、两条均线之间的相对距离大于0.1%、ADX大于设定的过滤器(默认25)。注意PERIOD_CURRENT表示当前图表周期,如果想用固定周期判断方向,改成PERIOD_H1即可,加码触发周期和方向判断周期分开是常见做法。
在OnTick()里,每次价格波动都会执行这个判断。不过这里有个隐藏坑:单根K线价格来回穿越均线时,IsTrendUp()可能在几十秒内反复返回true和false。我的处理是加一个「冷却时间」——两次加码之间至少间隔多少秒,默认600秒,把同一根K线上的反复触发压掉。这个冷却放在下一节的开单函数里判断。
3.3 锁仓单的开与解:开锁仓单看风险,解锁要看确认信号而不是猜底
当一组订单的浮亏触及阈值,比如整体浮亏达到账户净值的2%,就该开锁仓单。锁仓单的手数等于GroupVolume()返回的总手数,方向相反,这样行情再怎么走,这组订单的净值基本不再变动:
void LockChain(const long magic, const string chainId) { double vol = GroupVolume(magic, chainId); if(vol <= 0.0) return; bool trendUp = IsTrendUp(); MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); request.action = TRADE_ACTION_DEAL; request.symbol = _Symbol; request.volume = vol; request.type = trendUp ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; request.price = (request.type == ORDER_TYPE_SELL) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK); request.deviation = 20; request.magic = magic; request.comment = chainId + "_LOCK"; if(!OrderSend(request, result)) Print("锁仓失败: ", result.retcode, " 错误: ", result.comment); }锁仓单方向不是随便选的,而是与当前趋势判断相反:判断趋势向上但你手里有多单浮亏,锁仓单就该是卖出单,等趋势真的走弱时,多单解锁出场,留下或重开空单。deviation是20点的滑点容忍,行情剧烈时设太小会导致订单直接拒单,设太大会在放大行情里吃明显滑点,这个值要根据品种点差调整。
解锁条件我从来不写成「价格回到成本价」,那是猜底。解锁只认一个事实:趋势信号反转。当IsTrendUp()由true变成false且ADX仍然大于阈值,就把整组全部平掉,再按新方向开一张新的首单。这个「全部平仓再重开」的动作,等于把止损换成了换向,也正是整套EA相对传统趋势EA的核心差异。
4. 参数怎么设:先用一组初始参数表跑通回测,再谈调优
4.1 初始参数表:方向过滤、加码间隔、锁仓触发分开调
在真正做参数优化之前,先把每类参数分清楚。下表是我常用的一组初始值,适合EURUSD、XAUUSD这类流动性和波动率中等的品种;纯小币种或指数要按实际ATR缩放:
| 参数 | 作用 | 初始值 | 调优范围 |
|---|---|---|---|
| MaFast | 快速均线周期 | 21 | 13 ~ 34 |
| MaSlow | 慢速均线周期 | 55 | 34 ~ 89 |
| AdxFilter | ADX趋势强度门槛 | 25 | 20 ~ 35 |
| AtrFactor | 加码间隔 = ATR × 倍数 | 0.8 | 0.5 ~ 1.5 |
| VolFactor | 每层手数递增倍数 | 1.5 | 1.0 ~ 2.0 |
| MaxLevel | 最大加码层数 | 4 | 2 ~ 6 |
| LossLockPercent | 组内浮亏占净值比触发锁仓 | 2.0 | 1.5 ~ 3.5 |
| UnlockHours | 锁仓最长持有时间 | 6 | 3 ~ 12 |
分三组理解:MaFast/MaSlow/AdxFilter是一组「方向过滤器」,AtrFactor/VolFactor/MaxLevel是「进攻组」,LossLockPercent/UnlockHours是「防守组」。调优时每次只动一组,不要同时改三个方向的参数,否则回测结果变好,你都不知道是哪一步起了作用。
务必注意:初始参数不能直接拿去做遗传优化。先固定防守组,用行情数据跑一轮原始回测,看加码链能不能正常建立、锁仓单会不会频繁触发;如果防守组参数设得太紧,EA可能在正常波动里反复锁仓,手续费和延迟会吃掉全部利润。方向过滤器和进攻组可以稍后再配合调整。
4.2 回测周期与数据质量:MT5的三档模式选哪个,直接影响结论
MT5策略测试器里通常有三档:仅用开盘价、每个tick基于实时价格、每个tick基于真实数据。对这套EA,只能选后面两档,因为「锁仓单触发」依赖盘中价格插针,只靠开盘价根本测不出来。「每个tick基于实时价格」是用算法插值模拟的tick,适合快速验证;「每个tick基于真实数据」是回放数据商记录的tick流,适合做最终验证。
回测前先下载足够深的行情数据,时间跨度尽量覆盖近几年有单边趋势、有长时间震荡、有大跳空的阶段。常见做法是先跑「每个tick基于实时价格」做快速迭代,逻辑验证通过后再切到「每个tick基于真实数据」跑一遍终版。两次结果通常会有差异,要是差异超过20%,先怀疑逻辑本身,而不是换参数掩盖差异。
还有两个容易忽视的选项:点差和延迟。把点差设成当前品种的正常偏宽区间,把延迟设为默认或略高,尽量别用零延迟,否则加码触发价和实际成交价之间的差距会被回测掩盖。历史点差的模拟大多不准,手动把点差调成偏保守值,回测出来的净利润会比实际略差,但那是真实世界里可能达到的水平。
4.3 三类行情必须分开测:单边、宽幅震荡、大跳空
一个常见的回测误区是把一整段时间一次跑完,资金曲线好看就上线。问题是:你可能只覆盖了单边上涨,或者只覆盖了弱势震荡,策略的实际适用面根本没有验证到。
我的检查习惯是至少拆成三段。第一段找持续上升或下降的趋势段,验证顺势加码能不能正常触发、最大层数是否合理;第二段找价格反复穿越均线的宽幅震荡段,验证锁仓逻辑会不会被反复点爆,或者方向过滤虽开着却依旧来回止损;第三段找包含重要经济数据发布、明显大跳空的时段,验证解锁和一键平仓的滑点承受能力。
如果策略在震荡段表现很差但单边段表现很好,这是正常的,但要清楚它的「不适用区」在哪里,实盘才好做品种和时间的过滤。回测报告里重点看三个指标:最大回撤、连续止损次数、每笔平均盈利。最大回撤直接决定你能不能拿住这套EA;连续止损次数告诉你心理承受能力够不够;每笔平均盈利如果低于平均手续费的三倍,说明策略逻辑太薄,不如不用。
5. 锁仓顺势加码EA避坑指南:五个真实踩坑记录
5.1 加码条件写反了方向:浮盈加码变成浮亏摊平
现象:回测报告里盈利因子很高,但扔到实盘上一遇到单边反转就快速爆仓;打开订单记录发现,所有逆势加码单都在亏损时不断补仓。
原因:把加码触发条件写成了「价格反向走N点就加码」,这实际上变成了马丁加仓,与标题里的「顺势」背道而驰。有时是源码里状态变量在K线收盘前被反复改写,导致「顺势」的布尔值在回调那零点几秒里翻转,最终开在错误方向。
解决:在OnTick()里用K线收盘价做趋势判断,盘中只做加码触发检查;再加一条硬约束:加码单必须与首单方向一致,代码里用if(GetTrendDirection() != orderType) return;拦住一切不该开的单。这个判断看似简单,但十套源码里至少有四套会在这里埋雷。
5.2 锁仓单开在窄幅震荡里:没设时间失效,两边都被收割
现象:EA开锁后,行情在小区间里来回,持仓净值为正但一直没到解锁条件;隔了几天看,锁仓单被点差和库存费蚕食,资金曲线缓慢向下。
原因:锁仓把浮亏冻结了,但冻结期间每一秒都在持有成本,加上隔夜利息,时间一长锁仓本身就成了亏损来源。更糟的是,在窄幅震荡里解锁信号迟迟不出来,你等于被EA「锁死」在市场上。
解决:必须给锁仓加两个出口。时间出口就是UnlockHours,超过设定时间无论信号是否出现,都把整组平仓,拒绝无限等待;价格出口是「反向突破近N根K线的极值」,一旦突破说明震荡格局可能结束,立刻解锁并按突破方向重开。回测时要特别关注持仓周期这一列,平均持仓周期超过预期太多,八成是锁仓逻辑在拖时间。
5.3 固定点数间隔在多品种上失效:用ATR倍数代替固定点数
现象:同一组参数在EURUSD上回测正常,换到GBPJPY上后加码频率骤降,整个加码链迟迟建不起来;换到黄金上却变得过于密集,单子还没拉开距离就打满了最大层数。
原因:不同品种的日均波动差出几倍,固定点数与波动率脱钩。黄金一个波动可能就三四十点,GBPJPY一天能跑五六百点,拿同一个固定间隔显然不合理。
解决:将加码间距从固定点数改为AtrFactor * ATR。我用iATR(_Symbol, PERIOD_CURRENT, 14)动态取值,价格每走完一个ATR的0.8倍才允许加一层。这样还附带一个好处:市场波动变大时自动拉开间距,波动变小时自动收紧,等于每个品种有一套自适应节奏。唯一要注意的是ATR是滞后指标,遇到极端快速行情反应偏慢,所以仍然要配合最大层数控制总风险。
5.4 同价位重复加码:同一根K线被触发三单,间距真被吃掉了
现象:回测日志里显示某根K线上连续成交三笔加码单,价格几乎一样,手数却按倍数累加。结果趋势没走多远,整组建仓就满了,后面的大段利润全没吃到。
原因:加码条件在同一个tick里连续判断为真,EA在同一根K线、同一价位附近执行了多次开单。很多初版源码只写了「价格满足间距」就开单,没有把间距和「上次加码以来的最小推进」绑定。
解决:在加码函数入口处加载一个状态变量记录「上一次加码的价格」,新加码单要求当前价与上次加码价的绝对距离超过间隔阈值的1.2倍,并且两次开单之间至少经过MinAddMinutes(默认30分钟)的冷却。这条规则同时挡住了同K线重复触发和抢跑式的连续加码。
5.5 用「仅用开盘价」模式跑回测:锁仓加跳空,数据完全失真
现象:EA用小周期参数回测利润很好,切到每个tick模式后成绩大幅缩水甚至变负,上线后表现介于两者之间,根本没法确认哪个才是真实水平。
原因:锁仓、加码这类「盘中被一根K线触发」的逻辑,本质依赖盘中高低点和跳空,而「仅用开盘价」模式每根K线只给一个价格,等于把这些触发逻辑全部抹平。数据越细,真实滑点和延迟越接近实盘,回测差异自然越明显。
解决:快速迭代可以用「每个tick基于实时价格」,最终验证必须切到「每个tick基于真实数据」,并且把点差、延迟、滑点三个选项调到偏保守。这个选择不会让结果更好看,但会让你敢实盘。诚实起见,任何声称锁仓EA「在跳空行情里也完美执行」的回测百分比,都先打个问号。
6. 把源码变成能上实盘的EA:验证逻辑与上线前检查的最后一公里
6.1 回放日志逐单核对,而不是只看资金曲线
拿到任何一套EA源码,第一件事不是挂图表,而是打开它的日志输出。我给这套锁仓逻辑加一个Print()辅助输出,记录每次「趋势判断结果、加码触发价格、锁仓原因、解锁原因」,然后随便挑三段行情跑回测,打开工具盒的日志面板逐单比对。只有每一单的触发原因都能对上号,资金曲线才有参考价值;对不上号的钱,都是迟早要还回去的。
6.2 参数敏感性测试:每项±30%跑一轮,结果要「稳」不要「巧」
把参数表里每个参数上下调整30%,分别跑回测并记录最大回撤和净利润。如果某项参数只要动一点点,结果就从盈利变成大幅亏损,说明这套EA正卡在某个过拟合的尖峰上,这种「巧」不能要;相反,参数在小范围内变化时结果基本稳定,才值得继续投入实盘验证时间。所有参数一起优化的遗传算法结果,我从来不会直接用,那只是找了一个历史数据的死角。
6.3 上线前检查清单:从模拟盘到小仓位实盘的过渡习惯
最后列一份我每次上线前都会过的清单:模拟盘连续跑至少一个月,观察锁仓频率和组内平均持仓周期;实盘用小仓位跑两个月,期间只做「换方向」的微调,不碰进攻参数;每周末看一次持仓明细,确认有没有遗留的孤儿订单,比如EA掉线后单方向仓位无人管理。这套EA最危险的时刻正是它看起来最顺的时候——连续盈利会让人忍不住加仓位,而一次未触发的解锁就可能把几个月的利润还回去。我现在拿到任何一套EA源码,都会先问一句:它的锁仓出口在哪里。没有出口的策略再漂亮,我也不会用。希望帮到你。
本文还有配套的精品资源,点击获取