跨平台智能交易系统: 停止位·进阶篇
⛓️

跨平台智能交易系统: 停止位·进阶篇

(2/3)·当订单管理器被停止位逻辑撑爆,CStop 如何把复杂度拆出去

偏理论进阶 第 2/3 篇
把止损止盈的修改、监控全塞进订单管理器,是跨平台 EA 写到后期最容易踩的坑。管理器一旦既要管入场又要盯停止位,MetaTrader 4 与 5 的差异就会把代码搅成一团。本篇先理清为什么要把停止位拆给单独的类对象。

「挂单对象的止损止盈接口长这样」

在 MT5 的自定义订单管理类里,挂单(pending order)相关的属性大多用一对重载方法暴露:带参的是 setter,无参 const 的是 getter。比如 StopLoss(const double) 写止损价位,StopLoss(void) const 读回来,TakeProfit 同理。 OCO(const bool) 和 OCO(void) const 控制是否启用一键取消组(One-Cancels-Other),Pending(void) const 直接返回当前是否挂单状态。StopLossVisible / TakeProfitVisible 管画线显隐,StopLossStyle / TakeProfitStyle 接 ENUM_LINE_STYLE 改虚线实线。 SymbolName(void) 返回品种名,Volume(double) 设手数、Volume(void) const 读手数,Virtual(void) const 判断是不是虚拟单。下面这段是原始声明,直接贴进头文件就能用。 别把 getter 的 const 漏掉 漏掉 const 的话,把这个对象传进只读函数时会编译报错;MT5 编译器对 const 正确性卡得较死,改完接口记得两个重载都对齐。

MQL5 / C++
class="type">void               Name(class="kw">const class="type">class="kw">string);
class="type">class="kw">string             Name(class="type">void) class="kw">const;
class="type">void               OCO(class="kw">const class="type">bool oco);
class="type">bool               OCO(class="type">void) class="kw">const;
class="type">bool               Pending(class="type">void) class="kw">const;
class="type">void               StopLoss(class="kw">const class="type">class="kw">double);
class="type">class="kw">double             StopLoss(class="type">void) class="kw">const;
class="type">void               StopLossColor(class="kw">const class="type">class="kw">color);
class="type">bool               StopLossCustom(class="type">void);
class="type">void               StopLossName(class="kw">const class="type">class="kw">string);
class="type">class="kw">string             StopLossName(class="type">void) class="kw">const;
class="type">void               StopLossVisible(class="kw">const class="type">bool);
class="type">bool               StopLossVisible(class="type">void) class="kw">const;
class="type">void               StopLossStyle(class="kw">const ENUM_LINE_STYLE);
class="type">void               StopType(class="kw">const ENUM_STOP_TYPE);
ENUM_STOP_TYPE     StopType(class="type">void) class="kw">const;
class="type">class="kw">string             SymbolName(class="type">void);
class="type">void               TakeProfit(class="kw">const class="type">class="kw">double);
class="type">class="kw">double             TakeProfit(class="type">void) class="kw">const;
class="type">void               TakeProfitColor(class="kw">const class="type">class="kw">color);
class="type">bool               TakeProfitCustom(class="type">void);
class="type">void               TakeProfitName(class="kw">const class="type">class="kw">string);
class="type">class="kw">string             TakeProfitName(class="type">void) class="kw">const;
class="type">void               TakeProfitStyle(class="kw">const ENUM_LINE_STYLE);
class="type">void               TakeProfitVisible(class="kw">const class="type">bool);
class="type">bool               TakeProfitVisible(class="type">void) class="kw">const;
class="type">bool               Virtual(class="type">void) class="kw">const;
class="type">void               Volume(class="type">class="kw">double);
class="type">class="kw">double             Volume(class="type">void) class="kw">const;

◍ 订单停止逻辑的类接口拆解

在 MT5 的 EA 架构里,停止单(止损、止盈、挂单)的管理往往被抽象成一个独立类,通过一组虚函数暴露给派生层去实现。下面这段声明直接给出了该类对停止订单的「检查—删除—改单—创建—计价—尾随」全链路接口。 void VolumeType(const ENUM_VOLUME_TYPE); ENUM_VOLUME_TYPE VolumeType(void) const; //--- 停止订单检查 virtual bool CheckStopLoss(COrder*,COrderStop*); virtual bool CheckTakeProfit(COrder*,COrderStop*); virtual bool CheckStopOrder(ENUM_STOP_MODE,COrder*,COrderStop*)=0; virtual bool DeleteStopOrder(const ulong)=0; virtual bool DeleteMarketStop(const ulong)=0; virtual bool OrderModify(const ulong,const double); //--- 停止订单对象创建 virtual CStopLine *CreateEntryObject(const long,const string,const int,const double); virtual CStopLine *CreateStopLossObject(const long,const string,const int,const double); virtual CStopLine *CreateTakeProfitObject(const long,const string,const int,const double); //--- 停止订单价格计算 virtual bool Refresh(const string); virtual double StopLossCalculate(const string,const ENUM_ORDER_TYPE,const double); virtual double StopLossCustom(const string,const ENUM_ORDER_TYPE,const double); virtual double StopLossPrice(COrder*,COrderStop*); virtual double StopLossTicks(const ENUM_ORDER_TYPE,const double); virtual double TakeProfitCalculate(const string,const ENUM_ORDER_TYPE,const double); virtual double TakeProfitCustom(const string,const ENUM_ORDER_TYPE,const double); virtual double TakeProfitPrice(COrder*,COrderStop*); virtual double TakeProfitTicks(const ENUM_ORDER_TYPE,const double); //--- 尾随 virtual bool Add(CTrails*); virtual double CheckTrailing(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET); 逐行看:前两句是成交量类型的 setter/getter;CheckStopLoss / CheckTakeProfit 负责校验挂好的止损止盈是否合规,而 CheckStopOrder 是纯虚函数(=0),意味着具体触发逻辑必须由子类写死。DeleteStopOrder 和 DeleteMarketStop 也是纯虚,分别针对挂单和市价单上的停止位做移除。 CreateEntryObject 等三个工厂方法,入参都是 (long 魔术码, string 品种, int 周期, double 价格),返回 CStopLine*,说明停止线对象是在运行时按品种动态生成的。价格计算组里 StopLossCalculate / StopLossCustom 区分了「按算法算」和「按自定义算」,StopLossTicks 则直接吃 ENUM_ORDER_TYPE + 点数换算成绝对价——这类接口在回测 EURUSD 上,若 tick 尺寸取 0.00001,30 点止损会返回 0.00030 的偏移量。 尾随部分只有 Add(CTrails*) 和 CheckTrailing(...) 两个虚函数,后者入参带 ENUM_TRAIL_TARGET 枚举,表明尾随目标(止损线 or 止盈线)是在调用时指定的。外汇与贵金属杠杆高,这类自动停止管理若参数误设,可能瞬间放大回撤,上 MT5 跑之前务必用策略测试器单品种验证。

MQL5 / C++
class="type">void                VolumeType(class="kw">const ENUM_VOLUME_TYPE);
ENUM_VOLUME_TYPE   VolumeType(class="type">void) class="kw">const;
class=class="str">"cmt">//--- 停止订单检查
class="kw">virtual class="type">bool       CheckStopLoss(COrder*,COrderStop*);
class="kw">virtual class="type">bool       CheckTakeProfit(COrder*,COrderStop*);
class="kw">virtual class="type">bool       CheckStopOrder(ENUM_STOP_MODE,COrder*,COrderStop*)=class="num">0;
class="kw">virtual class="type">bool       DeleteStopOrder(class="kw">const class="type">class="kw">ulong)=class="num">0;
class="kw">virtual class="type">bool       DeleteMarketStop(class="kw">const class="type">class="kw">ulong)=class="num">0;
class="kw">virtual class="type">bool       OrderModify(class="kw">const class="type">class="kw">ulong,class="kw">const class="type">class="kw">double);
class=class="str">"cmt">//--- 停止订单对象创建
class="kw">virtual CStopLine *CreateEntryObject(class="kw">const class="type">long,class="kw">const class="type">class="kw">string,class="kw">const class="type">int,class="kw">const class="type">class="kw">double);
class="kw">virtual CStopLine *CreateStopLossObject(class="kw">const class="type">long,class="kw">const class="type">class="kw">string,class="kw">const class="type">int,class="kw">const class="type">class="kw">double);
class="kw">virtual CStopLine *CreateTakeProfitObject(class="kw">const class="type">long,class="kw">const class="type">class="kw">string,class="kw">const class="type">int,class="kw">const class="type">class="kw">double);
class=class="str">"cmt">//--- 停止订单价格计算   
class="kw">virtual class="type">bool       Refresh(class="kw">const class="type">class="kw">string);
class="kw">virtual class="type">class="kw">double     StopLossCalculate(class="kw">const class="type">class="kw">string,class="kw">const ENUM_ORDER_TYPE,class="kw">const class="type">class="kw">double);
class="kw">virtual class="type">class="kw">double     StopLossCustom(class="kw">const class="type">class="kw">string,class="kw">const ENUM_ORDER_TYPE,class="kw">const class="type">class="kw">double);
class="kw">virtual class="type">class="kw">double     StopLossPrice(COrder*,COrderStop*);
class="kw">virtual class="type">class="kw">double     StopLossTicks(class="kw">const ENUM_ORDER_TYPE,class="kw">const class="type">class="kw">double);
class="kw">virtual class="type">class="kw">double     TakeProfitCalculate(class="kw">const class="type">class="kw">string,class="kw">const ENUM_ORDER_TYPE,class="kw">const class="type">class="kw">double);
class="kw">virtual class="type">class="kw">double     TakeProfitCustom(class="kw">const class="type">class="kw">string,class="kw">const ENUM_ORDER_TYPE,class="kw">const class="type">class="kw">double);
class="kw">virtual class="type">class="kw">double     TakeProfitPrice(COrder*,COrderStop*);
class="kw">virtual class="type">class="kw">double     TakeProfitTicks(class="kw">const ENUM_ORDER_TYPE,class="kw">const class="type">class="kw">double);
class=class="str">"cmt">//--- 尾随  
class="kw">virtual class="type">bool       Add(CTrails*);
class="kw">virtual class="type">class="kw">double     CheckTrailing(class="kw">const class="type">class="kw">string,class="kw">const ENUM_ORDER_TYPE,class="kw">const class="type">class="kw">double,class="kw">const class="type">class="kw">double,class="kw">const ENUM_TRAIL_TARGET);

止损止盈的基类算法怎么落锤

在 MT5 的 EA 架构里,把止损止盈的计算抽进 CStopBase 这类基类,能让多品种、多订单类型的逻辑只写一遍。下面这段实现用 m_stoploss 和 m_takeprofit 存的是「点数」,真正挂单价格靠乘 symbol.Point() 换算,避免手动算错最小变动价。 StopLossCalculate 先调 Refresh(symbol) 拉最新行情,失败直接返回 0,相当于这次计算作废。买类订单(市价买、买停、买限)用 price 减去止损点数乘 Point,卖类则加回去——方向反着来,这是裸价格行为里最容易被忽略的符号坑。 TakeProfitCalculate 结构对称:买类加点数、卖类减点数。两个函数对 ORDER_TYPE_BUY_LIMIT 这类挂单同样生效,说明基类不区分市价与挂单触发态,只在入场价层面做静态测算。 虚函数表里 CreateObject、LotSizeCalculate、CloseStop 等留了 virtual 接口,其中 CloseStop 被标成 =0 纯虚,派生类必须自己写离场规则。外汇和贵金属杠杆高,这类自动测算若 Point 乘数设错,可能让止损偏出几十点,实盘前务必在策略测试器用 EURUSD 的 0.00001 点差验证一遍。

MQL5 / C++
class="kw">virtual CStopLine *CreateObject(class="kw">const class="type">long,class="kw">const class="type">class="kw">string,class="kw">const class="type">int,class="kw">const class="type">class="kw">double);
class="kw">virtual class="type">class="kw">double    LotSizeCalculate(COrder*,COrderStop*);
class="kw">virtual class="type">bool      GetClosePrice(class="kw">const class="type">class="kw">string,class="kw">const ENUM_ORDER_TYPE,class="type">class="kw">double&);
class="kw">virtual class="type">bool      CloseStop(COrder*,COrderStop*,class="kw">const class="type">class="kw">double)=class="num">0;
class="kw">virtual class="type">void      Deinit(class="type">void);
class="kw">virtual class="type">void      DeinitSymbol(class="type">void);
class="kw">virtual class="type">void      DeinitTrade(class="type">void);
class="kw">virtual class="type">void      DeinitTrails(class="type">void);
};
class="type">class="kw">double CStopBase::StopLossCalculate(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_ORDER_TYPE type,class="kw">const class="type">class="kw">double price)
  {
  if(!Refresh(symbol))
      class="kw">return class="num">0;
  if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_BUY_STOP || type==ORDER_TYPE_BUY_LIMIT)
      class="kw">return price-m_stoploss*m_symbol.Point();
  else if(type==ORDER_TYPE_SELL || type==ORDER_TYPE_SELL_STOP || type==ORDER_TYPE_SELL_LIMIT)
      class="kw">return price+m_stoploss*m_symbol.Point();
  class="kw">return class="num">0;
  }
class="type">class="kw">double CStopBase::TakeProfitCalculate(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_ORDER_TYPE type,class="kw">const class="type">class="kw">double price)
  {
  if(!Refresh(symbol))
      class="kw">return class="num">0;
  if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_BUY_STOP || type==ORDER_TYPE_BUY_LIMIT)
      class="kw">return price+m_takeprofit*m_symbol.Point();
  else if(type==ORDER_TYPE_SELL || type==ORDER_TYPE_SELL_STOP || type==ORDER_TYPE_SELL_LIMIT)
      class="kw">return price-m_takeprofit*m_symbol.Point();
  class="kw">return class="num">0;
  }

「止损容器类的基类骨架」

在订单管理器里挂止损逻辑时,CStops 本身只是个壳,真正承担容器职责的是它的基类 CStopsBase。它从 CArrayObj 派生,意味着底层直接复用 MQL5 的对象数组能力来存一堆 CStop 实例,不用自己写链表。 基类里有两个受保护成员很关键:m_active 控制该组止损是否参与实时判定,m_event_man 指向事件聚合器,便于把止损触发事件广播给盯盘面板。m_container 则反向持有上层容器对象,形成双向引用。 看下面的类声明,Type() 直接返回 CLASS_TYPE_STOPS,这是运行时区分止损容器与其它管理类对象的硬标识。Init() 要求传入品种管理器和账户信息指针,说明止损计算会依赖实时点差与保证金数据,外汇和贵金属品种因杠杆与波动差异,初始化后务必Validate()自检一遍。 Main() 返回主止损对象指针,通常对应最先触发的那一层;CreateElement() 按索引动态建元素,方便策略在加载时批量生成多个止损档位。

MQL5 / C++
class CStopsBase : class="kw">public CArrayObj
  {
class="kw">protected:
   class="type">bool                 m_active;
   CEventAggregator *m_event_man;
   CObject          *m_container;
class="kw">public:
                     CStopsBase(class="type">void);
                    ~CStopsBase(class="type">void);
   class="kw">virtual class="type">int       Type(class="type">void) class="kw">const {class="kw">return CLASS_TYPE_STOPS;}
   class=class="str">"cmt">//--- 初始化
   class="kw">virtual class="type">bool      Init(CSymbolManager*,CAccountInfo*,CEventAggregator*);
   class="kw">virtual CObject  *GetContainer(class="type">void);
   class="kw">virtual class="type">void      SetContainer(CObject*);
   class="kw">virtual class="type">bool      Validate(class="type">void) class="kw">const;
   class=class="str">"cmt">//--- 赋值和取值
   class="kw">virtual class="type">bool      Active(class="type">void) class="kw">const;
   class="kw">virtual class="type">void      Active(class="kw">const class="type">bool);
   class="kw">virtual CStop    *Main(class="type">void);
   class=class="str">"cmt">//--- 复原
   class="kw">virtual class="type">bool      CreateElement(class="kw">const class="type">int);
   };

◍ 订单与止损逻辑的实例配对关系

COrderStop 是某笔具体仓位对 CStop 规则的落地实现。一个仓位最多由 COrderStop 生成一个对应实例,但多个 COrderStop 可以共用同一个 CStop 定义。 若 EA 里挂了 3 个不同的 CStop 实例,理想状态下每笔 COrder 也应配套 3 个 COrderStop。跑过 1000 笔交易时,COrderStop 实例会膨胀到 1000 * 3 = 3000 个,而 CStop 定义始终只有 3 个,内存开销差异明显。 实际写 EA 不用手动 new 一个 COrderStop,它被 COrder 依据具体 CStop 自动实例化,开发者只管往 COrderStops 容器里塞规则。 下面这段 COrderStopBase 基类定义了单笔停止实例的核心字段与接口,开 MT5 建个类继承它就能直接调。 [CODE] 里 m_stoploss / m_takeprofit 用 CArrayDouble 存,说明支持多价位止盈止损;m_stop_type 和 m_stop_name 用来区分逻辑分支,调试时靠这两个量定位哪条规则生效。

MQL5 / C++
<span class="keyword">class</span> COrderStopBase : <span class="keyword">class="kw">public</span> CObject
&nbsp;&nbsp;{
<span class="keyword">class="kw">protected</span>:
&nbsp;&nbsp; <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_active;
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 停止位参数</span>
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_volume;
&nbsp;&nbsp; CArrayDouble&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_stoploss;
&nbsp;&nbsp; CArrayDouble&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_takeprofit;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">ulong</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_stoploss_ticket;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">ulong</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_takeprofit_ticket;
&nbsp;&nbsp; <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_stoploss_closed;
&nbsp;&nbsp; <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_takeprofit_closed;
&nbsp;&nbsp; <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_closed;
&nbsp;&nbsp; ENUM_STOP_TYPE&nbsp;&nbsp;&nbsp;&nbsp;m_stop_type;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_stop_name;
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 主订单对象</span>
&nbsp;&nbsp; COrder&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *m_order;
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 停止位对象</span>
&nbsp;&nbsp; CStop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*m_stop;
&nbsp;&nbsp; CStopLine&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*m_objentry;
&nbsp;&nbsp; CStopLine&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*m_objsl;
&nbsp;&nbsp; CStopLine&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*m_objtp;
&nbsp;&nbsp; COrderStops&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*m_order_stops;
<span class="keyword">class="kw">public</span>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COrderStopBase(<span class="keyword">class="type">void</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;~COrderStopBase(<span class="keyword">class="type">void</span>);
&nbsp;&nbsp; <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Type(<span class="keyword">class="type">void</span>) <span class="keyword">class="kw">const</span> {<span class="keyword">class="kw">return</span> CLASS_TYPE_ORDERSTOP;}
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 初始化</span>
&nbsp;&nbsp; <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">void</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Init(COrder*,CStop*,COrderStops*);
&nbsp;&nbsp; <span class="keyword">class="kw">virtual</span> COrderStops *GetContainer(<span class="keyword">class="type">void</span>);
&nbsp;&nbsp; <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">void</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetContainer(COrderStops*);
&nbsp;&nbsp; <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">void</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Show(<span class="keyword">class="type">bool</span>);
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 取值和赋值&nbsp;&nbsp;</span>
&nbsp;&nbsp; <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Active(<span class="keyword">class="type">void</span>) <span class="keyword">class="kw">const</span>;
&nbsp;&nbsp; <span class="keyword">class="type">void</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Active(<span class="keyword">class="type">bool</span> active);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EntryName(<span class="keyword">class="type">void</span>) <span class="keyword">class="kw">const</span>;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">ulong</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MainMagic(<span class="keyword">class="type">void</span>) <span class="keyword">class="kw">const</span>;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">ulong</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MainTicket(<span class="keyword">class="type">void</span>) <span class="keyword">class="kw">const</span>;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MainTicketPrice(<span class="keyword">class="type">void</span>) <span class="keyword">class="kw">const</span>;
&nbsp;&nbsp; <span class="macro">ENUM_ORDER_TYPE</span>&nbsp;&nbsp; MainTicketType(<span class="keyword">class="type">void</span>) <span class="keyword">class="kw">const</span>;
&nbsp;&nbsp; COrder&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *Order(<span class="keyword">class="type">void</span>);
&nbsp;&nbsp; <span class="keyword">class="type">void</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Order(COrder*);
&nbsp;&nbsp; CStop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*Stop(<span class="keyword">class="type">void</span>);
&nbsp;&nbsp; <span class="keyword">class="type">void</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stop(CStop*);
&nbsp;&nbsp; <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StopLoss(<span class="keyword">class="kw">const</span> <span class="keyword">class="type">class="kw">double</span>)

止损止盈与生命周期的接口骨架

下面这组声明来自一个持仓管理类的头文件尾部,集中暴露了止损、止盈以及对象清理的读写接口。外汇与贵金属杠杆高,SL/TP 的数值误差可能直接放大为爆仓风险,因此这类封装必须区分「取值」和「设值」两个重载。 StopLoss 有两态:无参版本返回 const double 表示当前止损价,带 const int 参数的版本用于按索引取历史止损,返回可修改的 double。StopLossClosed(bool) 与无参 bool 版本成对,控制并查询止损线是否已平仓关闭。 TakeProfit 的重载结构完全一致:TakeProfit(double) 返回 bool 代表设置是否成功,无参 const 版本拿当前值,带 int 的拿指定单的止盈。TakeProfitLast 取最后一次触发的止盈价,用于 trailing 回看。 尾部虚函数才是真正干活的地方。Check(double&) 是纯虚,派生类必须实现价格校验;Close、CheckTrailing、DeleteStopLoss、DeleteTakeProfit 都是 virtual bool,默认可能返回 false,需在策略类里重写才能真正删线平仓。开 MT5 把这段粘进自定义 CPosition 派生类,先重写 Check 再测 DeleteStopLoss 是否真从图表消失。

MQL5 / C++
class="type">class="kw">double              StopLoss(class="type">void) class="kw">const;
 class="type">class="kw">double              StopLoss(class="kw">const class="type">int);
 class="type">void                StopLossClosed(class="kw">const class="type">bool);
 class="type">bool                StopLossClosed(class="type">void);
 class="type">class="kw">double              StopLossLast(class="type">void) class="kw">const;
 class="type">class="kw">string              StopLossName(class="type">void) class="kw">const;
 class="type">void                StopLossTicket(class="kw">const class="type">class="kw">ulong);
 class="type">class="kw">ulong               StopLossTicket(class="type">void) class="kw">const;
 class="type">void                StopName(class="kw">const class="type">class="kw">string);
 class="type">class="kw">string              StopName(class="type">void) class="kw">const;
 class="type">bool                TakeProfit(class="kw">const class="type">class="kw">double);
 class="type">class="kw">double              TakeProfit(class="type">void) class="kw">const;
 class="type">class="kw">double              TakeProfit(class="kw">const class="type">int);
 class="type">void                TakeProfitClosed(class="kw">const class="type">bool);
 class="type">bool                TakeProfitClosed(class="type">void);
 class="type">class="kw">double              TakeProfitLast(class="type">void) class="kw">const;
 class="type">class="kw">string              TakeProfitName(class="type">void) class="kw">const;
 class="type">void                TakeProfitTicket(class="kw">const class="type">class="kw">ulong);
 class="type">class="kw">ulong               TakeProfitTicket(class="type">void) class="kw">const;
 class="type">void                Volume(class="kw">const class="type">class="kw">double);
 class="type">class="kw">double              Volume(class="type">void) class="kw">const;
 class=class="str">"cmt">//--- 检查  
 class="kw">virtual class="type">void        Check(class="type">class="kw">double&)=class="num">0;
 class="kw">virtual class="type">bool        Close(class="type">void);
 class="kw">virtual class="type">bool        CheckTrailing(class="type">void);
 class="kw">virtual class="type">bool        DeleteChartObject(class="kw">const class="type">class="kw">string);
 class="kw">virtual class="type">bool        DeleteEntry(class="type">void);
 class="kw">virtual class="type">bool        DeleteStopLines(class="type">void);
 class="kw">virtual class="type">bool        DeleteStopLoss(class="type">void);
 class="kw">virtual class="type">bool        DeleteTakeProfit(class="type">void);
让小布替你跑这套
这些跨平台停止位类的诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到订单与止损的解耦状态,你专注决策。

常见问题

订单管理器专注主要交易入场离场时,扩展成本更低;停止位涉及挂单、平仓等多场景,混入会令其过度复杂且难拆分。
仓位成功入场后先建 COrder,再生成代表止损止盈的对象实例加入进行中交易列表,本地多停止位由此留存。
可以,小布的品种页已内置对 CStop、COrderStop 等类的解耦诊断,省去手动翻代码的麻烦。
CStops 倾向管理停止位集合与批量逻辑,COrderStop 绑定单笔订单的具体停止位实例,二者分层降低耦合。
隐藏停止位或多停止位场景下,后台只给主止损,其余需在本地以对象保存才能监控和修改。