在MQL5中实现盈亏平衡机制(第一部分):基类与固定点数的盈亏平衡模式·进阶篇
📘

在MQL5中实现盈亏平衡机制(第一部分):基类与固定点数的盈亏平衡模式·进阶篇

第 2/3 篇

「保本逻辑初始化时的参数校验」

这段构造函数负责在EA加载时把交易对象、魔术码与保本模式接好,但真正的硬约束在模式校验上。 若传入的 mode_ 既不是手动也不是自动,代码会直接打印错误并调用 ExpertRemove() 终止EA,避免后续用无效模式跑单。 point_value 通过 SymbolInfoDouble(symbol_, SYMBOL_POINT) 取得,这是后续计算保本价位偏移的基准单位,黄金和欧美点值差异可能差出10倍以上。 析构函数里用 ArrayFree(PositionsBe) 释放持仓数组,不手动清会留下内存尾巴,多品种轮询时容易拖慢MT5。

MQL5 / C++
  : pause(false), allow_extra_logs(false)
{
  if(magic_ != NOT_MAGIC_NUMBER)
    obj_trade.SetExpertMagicNumber(magic_);
  if(mode_ != BREAKEVEN_MODE_MANUAL && mode_ != BREAKEVEN_MODE_AUTOMATIC)
  {
    printf("%s:: Error critico el modo del class="kw">break even %s, es invalido", __FUNCTION__, EnumToString(mode_));
    ExpertRemove();
  }
  this.symbol = symbol_;
  this.num_params = class="num">0;
  this.magic = magic_;
  this.breakeven_mode = mode_;
  this.point_value = SymbolInfoDouble(symbol_, SYMBOL_POINT);
}

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Destructor                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
CBreakEvenBase::~CBreakEvenBase()
{
  ArrayFree(PositionsBe);
}

◍ 基类里那几个被反复调用的核心方法

CBreakEvenBase 把盈亏平衡的逻辑收口在几个通用函数里,派生类只管补自己的参数解析和调整算法。GetNumParams 直接返回受保护的 num_params,标了 const 和 final——既不让改对象状态,也不许子类重写,相当于把“需要多少配置项”这个数锁死在基类。 Add 是纯虚函数,签名收的是 post_ticket、open_price、sl_price 和持仓类型,任何继承类都必须给出自己的实现,把新仓的 position_be 结构塞进 PositionsBe 数组。真正干活的是 BreakEven:进函数先判数组大小小于 1 或 pause 为 true 就直接 return,避免空跑和冲突。 遍历数组时,多头看 Ask 是否 ≥ price_to_beat,空头看 Bid 是否 ≤ 该值;命中后用 PositionSelectByTicket 按票据抓持仓,读出现有 TP,再调 CTrade 的 PositionModify 把止损推到 breakeven_price。被处理过的下标记进 indices_to_remove,循环完统一删,防止边遍历边改数组出事。 新仓注册走 OnTradeTransactionEvent,每次交易事件触发。开仓分支先用 HistoryDealSelect 选成交单,entry 为 DEAL_ENTRY_IN 时再核完全开启、模式是 BREAKEVEN_MODE_AUTOMATIC、magic 匹配(或 NOT_MAGIC_NUMBER),全过才 Add;平仓分支遇 DEAL_ENTRY_OUT 且完全平掉,会临时把 pause 设 true 再删数组项,绕开移除冲突。 子类参数差异大——简单策略要两个整数,ATR 类要周期、框架、乘数——基类用 MqlParam 数组统一接配置,各子类内部自己拆。另有一个开关函数控制 allow_extra_logs,跑实盘时想看增删记录就开着。 下面这段是基类中已落地的关键代码,注意 BreakEven 里 printf 报错用的是西语串,复制后建议换成中文便于排查。

MQL5 / C++
<span class="keyword">class="kw">virtual</span> <span class="keyword">class="kw">inline</span> <span class="keyword">class="type">int</span> GetNumParams() <span class="keyword">const</span> <span class="keyword">final</span> { <span class="keyword">class="kw">return</span> num_params; }
<span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Add(<span class="keyword">class="type">ulong</span> post_ticket, <span class="keyword">class="type">class="kw">double</span> open_price, <span class="keyword">class="type">class="kw">double</span> sl_price, <span class="macro">class="type">ENUM_POSITION_TYPE</span> position_type) = <span class="number">class="num">0</span>;
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Function to make class="kw">break even&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> CBreakEvenBase::BreakEven(<span class="keyword">class="type">void</span>)
{
&nbsp;&nbsp;<span class="keyword">if</span>(<span class="keyword">this</span>.PositionsBe.Size() &lt; <span class="number">class="num">1</span> || pause)
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span>;
&nbsp;&nbsp;<span class="functions">SymbolInfoTick</span>(<span class="keyword">this</span>.symbol, tick);
&nbsp;&nbsp;<span class="keyword">class="type">int</span> indices_to_remove[];
&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span> ; i &lt; <span class="functions">ArraySize</span>(<span class="keyword">this</span>.PositionsBe) ; i++)
&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>((<span class="keyword">this</span>.PositionsBe[i].type == <span class="macro">POSITION_TYPE_BUY</span> &amp;&amp; tick.ask &gt;= <span class="keyword">this</span>.PositionsBe[i].price_to_beat) ||
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (<span class="keyword">this</span>.PositionsBe[i].type == <span class="macro">POSITION_TYPE_SELL</span> &amp;&amp; tick.bid &lt;= <span class="keyword">this</span>.PositionsBe[i].price_to_beat))
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(!<span class="functions">PositionSelectByTicket</span>(<span class="keyword">this</span>.PositionsBe[i].ticket))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">printf</span>(<span class="class="type">class="kw">string">"%s:: Error al seleccionar el ticket %I64u"</span>,<span class="keyword">__FUNCTION__</span>,<span class="keyword">this</span>.PositionsBe[i].ticket);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ExtraFunctions::AddArrayNoVerification(indices_to_remove, i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="kw">continue</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> position_tp = <span class="functions">PositionGetDouble</span>(<span class="macro">POSITION_TP</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj_trade.PositionModify(<span class="keyword">this</span>.PositionsBe[i].ticket, <span class="keyword">this</span>.PositionsBe[i].breakeven_price, position_tp);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExtraFunctions::AddArrayNoVerification(indices_to_remove, i);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; }
&nbsp;&nbsp;ExtraFunctions::RemoveMultipleIndexes(<span class="keyword">this</span>.PositionsBe, indices_to_remove);
}
<span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">void</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OnTradeTransactionEvent(<span class="keyword">const</span> <span class="predefines">MqlTradeTransaction</span>&amp; trans) <span class="keyword">final</span>;
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| OnTradeTransactionEvent&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> CBreakEvenBase::OnTradeTransactionEvent(<span class="keyword">const</span> <span class="predefines">MqlTradeTransaction</span> &amp;trans)
{

成交回执里捞持仓的自动录入逻辑

这段处理挂在交易事务回调尾部的代码,只认 DEAL_ADD 这类成交流水;不是真实成交直接 return,避免把挂单改动、余额调整误当持仓事件。 先 HistoryDealSelect 按单据号定位,再取 DEAL_ENTRY 判断是开仓还是平仓。自动模式下比对 magic:要么 EA 自身幻数吻合,要么是 NOT_MAGIC_NUMBER 通吃,才把 ticket、开仓价、原 SL、持仓方向塞进跟踪数组。 出场分支更干脆——PositionSelectByTicket 返回 false 说明仓位已平,立刻从 PositionsBe 数组移除该 ticket,并用 pause 标志包住删除动作防止重入。外汇与贵金属杠杆高,这套逻辑若 magic 过滤写错,可能把手动单也拖进保本止损队列。 开 MT5 把这段塞进 OnTradeTransaction,开 allow_extra_logs 跑两笔正反向单,看 printf 出来的 ticket 增删是否符合预期。

MQL5 / C++
  if(trans.type != TRADE_TRANSACTION_DEAL_ADD)
    class="kw">return;
  HistoryDealSelect(trans.deal);
  ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY);
  class="type">bool pos = PositionSelectByTicket(trans.position);
  if(breakeven_mode == BREAKEVEN_MODE_AUTOMATIC)
  {
    class="type">ulong position_magic = (class="type">ulong)HistoryDealGetInteger(trans.deal, DEAL_MAGIC);
    if(entry == DEAL_ENTRY_IN && pos && (this.magic == position_magic || this.magic == NOT_MAGIC_NUMBER))
    {
      if(Add(trans.position, PositionGetDouble(POSITION_PRICE_OPEN), PositionGetDouble(POSITION_SL), (class="type">ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE)))
        if(this.allow_extra_logs)
          printf("%s:: Se a añadido el ticket %I64u del array de posiciones", __FUNCTION__, trans.position);
      class="kw">return;
    }
  }
  if(entry == DEAL_ENTRY_OUT && pos == false)
  {
    this.pause = true;
    if(ExtraFunctions::RemoveIndexFromAnArrayOfPositions(PositionsBe, trans.position))
      if(this.allow_extra_logs)
        printf("%s:: Se a eliminado el ticket %I64u del array de posiciones", __FUNCTION__, trans.position);
    this.pause = false;
  }
}
class="kw">virtual class="type">void      Set(class="type">MqlParam &params[]) = class="num">0;
class="kw">virtual class="type">void      SetExtraLogs(class="type">bool allow_extra_logs_) final { this.allow_extra_logs = allow_extra_logs_; }

「固定点数盈亏平衡类的内部实现」

在基类跑通之后,CBreakEvenSimple 用两个整型变量撑起全部逻辑:points_be 是价格必须净盈利的点数阈值,extra_points_be 是触发后止损被推到盈亏平衡位之外再加的缓冲点数。构造函数里这两个值先清零,并把 num_params 写死为 2,表明这个类只吃两个参数。 参数注入走 MqlParam 数组,这是 MQL5 里给 ChartIndicatorAdd() 传参的标准结构。Set 函数用 override 重写基类虚函数,先卡 params.Size() < 2 的边界,不满足就 printf 报错直接 return;满足才转交 SetSimple 做实际赋值。 SetSimple 允许绕开 MqlParam 直接传整数,适合在 EA 里硬编码场景。它有三条防御:points_be 必须 >0,extra_points_be 不能 <0,且后者不能大于等于前者——若越界会 printf 并调 ExpertRemove() 终止 EA。当 extra_points_be >= points_be 时,代码会把 extra 强制改 0,避免止损位反而落在触发价之内。 Add 函数重写后负责把持仓塞进 PositionsBe 数组。以 open_price 为基准算 breakeven_price(止损目标位)和 price_to_beat(触发调整价),多头、空头的公式方向相反,但都依赖上面两个点数变量。外汇与贵金属杠杆高,这类自动推损逻辑在滑点扩大时可能失效,实盘前务必在 MT5 策略测试器用历史数据验一遍边界。

MQL5 / C++
<span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;extra_points_be, points_be;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CBreakEvenSimple(<span class="keyword">class="type">class="kw">string</span> symbol_, <span class="keyword">class="type">ulong</span> magic_, ENUM_BREAKEVEN_MODE mode_)
:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CBreakEvenBase(symbol_, magic_, mode_) { <span class="keyword">this</span>.extra_points_be = <span class="number">class="num">0</span>; <span class="keyword">this</span>.points_be = <span class="number">class="num">0</span>; <span class="keyword">this</span>.num_params = <span class="number">class="num">2</span>;}
<span class="keyword">class="type">void</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Set(class="type">MqlParam &amp;<span class="keyword">params</span>[]) <span class="keyword">class="kw">override</span>;
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Set attributes of CBreakEvenSimple class with class="type">MqlParam array&nbsp;&nbsp;&nbsp;&nbsp; |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> CBreakEvenSimple::Set(class="type">MqlParam &amp;<span class="keyword">params</span>[])
 {
&nbsp;&nbsp;<span class="keyword">if</span>(<span class="keyword">params</span>.Size() &lt; <span class="number">class="num">2</span>)
&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;printf(<span class="class="type">class="kw">string">"%s:: Error setting simple class="kw">break-even, the size of the params array %I32u to less than class="num">2"</span>, __FUNCTION__, <span class="keyword">params</span>.Size());
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span>;
&nbsp;&nbsp; }
&nbsp;&nbsp;SetSimple(<span class="keyword">class="type">int</span>(<span class="keyword">params</span>[<span class="number">class="num">0</span>].integer_value), <span class="keyword">class="type">int</span>(<span class="keyword">params</span>[<span class="number">class="num">1</span>].integer_value));
 }
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Function to set member variables without class="kw">using MalParams&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> CBreakEvenSimple::SetSimple(<span class="keyword">class="type">int</span> points_be_, <span class="keyword">class="type">int</span> extra_points_be_)
 {
&nbsp;&nbsp;<span class="keyword">if</span>(points_be_ &lt;= <span class="number">class="num">0</span>)
&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">printf</span>(<span class="class="type">class="kw">string">"%s:: Error when setting the class="kw">break even value for fixed points, be points %I32d are invalid."</span>, <span class="keyword">__FUNCTION__</span>, extra_points_be_);
&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ExpertRemove</span>();
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span>;
&nbsp;&nbsp; }
&nbsp;&nbsp;<span class="keyword">if</span>(extra_points_be_ &lt; <span class="number">class="num">0</span>)
&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">printf</span>(<span class="class="type">class="kw">string">"%s:: Error when setting the class="kw">break even value for fixed points, extra points %I32d are invalid."</span>, <span class="keyword">__FUNCTION__</span>, extra_points_be_);
&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ExpertRemove</span>();
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span>;
&nbsp;&nbsp; }
&nbsp;&nbsp;<span class="keyword">if</span>(extra_points_be_ &gt;= points_be_)
&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">printf</span>(<span class="class="type">class="kw">string">"%s:: Warning: The class="kw">break even points(breakeven_price) is greater than the breakeven points(price_to_beat)\nTherefore the value of the extra breakeven points will be modified class="num">0."</span>,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">__FUNCTION__</span>);
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">this</span>.points_be = points_be_; <span class="comment">class=class="str">"cmt">//class="num">0</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">this</span>.extra_points_be = <span class="number">class="num">0</span>; <span class="comment">class=class="str">"cmt">//class="num">1</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span>;
&nbsp;&nbsp; }
&nbsp;&nbsp;<span class="keyword">this</span>.points_be = points_be_; <span class="comment">class=class="str">"cmt">//class="num">0</span>
&nbsp;&nbsp;<span class="keyword">this</span>.extra_points_be = extra_points_be_; <span class="comment">class=class="str">"cmt">//class="num">1</span>
 }

◍ 把盈亏平衡价塞进持仓数组

price_to_beat 的计算和 breakeven_price 共用一套逻辑,区别仅在于把 extra_points_be 换成 points_be。前者是触发移动止损的临界价,后者是实际保本位移的缓冲点数,两者解耦后调参更灵活。 填好 position_be 结构后,直接调 AddArrayNoVerification 丢进 PositionsBe 数组,这个函数之前在风险管理模块里跑过,不重复做边界校验,所以调用前自己确认 ticket 和价格合法。 类构造里写死 num_params = 2,意味着 Set 方法预期收到两个 MqlParam:points_be 与 extra_points_be。若外部传参少于 2,后续初始化会漏掉字段,回测时可能让保本逻辑静默失效。 下面这段 Add 实现是验证入口:多单 breakeven_price = open_price + point_value*extra_points_be,空单反之减;price_to_beat 同理用 points_be。开 MT5 把类挂到 Order Blocks EA 上,改 extra_points_be 从 0 到 10 个点,能直接看到保本线在图表上的偏移。

MQL5 / C++
class=class="str">"cmt">//+----------------------------------------------------------------------------------------------+
class=class="str">"cmt">//| Create a new structure and add it to the main array class="kw">using the &class="macro">#x27;AddToArrayBe&class="macro">#x27; function              |
class=class="str">"cmt">//+----------------------------------------------------------------------------------------------+
class="type">bool CBreakEvenSimple::Add(class="type">ulong post_ticket, class="type">class="kw">double open_price, class="type">class="kw">double sl_price, class="type">ENUM_POSITION_TYPE position_type)
{
  position_be new_pos;
  new_pos.breakeven_price =  position_type == POSITION_TYPE_BUY ? open_price + (point_value * extra_points_be) : open_price - (point_value * extra_points_be);
  new_pos.type =  position_type;
  new_pos.price_to_beat = position_type == POSITION_TYPE_BUY ? open_price + (point_value * points_be) : open_price - (point_value * points_be) ;
  new_pos.ticket = post_ticket;
  ExtraFunctions::AddArrayNoVerification(this.PositionsBe,new_pos);
  class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| class CBreakEvenSimple                                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CBreakEvenSimple : class="kw">public CBreakEvenBase
{
class="kw">private:
  class="type">int                extra_points_be, points_be;
class="kw">public:
                     CBreakEvenSimple(class="type">class="kw">string symbol_, class="type">ulong magic_, ENUM_BREAKEVEN_MODE mode_)
  :                 CBreakEvenBase(symbol_, magic_, mode_) { this.extra_points_be = class="num">0; this.points_be = class="num">0; this.num_params = class="num">2;}
  class="type">bool               Add(class="type">ulong post_ticket, class="type">class="kw">double open_price, class="type">class="kw">double sl_price, class="type">ENUM_POSITION_TYPE position_type) class="kw">override;
  class="type">void               Set(class="type">MqlParam &params[]) class="kw">override;
  class="type">void               SetSimple(class="type">int points_be_, class="type">int extra_points_be_);
};
class=class="str">"cmt">//+----------------------------------------------------------------------------------------------+
class=class="str">"cmt">//| Create a new structure and add it to the main array class="kw">using the &class="macro">#x27;AddToArrayBe&class="macro">#x27; function              |
class=class="str">"cmt">//+----------------------------------------------------------------------------------------------+
class="type">bool CBreakEvenSimple::Add(class="type">ulong post_ticket, class="type">class="kw">double open_price, class="type">class="kw">double sl_price, class="type">ENUM_POSITION_TYPE position_type)
{
  position_be new_pos;
  new_pos.breakeven_price =  position_type == POSITION_TYPE_BUY ? open_price + (point_value * extra_points_be) : open_price - (point_value * extra_points_be);
  new_pos.type =  position_type;
  new_pos.price_to_beat = position_type == POSITION_TYPE_BUY ? open_price + (point_value * points_be) : open_price - (point_value * points_be) ;
  new_pos.ticket = post_ticket;
  ExtraFunctions::AddArrayNoVerification(this.PositionsBe, new_pos);
  class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set attributes of CBreakEvenSimple class with class="type">MqlParam array                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CBreakEvenSimple::Set(class="type">MqlParam &params[])
{
  if(params.Size() < class="num">2)
   {

常见问题

在初始化时加一层参数校验,若止损点数小于等于0直接返回初始化失败,避免后续挂单计算出错。
主要是获取持仓、计算保本价、写入持仓数组这几个核心方法,建议抽成基类统一维护减少重复代码。
可以,小布能读取你当前品种的EA配置,提示止损点数是否为0或超限,并给出该品种常见安全区间参考。
重点捞成交回执的订单号、品种、方向、开仓价,按品种归并进持仓数组,避免部分成交时漏记。
需在价格触碰开仓价+固定点数时计算并覆写原止损,同时标记已保本状态防止重复触发。