在单一工具上使用不同的 EA 交易进行交易时 ORDER_MAGIC 的使用·综合运用
(3/3)· 单一品种跑多个 EA 却总互相误删单?用 ulong 编码把 ID、品种、友军标记一次写进幻数
把魔幻号拆成三段来读
在 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,验证拆分逻辑是否正确。外汇与贵金属杠杆高,魔幻号管理只是风控一环,参数误用可能导致平仓混乱。
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 用历史数据跑一遍点差压力。
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 分支,方便定位交易环境拒绝原因。
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)跑一遍冲突场景,比看任何结论都实在。