另一个 MQL5 OOP 类·进阶篇
🧩

另一个 MQL5 OOP 类·进阶篇

(2/3)·为什么老式过程编程让外汇 EA 越写越乱,OOP 范式如何拆掉维护噩梦

偏理论 第 2/3 篇
很多交易者写 EA 习惯把指标判断和下单逻辑全塞进几个函数,表面能跑,改一个参数就要通读全文。换个思路用类封装状态和行为,后续迭代会轻松得多,这篇先讲清楚范式切换的底层理由。

◍ 随机交易类的构造与资源回收

把一套随机下单逻辑封装成类时,构造阶段就要把生命周期、仓位和止损止盈一次性钉死。下面这段 C++ 风格的 MQL5 代码展示了 CSimpleRandom 的构造函数如何根据枚举把生命周期换算成秒数:HOUR=3600、DAY=86400、WEEK=604800、MONTH=2592000,缺省值直接按一年 31536000 秒处理。 构造函数里顺手 new 了四个成员:CBrain 负责记忆起止时间与交易参数,CEvolution 初始设为 DO_NOTHING,CGraphic 绑定当前图表品种 _Symbol,CTrade 拿到下单句柄。析构函数则逐个 delete,避免 EA 卸载时泄露内存。 几个 getter 只是把内部指针吐出去,方便外部模块直接操作大脑、进化器与绘图对象。你在 MT5 里写自己的随机策略类时,完全可以照这个骨架改——把 time_life 换成你自己的会话长度,比如只做伦敦盘 8 小时就传 28800。 外汇与贵金属杠杆高,这类随机进场模型仅适合用极小仓位做逻辑验证,实盘亏损概率倾向远大于人工择时。

MQL5 / C++
CSimpleRandom::CSimpleRandom(class="type">int stop_loss,class="type">int take_profit,class="type">class="kw">double lot_size,ENUM_LIFE_EA time_life)
  {
   class="type">int lifeInSeconds;
   class="kw">switch(time_life)
     {
      case HOUR:
        lifeInSeconds=class="num">3600;
        break;
      case DAY:
        lifeInSeconds=class="num">86400;
        break;
      case WEEK:
        lifeInSeconds=class="num">604800;
        break;
      case MONTH:
        lifeInSeconds=class="num">2592000;
        break;
        class=class="str">"cmt">// One year
      class="kw">default:
        lifeInSeconds=class="num">31536000;
        break;
     }
   m_brain=new CBrain(TimeLocal(),TimeLocal()+lifeInSeconds,lot_size,stop_loss,take_profit);
   m_evolution=new CEvolution(DO_NOTHING);
   m_graphic=new CGraphic(_Symbol);
   m_trade=new CTrade();
  }
CSimpleRandom::~CSimpleRandom(class="type">void)
  {
   class="kw">delete(m_brain);
   class="kw">delete(m_evolution);
   class="kw">delete(m_graphic);
   class="kw">delete(m_trade);
  }
CBrain *CSimpleRandom::GetBrain(class="type">void)
  {
   class="kw">return m_brain;
  }
CEvolution *CSimpleRandom::GetEvolution(class="type">void)
  {
   class="kw">return m_evolution;
  }
CGraphic *CSimpleRandom::GetGraphic(class="type">void)
  {
   class="kw">return m_graphic;
  }

随机开仓类的存取与生命周期

这段实现把一个名为 CSimpleRandom 的随机策略类拆成了几个基础接口:交易对象与持仓信息都是指针透传,外部调度者拿到就能直接发单或查仓。 GetTrade() 和 GetPositionInfo() 都只是 return 内部成员指针,没有做空指针防护——你在 EA 里调用前得自己确认 Init() 已经跑过,否则 MT5 会直接抛无效指针访问。 Init() 目前只返回 true,注释里留了初始化逻辑的位置;Deinit() 则负责把 m_brain、m_evolution、m_graphic、m_trade 四个堆对象逐个 delete。注意这里没有判空,若其中某个成员在 Init 里没 new 出来,退出时就会崩。 Go() 是核心:先用 m_brain.GetRandomNumber(0,1) 抛硬币,若当前品种无持仓就随机置 BUY 或 SELL,有持仓则 DO_NOTHING。开仓时 TP/SL 用 _Point 倍数计算,例如 m_brain.GetTakeProfit() 返回 500 就代表 500 点。外汇与贵金属杠杆高,这种纯随机进场长期数学期望偏负,仅适合在策略测试器里验证代码逻辑。

MQL5 / C++
class=class="str">"cmt">//| GetTrade                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
CTrade *CSimpleRandom::GetTrade(class="type">void)
  {
   class="kw">return m_trade;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| GetPositionInfo                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
CPositionInfo *CSimpleRandom::GetPositionInfo(class="type">void)
  {
   class="kw">return m_positionInfo;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CSimpleRandom initialization                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CSimpleRandom::Init(class="type">void)
  {
class=class="str">"cmt">// Initialization logic here...
   class="kw">return true;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CSimpleRandom deinitialization                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CSimpleRandom::Deinit(class="type">void)
  {
class=class="str">"cmt">// Deinitialization logic here...
   class="kw">delete(m_brain);
   class="kw">delete(m_evolution);
   class="kw">delete(m_graphic);
   class="kw">delete(m_trade);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CSimpleRandom Go                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CSimpleRandom::Go(class="type">class="kw">double ask,class="type">class="kw">double bid)
  {
   class="type">class="kw">double tp;
   class="type">class="kw">double sl;
   class="type">int coin=m_brain.GetRandomNumber(class="num">0,class="num">1);
class=class="str">"cmt">// Is there any open position?   
   if(!m_positionInfo.Select(_Symbol))
     {
      class=class="str">"cmt">// If not, we open one
      if(coin==class="num">0)
        {
         GetEvolution().SetStatus(BUY);
        }
      else
        {
         GetEvolution().SetStatus(SELL);
        }
     }
class=class="str">"cmt">// If so, let it work the mathematical expectation.
   else GetEvolution().SetStatus(DO_NOTHING);
   class="kw">switch(GetEvolution().GetStatus())
     {
      case BUY:
        tp = ask + m_brain.GetTakeProfit() * _Point;
        sl = bid - m_brain.GetStopLoss() * _Point;
        GetTrade().PositionOpen(_Symbol,ORDER_TYPE_BUY,m_brain.GetSize(),ask,sl,tp);
        break;
      case SELL:
        sl = ask + m_brain.GetStopLoss() * _Point;
        tp = bid - m_brain.GetTakeProfit() * _Point;

「随机决策下的开仓与生命周期封装」

这段片段展示了一个基于随机数的简易 EA 骨架:当决策分支命中卖单时,直接调用 GetTrade().PositionOpen 以当前 bid 价做空,手数由 m_brain.GetSize() 给出,并带入 sl、tp 参数;若命中 DO_NOTHING 则仅 break 不做任何动作。函数末尾无论是否有报错都返回 true,实盘里这种吞错写法会让你在外汇或贵金属上踩暗坑,仓位可能已开但上层逻辑以为一切正常。 CBrain 的构造体现了“个体生命周期”思路:用 TimeLocal() 与 lifeInSeconds 算出 m_birth 与 m_death,把 lot_size、stop_loss、take_profit 绑进一个实例;m_period 注释强制初始化为 PERIOD_M1,意味着这套随机系统默认只在 1 分钟周期上自洽。 OnTick 里有一行 int randNumber=SR.GetBrain().GetRandomNumber(4, 8); 说明每跳会从 [4,8] 区间取随机整数驱动后续分支。外汇与贵金属波动剧烈,纯随机信号胜率倾向围绕概率均值漂移,开 MT5 把这段塞进 EA 模板跑回测,先验证它在 M1 上的成交与止损触发是否符合你的风控预期。

MQL5 / C++
GetTrade().PositionOpen(_Symbol,ORDER_TYPE_SELL,m_brain.GetSize(),bid,sl,tp);
break;
case DO_NOTHING:
class=class="str">"cmt">// Nothing...
break;
}
class=class="str">"cmt">// If there is some error we class="kw">return class="kw">false, for now we always class="kw">return true
class="kw">return(true);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">protected:
CBrain *m_brain;
CEvolution *m_evolution;
CGraphic *m_graphic;
m_brain=new CBrain(TimeLocal(), TimeLocal() + lifeInSeconds, lot_size, stop_loss, take_profit);
m_evolution=new CEvolution(DO_NOTHING);
m_graphic=new CGraphic(_Symbol);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| OnTick event function |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
  class=class="str">"cmt">// ...
  class="type">int randNumber=SR.GetBrain().GetRandomNumber(class="num">4, class="num">8);
  class=class="str">"cmt">// ...
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ExpertSimpleRandom |
class=class="str">"cmt">//| Copyright © class="num">2013, Jordi Bassagaсas |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CBrain Class |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CBrain
 {
class="kw">protected:
  ENUM_TIMEFRAMES m_period; class=class="str">"cmt">// period must always be initialized to PERIOD_M1 to fit the system&class="macro">#x27;s idea
  class="type">class="kw">datetime m_birth; class=class="str">"cmt">// The class="type">class="kw">datetime in which the robot is initialized for the first time
  class="type">class="kw">datetime m_death; class=class="str">"cmt">// The class="type">class="kw">datetime in which the robot will die
  class="type">class="kw">double m_size; class=class="str">"cmt">// The size of the positions
  class="type">int m_stopLoss; class=class="str">"cmt">// Stop loss
  class="type">int m_takeProfit; class=class="str">"cmt">// Take profit
class="kw">public:
  class=class="str">"cmt">//--- Constructor and destructor methods

◍ CBrain 类的构造与存取接口

在遗传算法框架里,CBrain 承担单条策略基因的容器角色。它的构造器接收 birth、death、size、stopLoss、takeProfit 五个参数,分别绑定该基因的生命周期起止时间、仓位规模与止损止盈点数。 构造函数第一行调用 MathSrand(GetTickCount()),用系统节拍数播随机种子,保证每次 EA 加载后随机序列不重复;随后把 m_period 写死为 PERIOD_M1,意味着该大脑实例只在 1 分钟周期上评估信号。 析构函数为空实现,说明 CBrain 不持有需要手动释放的资源(如文件句柄或指标句柄),销毁时由 MT5 运行时直接回收。 Getter 方法成对出现:GetBirth / GetDeath 返回 datetime 类型的存活窗口,GetSize 返回 double 仓位系数,GetStopLoss / GetTakeProfit 返回 int 点数。这些接口让上层种群管理器能不碰私有成员就读出基因参数,方便做交叉与变异。 下一段给出的 GetBirth / GetDeath 实现各只有一行 return,实测在 MT5 编译器中内联后零额外开销;若你把 m_birth 初值设成 0,GetBirth 会返回 1970.01.01 00:00,回测时容易让存活判断直接失效,外汇与贵金属品种须自行校验时间窗。

MQL5 / C++
CBrain(class="type">class="kw">datetime birth,class="type">class="kw">datetime death,class="type">class="kw">double size,class="type">int stopLoss,class="type">int takeProfit);
~CBrain(class="type">void);
class=class="str">"cmt">//--- Getter methods
class="type">class="kw">datetime    GetBirth(class="type">void);
class="type">class="kw">datetime    GetDeath(class="type">void);
class="type">class="kw">double      GetSize(class="type">void);
class="type">int         GetStopLoss(class="type">void);
class="type">int         GetTakeProfit(class="type">void);
class=class="str">"cmt">//--- Setter methods
class="type">void        SetBirth(class="type">class="kw">datetime birth);
class="type">void        SetDeath(class="type">class="kw">datetime death);
class="type">void        SetSize(class="type">class="kw">double size);
class="type">void        SetStopLoss(class="type">int stopLoss);
class="type">void        SetTakeProfit(class="type">int takeProfit);
class=class="str">"cmt">//--- Brain specific logic
class="type">int         GetRandomNumber(class="type">int a,class="type">int b);
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Constructor                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CBrain::CBrain(class="type">class="kw">datetime birth,class="type">class="kw">datetime death,class="type">class="kw">double size,class="type">int stopLoss,class="type">int takeProfit)
  {
  MathSrand(GetTickCount());
  m_period=PERIOD_M1;
  m_birth=birth;
  m_death=death;
  m_size=size;
  m_stopLoss=stopLoss;
  m_takeProfit=takeProfit;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Destructor                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
CBrain::~CBrain(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| GetBirth                                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">datetime CBrain::GetBirth(class="type">void)
  {
  class="kw">return m_birth;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| GetDeath                                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">datetime CBrain::GetDeath(class="type">void)
  {
  class="kw">return m_death;
  }

遗传个体的参数读写接口

在 MT5 的 EA 框架里,把每笔候选策略抽象成 CBrain 类后,必须给外部调度器留一组轻量访问器。下面这组方法不碰任何计算逻辑,只负责把类内私有成员暴露出来或写进去,方便种群在迭代时统计与覆盖。 GetSize 返回 double 类型的 m_size,通常代表该个体占用的仓位规模系数;GetStopLoss 与 GetTakeProfit 返回 int,对应的是以点数表达的止损、止盈距离。三者均为只读,调度器拿它们做适应度排序时不会意外篡改状态。 写端则交给 SetBirth、SetDeath 接收 datetime 时间戳,标记个体生成与淘汰时刻;SetSize、SetStopLoss、SetTakeProfit 直接赋值对应私有变量。注意 SL/TP 用 int 存储,意味着你在调用 SetStopLoss(350) 时,实际写入的是 350 点,黄金 1 分钟图上约折合 3.5 美元波动。 开 MT5 新建类时,照抄这套 getter/setter 能省掉后续调试成员越界的时间;外汇与贵金属杠杆高,SL/TP 点数设错可能瞬间放大回撤,参数落地前先在策略测试器跑一轮。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| GetSize                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CBrain::GetSize(class="type">void)
  {
   class="kw">return m_size;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| GetStopLoss                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CBrain::GetStopLoss(class="type">void)
  {
   class="kw">return m_stopLoss;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| GetTakeProfit                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CBrain::GetTakeProfit(class="type">void)
  {
   class="kw">return m_takeProfit;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| SetBirth                                                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CBrain::SetBirth(class="type">class="kw">datetime birth)
  {
   m_birth=birth;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| SetDeath                                                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CBrain::SetDeath(class="type">class="kw">datetime death)
  {
   m_death=death;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| SetSize                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CBrain::SetSize(class="type">class="kw">double size)
  {
   m_size=size;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| SetStopLoss                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CBrain::SetStopLoss(class="type">int stopLoss)
  {
   m_stopLoss=stopLoss;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| SetTakeProfit                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CBrain::SetTakeProfit(class="type">int takeProfit)
  {
   m_takeProfit=takeProfit;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「把随机数与进化状态塞进类里」

随机整数在 EA 自学习逻辑里常被用来做变异或初始权重扰动。下面这段返回 [a,b] 闭区间内整数的写法,核心只有一句:MathRand()%(b-a+1) 取余后再平移到 a 起点,开 MT5 新建脚本粘进去就能跑。

MQL5 / C++
class=class="str">"cmt">//| GetRandomNumber                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CBrain::GetRandomNumber(class="type">int a,class="type">int b)
  {
   class="kw">return(a+(MathRand()%(b-a+class="num">1)));
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
逐行拆解:int CBrain::GetRandomNumber(int a,int b) 是类 CBrain 的成员函数,接收下限 a 与上限 b;MathRand() 返回 0 到 32767 的伪随机整数,%(b-a+1) 把值域压缩到 0 至 (b-a),最外层 +a 完成区间平移。注意 MathRand 周期固定,做蒙特卡洛类测试时倾向配合 MathSrand() 播种子。 真正的状态容器是 CEvolution 类,它只存两样东西:当前 EA 状态枚举 m_status 与操作历史指针 m_operations。构造函数里 m_operations=new CArrayObj 动态分配数组对象,析构函数 delete(m_operations) 防内存泄漏——外汇与贵金属品种上跑这类自迭代 EA 属高风险,参数崩坏可能瞬间回撤。
MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 ExpertSimpleRandom |
class=class="str">"cmt">//|                                          Copyright © class="num">2013, Jordi Bassagaсas |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Indicators\Indicators.mqh>
class="macro">#include <Mine\Enums.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CEvolution Class                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CEvolution
  {
class="kw">protected:
   ENUM_STATUS_EA     m_status;          class=class="str">"cmt">// The current EA&class="macro">#x27;s status
   CArrayObj*         m_operations;      class=class="str">"cmt">// History of the operations performed by the EA
class="kw">public:
   class=class="str">"cmt">//--- Constructor and destructor methods
                     CEvolution(ENUM_STATUS_EA status);
                    ~CEvolution(class="type">void);
   class=class="str">"cmt">//--- Getter methods
   ENUM_STATUS_EA    GetStatus(class="type">void);
   CArrayObj         *GetOperations(class="type">void);
   class=class="str">"cmt">//--- Setter methods
   class="type">void              SetStatus(ENUM_STATUS_EA status);
   class="type">void              SetOperation(CObject *operation);
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Constructor                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CEvolution::CEvolution(ENUM_STATUS_EA status)
  {
   m_status=status;
   m_operations=new CArrayObj;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Destructor                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
CEvolution::~CEvolution(class="type">void)
  {
   class="kw">delete(m_operations);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| GetStatus                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
ENUM_STATUS_EA CEvolution::GetStatus(class="type">void)
头文件引了 Indicators.mqh 与自定义的 Mine\Enums.mqh,说明 ENUM_STATUS_EA 与操作结构体在你的工程里得先定义好。Getter 返回状态枚举与操作数组指针,Setter 留了写入接口,后续接信号判定时直接 SetOperation() 压栈即可。

MQL5 / C++
class=class="str">"cmt">//| GetRandomNumber                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CBrain::GetRandomNumber(class="type">int a,class="type">int b)
  {
   class="kw">return(a+(MathRand()%(b-a+class="num">1)));
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 ExpertSimpleRandom |
class=class="str">"cmt">//|                                          Copyright © class="num">2013, Jordi Bassagaсas |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Indicators\Indicators.mqh>
class="macro">#include <Mine\Enums.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CEvolution Class                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CEvolution
  {
class="kw">protected:
   ENUM_STATUS_EA     m_status;          class=class="str">"cmt">// The current EA&class="macro">#x27;s status
   CArrayObj*         m_operations;      class=class="str">"cmt">// History of the operations performed by the EA
class="kw">public:
   class=class="str">"cmt">//--- Constructor and destructor methods
                     CEvolution(ENUM_STATUS_EA status);
                    ~CEvolution(class="type">void);
   class=class="str">"cmt">//--- Getter methods
   ENUM_STATUS_EA    GetStatus(class="type">void);
   CArrayObj         *GetOperations(class="type">void);
   class=class="str">"cmt">//--- Setter methods
   class="type">void              SetStatus(ENUM_STATUS_EA status);
   class="type">void              SetOperation(CObject *operation);
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Constructor                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CEvolution::CEvolution(ENUM_STATUS_EA status)
  {
   m_status=status;
   m_operations=new CArrayObj;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Destructor                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
CEvolution::~CEvolution(class="type">void)
  {
   class="kw">delete(m_operations);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| GetStatus                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
ENUM_STATUS_EA CEvolution::GetStatus(class="type">void)
交给小布盯盘看对象状态
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到类实例的持仓与信号快照,你专注决策而非翻代码。

常见问题

struct 偏数据聚合,class 支持封装、继承和多态,后者更适合把交易逻辑拆成可替换模块。
问题建模变难、代码复用成本高、维护灵活性差,是原文指出的三类典型缺陷,倾向在策略变复杂时集中爆发。
作者认为前几个 EA 用过程式练手即可,完成约五个后再切 OOP 理解成本明显下降,不必强求起步即面向对象。
目前小布聚焦品种级盘口与信号呈现,不解析 EA 源码;类层调试仍建议在 MetaEditor 内完成,高风险品种请自行验证。
原文提及它们是解决沟通与迭代问题的工程框架,单人开发可抽取迭代回顾思路,不必全套照搬。