在MQL5中实现盈亏平衡机制(第一部分):基类与固定点数的盈亏平衡模式(基础篇)
「先把盈亏平衡的基类骨架搭起来」
在 MT5 里做止损自动平移,第一步不是写策略,而是把「盈亏平衡」这件事抽象成一个可复用的基类。原文给出的 CBreakEvenBase 就是干这个的:它不负责具体平移规则,只管持仓筛选、单子遍历、以及把计算结果回写止损位的通用流程。 具体落地时,作者先定义了结构体与枚举来区分不同的盈亏平衡模式(例如按固定点数触发、还是按盈利比例触发)。基类里提供通用函数,子类只要重写「何时触发」和「止损移到哪」两个逻辑即可。这样后续加新模式,不用动底层循环。 首个可直接用的子类叫 CBreakEvenSimple,实现的是最朴素的固定点数盈亏平衡:当浮盈达到设定点数,就把止损搬到开仓价加(多单)/减(空单)一个偏移。外汇与贵金属杠杆高、滑点随机,这类机制只能降低回撤概率,不保证不亏。 原文给出的示例发布于 2026-05-19,在 MetaTrader 5 环境编译通过,基础类约 344 行示例工程体量。开 MT5 新建 EA,把基类头文件 include 进去,就能在 OnTradeTransaction 里挂上 CBreakEvenSimple 做验证。
把止损推到开仓价到底在防什么
把止损移到盈亏平衡位,本质是在价格朝盈利方向走出一段后,把止损价拨到入场价附近。这样做最直接的收益是:哪怕后续行情突然回头,持仓也不会以亏损收场,只是把既得浮盈让出一部分。 这套机制主要扛两件事。一是保证价格没摸到止盈前,交易不会以负收益离场;二是在入场价之外留几个点作为缓冲,把已经跑出来的利润锁住,而不是任由回撤吞掉。 外汇和贵金属杠杆高、跳空频繁,盈亏平衡不是免死金牌,只是把亏损概率压到接近零的被动防护。本篇先搭系统基类,做出第一种简易盈亏平衡模板,后面两种都从它扩出去。
◍ 三种盈亏平衡移损的触发逻辑
盈亏平衡的本质是:价格偏离开仓价一定距离后,把止损搬到入场价附近,用少量浮赢或持平去置换继续持仓的权利。原文图1到图3展示了一个空头例子——初始止损挂在 Order Blocks 信号位,新K线出现后系统自动把止损推到离场150点处,最终价格在盈亏平衡位平仓,躲掉了全额止损。这里150点就是固定点数模式的典型参数,对黄金约折合1.50美元。 固定点数模式简单直接,但缺陷在于不认波动率。若黄金在重大数据前单波扫出3.00美元以上,150点(1.50美元)的盈亏平衡线很容易被正常噪音打掉,交易没走到趋势就 premature 出局。 ATR模式用乘数替代死点数:止损距离 = ATR × 乘数,随市场波动率呼吸。高波动品种如XAUUSD用这套更不容易被毛刺洗出去,但参数要自己按品种调,MT5里加载iATR看当前值最直观。 RRR模式按风险回报比搬止损。假设开仓止盈是止损两倍(RRR 1:2),可设“浮盈达到1:1初始风险”时止损移入场价;激进的也可等1:2、1:3才移。胜率和RRR成反比——比值越高价格要跑越远,比值越低反向亏损越狠,移损节奏得和策略匹配。外汇与贵金属杠杆高、滑点跳空频繁,任何移损都不能消灭风险,只是把亏损概率重新分配。
「盈亏平衡要先备好的结构体与枚举」
写盈亏平衡逻辑前,先别急着碰主类。把持仓要跟踪的核心字段和模式开关用结构体、枚举固化下来,后面挂止损、判突破才不会乱。 position_be 结构体只装四样东西:ticket 单号、breakeven_price 目标止损位、price_to_beat 需被突破的触发价、以及 ENUM_POSITION_TYPE 持仓方向。少一个,运行时就得现算,既慢又易错。 激活方式用 ENUM_BREAKEVEN_TYPE 区分三种:按盈亏比(RR)、按固定点数、按 ATR。模式层再用 ENUM_BREAKEVEN_MODE 切自动与手动——自动靠 OnTradeTransaction 抓新仓,手动留给你挑单子和时机,离散剥头皮或高精度设置用着更顺。 外汇与贵金属波动剧烈,自动模式虽省事但可能在没有人工过滤时保护到不该保护的单子,实盘前请在 MT5 策略测试器里用历史数据验一遍触发逻辑。
class="kw">struct position_be { class="type">ulong ticket; class=class="str">"cmt">//Position Ticket class="type">class="kw">double breakeven_price; class=class="str">"cmt">//Be price class="type">class="kw">double price_to_beat; class=class="str">"cmt">//Price to exceed to reach class="kw">break even class="type">ENUM_POSITION_TYPE type; class=class="str">"cmt">//Position type }; enum ENUM_BREAKEVEN_TYPE { BREAKEVEN_TYPE_RR = class="num">0, class=class="str">"cmt">//By RR BREAKEVEN_TYPE_FIXED_POINTS = class="num">1, class=class="str">"cmt">//By FixedPoints BREAKEVEN_TYPE_ATR = class="num">2 class=class="str">"cmt">//By Atr }; enum ENUM_BREAKEVEN_MODE { BREAKEVEN_MODE_AUTOMATIC, BREAKEVEN_MODE_MANUAL };
搭好保本逻辑的基类骨架
做自动保本 EA,第一步是把通用状态塞进一个基类 CBreakEvenBase,后续各种保本策略子类直接继承,不用重复写订单发送、品种点值、magic 过滤这些脏活。 基类受保护成员里,obj_trade 负责发单改单,tick 存最新报价,symbol 和 point_value 锁定品种与点值,PositionsBe[] 装待管理的持仓,magic 做订单识别,pause 防数组越界,num_params 留给孩子类填,breakeven_mode 切手动或自动,allow_extra_logs 控制冗余日志。 构造函数吃三个参:symbol_ 取买卖价、magic_ 识别订单、mode_ 定保本类型。进去先把 pause 和 allow_extra_logs 置 false,校验 mode_ 合法;不合法就打印错误并 ExpertRemove 把 EA 踢出图表,避免错配继续跑。point_value 在构造时算好,num_params 初始化 0,由各子类构造补。 若 magic_ 等于 NOT_MAGIC_NUMBER(风险管理头文件里的宏,表示不筛 magic),就不调 CTrade 的设 magic 函数,自动模式下等于对所有单子生效——外汇和贵金属波动大,这种全量兜底容易误伤非策略单,接之前想清楚。 析构函数只干一件实事:释放 PositionsBe[] 内存,对象销毁时自动跑,不用手管。 下面这段是类声明与构造头,可直接贴进 MT5 看编译。逐行看:#include 拉风险管理头;protected 块列了 9 个成员及注释;构造声明取 string/ulong/枚举三参;实现头接着写。
class="macro">#include <Risk_Management.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Main class to apply class="kw">break even | class=class="str">"cmt">//+------------------------------------------------------------------+ class CBreakEvenBase { class="kw">protected: CTrade obj_trade; class=class="str">"cmt">//CTrade object class="type">MqlTick tick; class=class="str">"cmt">//tick structure class="type">class="kw">string symbol; class=class="str">"cmt">//current symbol class="type">class="kw">double point_value; class=class="str">"cmt">//value of the set symbol point position_be PositionsBe[]; class=class="str">"cmt">//array of positions of type Positions class="type">ulong magic; class=class="str">"cmt">//magic number of positions to make class="kw">break even class="type">bool pause; class=class="str">"cmt">//Boolean variable to activate the pause of the review, this is used to prevent the array from going out of range class="type">int num_params; class=class="str">"cmt">//Number of parameters the class needs ENUM_BREAKEVEN_MODE breakeven_mode; class=class="str">"cmt">//Break even mode, manual or automatic class="type">bool allow_extra_logs; }; CTrade obj_trade; class=class="str">"cmt">//CTrade object class="type">MqlTick tick; class=class="str">"cmt">//tick structure class="type">class="kw">string symbol; class=class="str">"cmt">//current symbol class="type">class="kw">double point_value; class=class="str">"cmt">//value of the set symbol point position_be PositionsBe[]; class=class="str">"cmt">//array of positions of type Positions class="type">ulong magic; class=class="str">"cmt">//magic number of positions to make class="kw">break even class="type">bool pause; class=class="str">"cmt">//Boolean variable to activate the pause of the review, this is used to prevent the array from going out of range class="type">int num_params; class=class="str">"cmt">//Number of parameters the class needs ENUM_BREAKEVEN_MODE breakeven_mode; class=class="str">"cmt">//Break even mode, manual or automatic class="type">bool allow_extra_logs; CBreakEvenBase(class="type">class="kw">string symbol_, class="type">ulong magic_, ENUM_BREAKEVEN_MODE mode_); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CBreakEvenBase::CBreakEvenBase(class="type">class="kw">string symbol_, class="type">ulong magic_, ENUM_BREAKEVEN_MODE mode_)