自动机编程:EA逻辑全新范式
🤖

自动机编程:EA逻辑全新范式

用状态切换替代杂乱标志,构建可靠交易系统

MQL5 编程 难度 · 进阶 2026-06-28 6 分钟阅读
#自动机编程#MQL5#状态机#EA开发#SWITCH技术

传统EA开发的逻辑困境

绝大多数MQL4/5交易者写EA时,习惯在脑海中构思逻辑后直接编码,随后才补一份粗略文档。开发者常引入大量中间布尔标志(如 bool flagA=true)来表示“条件已满足”,并在程序其他角落费力重置为false。当程序规模扩大、逻辑分支增多,标志之间耦合混乱,无人能单凭文本核对程序是否忠实于原算法。

更深层问题在于:书面算法与心中算法分离,最终代码与二者皆可能不一致。若开发者离职或生病,后来者面对数千行夹杂全局标志的代码,极难定位逻辑缺陷——这里指的并非语法错误或API误用,而是业务规则的错误实施。调试器虽能监视变量,却无法自动揭示“系统当前处于何种业务状态”。

⚠ 常见坑
用一堆布尔标志拼凑复杂仓位管理逻辑,遇到缺口、预付款不足、多单空单异常并存等极端市况,极易落入未设想分支,导致EA不动作或乱下单。

自动机编程的核心理念

1991年俄罗斯学者A.A. Shalyto提出“自动机编程”:将程序逻辑视作有限自动机,系统在任何时刻处于唯一明确状态(STATE),状态由多值控制变量(如 int STATUS)区分,而非多个二进制标志。输入动作(事件或变量)触发状态间迁移,迁移时执行输出动作并写日志。

数学上,若用n个二进制标志,组合可达2^n,其中许多组合在业务中无意义,成为“非可视化状态”(unvisualized)。EA若进入此类状态,表现等同死机:不处理缺口、负余额、错单等。自动机编程要求设计阶段枚举所有必需状态,用单一多值变量标识,从而根除未指定状态。

该方法包含三要素:有限自动机数学模型、图形化迁移图、由图到代码的同构映射(SWITCH结构)。因主要用switch实现,也称SWITCH技术。它把“等待某输入”作为状态天然属性,稳定状态持续响应,不稳定状态完成迁移即离开。

显式状态编程与反应式系统

自动机方案特别适合事件驱动的反应式系统——EA正是典型:按行情与订单事件速度与环境交互。显式状态编程将输出动作挂到迁移弧或节点(混合Moore/Mealy机),逻辑从事件处理函数抽离,形成互连自动机系统,独立于具体交易平台接口。

自动机以三位一体使用:规格设计用迁移图、实施保留在代码switch中、运行依据自动机日志审计。日志自动记录每次状态跃迁与动作,大规模复杂逻辑下,每份日志如同脚本,可复盘真实活动实体,而非静态图片。

✦ 优势
程序只需一个外层循环+一个switch,彻底免去成堆中间标志;状态数N与迁移数N^2明确,强迫覆盖所有业务情形,一致性可证明。

基础概念:状态与输入动作

STATE的核心属性是将未来与过去分隔:当前状态含一切决定反应的过往信息,无需查历史。输入动作常为向量,分事件(如OnTick收到新柱)和输入变量(如账户净值)。不带输出的自动机对输入作反应改状态;带输出自动机另经输出函数发动作。

设计复杂系统从控制对象与外部事件集出发:为每请求配输入变量,每命令配输出变量,基于控制状态构建自动机。实际中50–95%理论迁移因业务不可能而剔除,状态数亦可减60–95%,大幅降低描述难度。

MQL5代码模板与实例

最简骨架:全局 int STATUS=0;外层函数唯一循环内 switch(STATUS),各case检查迁移条件并改STATUS。如下为水三态模型(温度T驱动),展示循环即“重复当前状态”的自动机-loop。

int STATUS=0; // a global integer is by all means always a variable !!! STATUS is a multi-valued flag
//----------------------------------------------------------------------------------------------//
int start() // outer loop is a must
  {
   switch(STATUS)
     {
      case 0:  //--- start state of the program
         if(T>0 && T<100) STATUS=1;
         if(T>=100)       STATUS=2;
         if(T<=0)         STATUS=3;
         break;

      case 1:  //---  liquid
         // set of calculations or actions in this situation (repeating the 1st status -- a loop in automata-based programming) //
         // and calls of other nested automata A4, A5;
         if(T>=100 )      { STATUS=2; /* set of actions when transitioning, calls of other nested automata A2, A3;*/}
         if(T<0)          { STATUS=3; /* set of actions when transitioning, calls of other nested automata A2, A3;*/}
         // logging transitions and actions when the condition is met.
         break;

      case 2:  //--- gas
         // set of calculations or actions in this situation (repeating the 2nd status -- a loop in automata-based programming) //
         // and calls of other nested automata A4, A5;
         if(T>0 && T<100) { STATUS=1; /* set of actions when transitioning, calls of other nested automata A2, A3;*/}
         if(T<=0)         { STATUS=3; /* set of actions when transitioning, calls of other nested automata A2, A3;*/}
         // logging transitions and actions when the condition is met.
         break;

      case 3:  //--- solid
         // set of calculations or actions in this situation (repeating the 3rd status -- a loop in automata-based programming) //
         // and calls of other nested automata A4, A5;
         if(T>0 && T<100) {STATUS=1; /* set of actions when transitioning, calls of other nested automata A2, A3;*/}
         if(T>=100)       {STATUS=2; /* set of actions when transitioning, calls of other nested automata A2, A3;*/}
         // logging transitions and actions when the condition is met.
         break;
     }
   return(0);
  }

延迟继电器示例:用STATUS区分拾取/释放,输入x与阈值比较,输出Y跟随,无额外标志。

int   status=0;  // at the beginning of the program we globally assign
//------------------------------------------------------------------//
switch(status)
  {
   case 0: //  start  
      Y=x;
      if(x>xmax)  {status=1;}
      if(x<xmin)  {status=2;}
      break;

   case 1: //++++++++++++++++++++
      if(x>xmax)  Y=x;
      if(x<xmax)  Y=xmin;
      if(x<=xmin) {status=2; Y=xmin;}
      break;

   case 2: //--------------------
      if(x<xmin)  Y=x;
      if(x>xmin)  Y=xmax;
      if(x>=xmax) {status=1; Y=xmax;}
      break;
  }

音频播放器自动机:6状态(就绪、无音轨、播放、快进、倒回、暂停),按钮事件驱动迁移,输出函数控界面。理论36迁移仅15真实存在。

switch(STATUS)
  {
   case 0: //--- "Ready"
      if(Event == 3) { STATUS = 3; } //«>>» button pressed
      if(Event == 6) { STATUS = 1; } //Audio file not found 
      if(Event == 1) { STATUS = 2; } //«PLAY» button pressed
   
      z1();  // Set the indicator to the initial state
      break;

   case 1: //--- "No Track"
      z6();  // Give the «No Track» message
      break;

   case 2: //--- "Playing"
      if(Event == 4) { STATUS = 4; } //«<<» button pressed
      if(Event == 5) { STATUS = 5; } //«PAUSE»( | | ) button pressed
      if(Event == 3) { STATUS = 3; } //«>>» button pressed
      if(Event == 2) { STATUS = 0; } //«STOP» button pressed
      z2(); // Playing
      break;

   case 3: //--- "Fast-Forward"
      z3();  // Next track
      { STATUS=2; }
      break;

   case 4: //--- "Rewind"
      z4(); // Previous track
      { STATUS=2; }
      break;

   case 5: //--- "Pause"
      if(Event == 5) { STATUS = 2; } //«PAUSE» button pressed
      if(Event == 1) { STATUS = 2; } //«PLAY» button pressed
      if(Event == 2) { STATUS = 0; } //«STOP» button pressed
      if(Event == 3) { STATUS = 3; } //«>>» button pressed
      if(Event == 4) { STATUS = 4; } //«<<» button pressed
      z5(); //Pause
      break;
  }

项目文档与实施建议

Shalyto强调设计文档:简介、问题描述、类图(若OOP)、每自动机三件套(语言描述+链接图+迁移图)。链接图左列输入源/变量/事件/谓词,右列输出变量/生成事件/接收装置。计算算法单独章节,实施章节给同构模板。

对比传统“代码即文档”的错觉:源码量增长,理解难度指数升。无高阶文档时,重构史前代码极难。自动机法因图代码同构,文档天然不脱节,新成员按迁移图即可懂逻辑。

✦ 实践提示
从现有EA抽取状态:先列所有业务模式(空闲、挂单中、加仓中、平仓中、止损锁定),再画迁移条件,最后填switch case,删掉原有标志变量。

总结与对交易者的价值

自动机编程不是语法糖,而是思维转换:从“用变量记发生过什么”到“系统现在是什么态,允许去哪”。它迫使你穷举市况,规避未可视化状态,特别契合EA需常年无人值守运行的要求。虽学习曲线陡,但一旦建模完成,扩展新功能只需加case,不破旧逻辑。

原文作者畅想未来MQL6原生支持自动机,当前我们完全可用MQL5手写switch实现。外汇事件变量虽多,但合理降维后百步内可解,如同象棋引擎面对未知对手。掌握此法,是迈向严谨量化交易工程的第一步。

常见问题

是的。自动机程序核心特色是仅有一个外层循环(如OnTick或start函数中的while/直接调用),循环内唯一switch(STATUS)控制所有逻辑分支,避免散落各处的标志判断,确保状态迁移集中可查。
将多个bool标志合并为一个多值int状态变量(如STATUS=0,1,2...),每个值代表一种业务状态。迁移条件满足时只改STATUS,输出动作在case内或迁移时调用,彻底删除全局bool及重置逻辑。
在每case内条件满足改STATUS前后,调用自定义日志函数打印旧状态、事件、新状态与输出动作,例如 PrintFormat('Trans: %d->%d by Evt%d', prev, STATUS, Event); 便于后期审计复杂逻辑。
设计阶段按业务意义删减:如“暂停态”不可能直接跳“快进”可省去对应if。但模板可保留完整N^2网格注释,实际只写存在的分支,既全貌清晰又代码精简。
实时快讯
实时
加载中…
查看全部 →