在单一工具上使用不同的 EA 交易进行交易时 ORDER_MAGIC 的使用·综合运用
🧩

在单一工具上使用不同的 EA 交易进行交易时 ORDER_MAGIC 的使用·综合运用

(3/3)· 单一品种跑多个 EA 却总互相误删单?用 ulong 编码把 ID、品种、友军标记一次写进幻数

含代码示例 第 3/3 篇
把 12345、77777 这类俗套幻数直接写死,多个 EA 在同一品种上会认错彼此的挂单。一旦手动加仓或第三方脚本介入,仓位统计就可能全盘错乱。幻数不是随机整数,而是留给系统的身份协议。

把魔幻号拆成三段来读

在 MT5 的 EA 里,魔幻号(magic number)常被塞进一个 ulong 整数里,用来区分不同策略或标的。上面这套 Cmagic 类把魔幻号按「品种 / 交互码 / 数字名」三段各 3 位组装,SetMagic_result 负责写入并立刻拆出三个字段。 decodeMagic_result 是核心拆解函数:它先把魔幻号转成字符串,用 MathMod(StringLen,3) 算余数,若不是 3 的倍数就在前面补「0」或「00」,保证总长度是 3 的整数倍。随后按 category 从右往左截 3 位转回整数——category=1 取最右段,=2 取中段,=3 取左段。 GetMagic_result 用枚举直接回传对应段的数值,sGetMagic_result 则回传文本解释(如品种名、交互描述)。实盘里你改 magicnumber 为 123456789,decodeMagic_result(1) 返回 789、 (2) 返回 456、 (3) 返回 123,验证拆分逻辑是否正确。外汇与贵金属杠杆高,魔幻号管理只是风控一环,参数误用可能导致平仓混乱。

MQL5 / C++
class="type">class="kw">ulong Cmagic::SetMagic_result(class="type">class="kw">ulong magicnumber)
  {
   mag.magicnumber       =magicnumber;
   mag.expert_symbol     =decodeMagic_result(class="num">1);
   mag.code_interaction  =decodeMagic_result(class="num">2);
   mag.digital_name      =decodeMagic_result(class="num">3);
   class="kw">return(mag.magicnumber);
  }
class="type">int Cmagic::decodeMagic_result(class="type">int category)
  {
   class="type">class="kw">string string_value=(class="type">class="kw">string)mag.magicnumber;
   class="type">int rem=(class="type">int)MathMod(StringLen(string_value),class="num">3);
   if(rem!=class="num">0)
     {
      rem=class="num">3-rem;
      class="type">class="kw">string srem="class="num">0";
      if(rem==class="num">2)srem="class="num">00";
      string_value=srem+string_value;
     }
   class="type">int start_pos=StringLen(string_value)-class="num">3*category;
   class="type">class="kw">string value=StringSubstr(string_value,start_pos,class="num">3);
   class="kw">return((class="type">int)StringToInteger(value));
  }
class="type">class="kw">string string_value=(class="type">class="kw">string)mag.magicnumber;
class="type">class="kw">ulong Cmagic::GetMagic_result(Emagic enum_)
  {
   class="kw">switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : class="kw">return(mag.digital_name);      break;
      case ENUM_CODE_INTERACTION : class="kw">return(mag.code_interaction); break;
      case ENUM_EXPERT_SYMBOL    : class="kw">return(mag.expert_symbol);    break;
      class="kw">default: class="kw">return(mag.magicnumber); break;
     }
  }
class="type">class="kw">string Cmagic::sGetMagic_result(Emagic enum_)
  {
   class="kw">switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : class="kw">return(expertcode(mag.digital_name));           break;
      case ENUM_CODE_INTERACTION : class="kw">return(codeinterdescript(mag.code_interaction)); break;
      case ENUM_EXPERT_SYMBOL    : class="kw">return(symbolexpert(mag.expert_symbol));        break;
      class="kw">default: class="kw">return((class="type">class="kw">string)mag.magicnumber); break;
     }
  }

◍ EA 参数骨架与初始化落点

这段 MT5 EA 的 input 区块把交易逻辑的外壳先钉死了:止损 300 点、获利 1000 点、MA 周期 25、默认仓位 0.4 手,平滑方式走 MODE_SMA、价格取 PRICE_OPEN。把这几个数直接抄进策略测试器,就能复现它最基础的出场与均线参考框架。 幻数不是写死的常量,而是在 OnInit 里由 digital_name_=4 与 code_interaction_=1 通过 mg.SetMagic_request 动态算出来,存进全局 static ulong magic。这么做的好处是同一终端跑多个副本时,订单归属靠数字名+交互码区分,不会互删对方仓位。 OnInit 里还顺手用 TimeCurrent 把 EA 启动时刻存进 CurrentTime,后面若要做“运行 N 分钟后禁开仓”之类的时间过滤,直接拿这个基准比就行。外汇与贵金属杠杆高,这类硬编码 SL/TP 在跳空时可能滑到更远,实盘前务必在 MT5 用历史数据跑一遍点差压力。

MQL5 / C++
class="type">class="kw">string symbol,
 class="type">class="kw">datetime CurrentTime)
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| EA 代码                                                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//--- 输入参数
class="kw">input class="type">class="kw">ulong       digital_name_      =class="num">4;          class=class="str">"cmt">// EA 数字名称
class="kw">input class="type">class="kw">ulong       code_interaction_  =class="num">1;          class=class="str">"cmt">// 交互代码
class="kw">input Emagic      _enum              =class="num">0;          class=class="str">"cmt">// 幻数模式  
class="kw">input class="type">int         SL                 =class="num">300;        class=class="str">"cmt">// 止损
class="kw">input class="type">int         TP                 =class="num">1000;       class=class="str">"cmt">// 获利
class="kw">input class="type">int         MA_Period          =class="num">25;         class=class="str">"cmt">// MA 周期
class="kw">input class="type">class="kw">double      lot                =class="num">0.4;        class=class="str">"cmt">// 仓位持仓量
class="kw">input class="type">int         MA_shift           =class="num">0;          class=class="str">"cmt">// 指标偏移
class="kw">input ENUM_MA_METHOD MA_smooth      =MODE_SMA;    class=class="str">"cmt">// 平滑类型
class="kw">input ENUM_APPLIED_PRICE price       =PRICE_OPEN; class=class="str">"cmt">// 价格类型 
class=class="str">"cmt">//--- 我们将保存指标句柄
class="type">int MA_handle,type_MA,rezult;
class="kw">static class="type">class="kw">ulong magic;
class="type">class="kw">double v[class="num">2];
class="type">class="kw">datetime  CurrentTime;class=class="str">"cmt">// 保存 EA 开始时间的变量
class="type">MqlTradeResult   res;   class=class="str">"cmt">// OrderSend 响应结构的指针
class="type">MqlTick          tick;  class=class="str">"cmt">// 最后市场信息结构的指针
CProvision prov;
Cmagic mg;
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| EA 初始化函数                                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">int OnInit()
  {
   magic=mg.SetMagic_request(digital_name_,code_interaction_);
class=class="str">"cmt">// EA 印记 (幻数标识符), 此标识符声明作为全局变量
class=class="str">"cmt">// 用于 class="type">int CProvision::SendOrder(ENUM_ORDER_TYPE type,class="type">class="kw">double volume)
   CurrentTime=TimeCurrent();class=class="str">"cmt">// 保存 EA 开始时间的变量
class=class="str">"cmt">//--- 创建指标的句柄

「用 OnTick 把均线信号接进开平仓」

这段逻辑跑在 MT5 的 OnTick 里,每次报价进来先取最近两根均线缓冲值做类型判断,再决定是开仓还是反手。CopyBuffer 只拉 0~1 两根柱,够用且省开销;若返回 ≤0 直接打印错误并退出,避免脏数据下单。 已有虚拟仓位时,代码比对当前仓位类型与均线指示类型:不一致才反手,发送订单量为原持仓量加 lot。无仓位则按 type_MA 直接开一手 lot。外汇与贵金属杠杆高,这类反手逻辑在滑点扩大时可能连续触发,实盘前务必在策略测试器用 2023 年 XAUUSD M15 跑一遍。 几个落点值得你直接抄去改:mg.PositionVirtualMagic 的 _enum 与 _Symbol 要和你自己的虚拟持仓结构对齐;prov.SendOrder 返回的 rezult 为 -1 时走 GetLastError 分支,方便定位交易环境拒绝原因。

MQL5 / C++
  MA_handle=iMA(Symbol(),class="num">0,MA_Period,MA_shift,MA_smooth,price);
  class="kw">return(class="num">0);
}
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| EA 即时价格处理函数                                          |
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">void OnTick()
  {
  if(CopyBuffer(MA_handle,class="num">0,class="num">0,class="num">2,v)<=class="num">0)
     {Print("#",magic,"复制错误");class="kw">return;}
  type_MA=prov.TYPE(v); class=class="str">"cmt">// 依据 MA 指示判断类型
  mg.SetMagic_result(magic);class=class="str">"cmt">// 将信息放于结构
  if(mg.PositionVirtualMagic(_enum,_Symbol,CurrentTime))class=class="str">"cmt">// 如果有开仓 
    {
     if((class="type">int)mg.cTYPE()!=type_MA)class=class="str">"cmt">// 检查是否是平仓时机
       {
       mg.SetMagic_result(magic);class=class="str">"cmt">// 将信息放于结构
       Print("#",mg.GetMagic_result(_enum),"标识号对应的仓位持仓量 ",mg.cVOLUME(),
            " 反向仓位类型 ",(class="type">int)mg.cTYPE()," 依据 ",type_MA);
       class=class="str">"cmt">//cpvm.cVOLUME() - 虚拟仓位持仓量
       rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,mg.cVOLUME()+lot);class=class="str">"cmt">// 反向仓位
       if(rezult!=-class="num">1)Print("№",magic," 操作结果代码 ",rezult," 交易量 ",res.volume);
       else{Print("№",magic,"错误",GetLastError()); class="kw">return;}
       }
     }
  else class=class="str">"cmt">// 如果没有仓位则开仓
    {
     Print("#",magic,"标识号对应的仓位持仓量  ",mg.cVOLUME()," 仓位类型",type_MA);
     rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);class=class="str">"cmt">// 开仓 
     if(rezult!=-class="num">1)Print("#",magic," 操作结果代码 ",rezult," 交易量 ",res.volume);
     else{Print("#",magic,"错误",GetLastError()); class="kw">return;}
    }
  }

最后一句大实话

Magic 编号这套机制把多 EA 同品种协作的底层秩序交回了编写者手里,但工具归工具,能不能在实盘里避开净额对冲时的魔术混淆,全看你自己的仓位追踪写得扎不扎实。 论坛里有人提到 HistorySelectByPosition() 只能拉出构成净头寸的剩余交易,若 5 笔多 3 笔空,它只回剩 2 笔,MT4 式未结明细得自己补函数;2020 年还有人踩到虚拟仓位下同一 tick 重复开仓的坑,说明回测与实时历史同步仍有边界情况。 外汇和贵金属杠杆高、滑点狠,拿Magic做分仓前先开 MT5 用文末三个 mq5(14.33/21.03/45.74 KB)跑一遍冲突场景,比看任何结论都实在。

把幻数巡检交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到当前所有挂单的 magic 拆解,谁家的单、是否友军一目了然,你只管定协作规则。

常见问题

ulong 最大值为 18 446 744 073 709 551 615,扣除符号位仍有 16 个完整类别可用,远超千级 EA 数量的编码需求,关键在规则而非数字大小。
可以。小布盯盘在品种页对挂单 magic 做结构化解析,若你按本文的 ID+品种+友军位编码,它能直接标注来源与归属,省去手动翻日志。
EA 可能跨品种查单或受外部脚本影响,仅看 Symbol() 不够稳;幻数内嵌品种位可让任何协作 EA 仅凭 magic 就判定单子归属,不依赖运行环境。
在编码规则里预留若干位作'友军/外来'标志,检查仓位时先读该段,一致才按协作逻辑处理,否则跳过,这样复杂系统也不会互相清仓。