一个使用命名管道在 MetaTrader 5 客户端之间进行通信的无 DLL 解决方案·综合运用
🔌

一个使用命名管道在 MetaTrader 5 客户端之间进行通信的无 DLL 解决方案·综合运用

(3/3)·从进程间管道机制到 CNamedPipes 类落地,再到流式报价压测的完整收口

实战向 第 3/3 篇
不少交易者用文件读写在多个 MT5 终端间传报价,遇到流式更新就卡顿丢点。匿名管道又绑死父子进程,跨账户终端根本连不上。本篇用的命名管道方案,不依赖 DLL 也能跑通本机或内网多客户端。

◍ 用自定义事件把多图表的报价推到监听端

这段逻辑干的事很直接:遍历终端里已打开的图表,把账户号和买卖价通过 CHARTEVENT_CUSTOM+6666 的自定义事件广播出去,让别的图表或 EA 能接住即时价做处理。 外层先拿 ChartFirst() 拿到第一张图表的句柄,用 chart<100 的计数器兜底,避免极端情况下死循环。每轮用 EventChartCustom 把 account 和 bidask 推给当前图,再用 ChartNext 切到下一张,遇到句柄返回 0 就 break,说明图表清单已经走完。 监听侧在 OnChartEvent 里判断 id==CHARTEVENT_CUSTOM+6666 且 dparam 等于管道标识时才认作报价事件,其余 id 大于基础自定义事件的归为别的自定义消息,小于等于的当作普通用户交互。 想验证就开 MT5 挂上这段,开三张以上图表,在接收端 Print 出 dparam,能看到账户号随 tick 被逐图推送;外汇和贵金属报价波动快、点差跳变频繁,多图表广播会放大终端负载,实盘前先在模拟盘压一遍。

MQL5 / C++
class="type">long currChart=ChartFirst(); class="type">int chart=class="num">0;
while(chart<class="num">100)
  {
  EventChartCustom(currChart,class="num">6666,class="num">0,(class="type">class="kw">double)account,bidask);
  currChart=ChartNext(currChart);
  if(currChart==class="num">0) break;     class=class="str">"cmt">// 到达图表清单结尾
  chart++;
  }
   if(GlobalVariableCheck("gvar0")==true || (kernel32::GetLastError()==ERROR_BROKEN_PIPE)) break;

  }
 }
 while(tickReceived==true);
 if(i>class="num">0)
  {
  Print(IntegerToString(i)+"即时价收到.");
  i=class="num">0;
  };
 if(GlobalVariableCheck("gvar0")==true || (kernel32::GetLastError()==ERROR_BROKEN_PIPE)) break;
 Sleep(class="num">100);
  }
 }
pipe.Close();
}
class="type">void OnChartEvent(const class="type">int id,
                const class="type">long &lparam,
                const class="type">class="kw">double &dparam,
                const class="type">class="kw">string &sparam)
  {
 if (dparam==(class="type">class="kw">double)incomingPipe)
  if(id>CHARTEVENT_CUSTOM)
   {
    if(id==CHARTEVENT_CUSTOM+class="num">6666)
     {
     class=class="str">"cmt">// 处理进入的即时价
     }
   } else
     {
     class=class="str">"cmt">// 处理用户时间
     }
  }

共享内存管道的实际吞吐表现

基于共享内存的命名管道在 MT5 客户端之间搬运报价几乎没有协议开销。我在两台 MT5 终端之间跑了批量压测:发送端用 WriteTick() 逐笔推报价,用 GetTickCount() 卡前后时间戳。

  • 次样本里,10 万笔报价发送与接收耗时基本对等,集中在 592–702 ms;100 万笔则在 5610–5930 ms 区间。表内第 6–10 行显示收发时间差最大不到 20 ms,说明瓶颈不在跨进程拷贝。

在一台 Windows Vista、2.0GHz T4200、3GB RAM 的老笔记本上,100 万次报价的平均吞吐约 17 万次价格变动/秒。外汇与贵金属行情高频转发属高风险操作,实盘机器若负载拉满可能拖慢 EA 决策。 下面这段发送端脚本是压测核心:Print 打点后,用 GetTickCount 取起始毫秒,循环 10 万次调用 pipe.WriteTick 把报价写进管道,再取 stop 算耗时并关闭管道。 服务端脚本则监听 ReadTick,首 tick 记录 start,断管时跳出循环并打印总接收耗时,逻辑对称。

MQL5 / C++
   Print("发送...");
   class="type">uint start = GetTickCount();
   for (class="type">int i=class="num">0;i<class="num">100000;i++)
      pipe.WriteTick(outgoing);
   class="type">uint stop = GetTickCount();
   Print("发送时间" + IntegerToString(stop-start) + " [ms]");
   pipe.Close();
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                      SpeedTestPipeServer.mq5 |
class=class="str">"cmt">//|                     Copyright class="num">2010, Investeo.pl |
class=class="str">"cmt">//|                                http:/Investeo.pl |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2010, Investeo.pl"
class="macro">#class="kw">property link      "http:/Investeo.pl"
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property script_show_inputs
class="macro">#include <CNamedPipes.mqh>
input class="type">int account=class="num">0;
class="type">bool tickReceived;
class="type">uint start,stop;
CNamedPipe pipe;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 交易程序初始函数                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
   class="type">int i=class="num">0;
   if(pipe.Create(account)==true)
      if(pipe.Connect()==true)
         Print("管道已连接");
   do
     {
      tickReceived=pipe.ReadTick();
      if(i==class="num">0) start=GetTickCount();
      if(tickReceived==class="kw">false)
        {
         if(kernel32::GetLastError()==ERROR_BROKEN_PIPE)
           {
            Print("客户端从管道断开 "+pipe.GetPipeName());
            pipe.Disconnect();
            break;
           }
        }
      else i++;
     }
   while(tickReceived==true);
   stop=GetTickCount();
   if(i>class="num">0)
     {
      Print(IntegerToString(i)+" ticks received.");
      i=class="num">0;
     };

   pipe.Close();
   Print("服务器: 接收时间 "+IntegerToString(stop-start)+" [ms]");
  }
class=class="str">"cmt">//+------------------------------------------------------------------

「把这条线请下神坛」

命名管道在 MT5 客户端之间搬实时报价是可行的,CNamedPipes 类附带了 mqh 头文件与若干演示源文件,其中 cnamedpipes.mqh 体积 21.65 KB,sendtickpipeindicator.mq5 仅 3.71 KB,足以在两张图表间透传 tick。 但社区反馈很直白:2023 年 6 月起的多个版本在附加指标时抛出「访问违规」,有用户自行修补了类文件并补了 WriteDouble 方法才勉强跑通。跨终端通信不是银弹,实盘里多账户对冲若依赖这套老管道,要先在模拟环境压一遍。 外汇与贵金属本身高杠杆、高风险,任何进程间传价方案都只是工具链一环,别神化它的稳定性。 下面这段是社区补的写双精度方法,直接看 kernel32 的 WriteFile 怎么被包了一层: #import "kernel32.dll" ... 引入 WriteFile(ulong,double&,int,int&,ulong) 系统调用 ... #import bool CNamedPipe::WriteDouble(double data) { int data_size = sizeof(data);

if(!datadata_size<1) return(false);

int written;

if(!WriteFile(hPipe,data,data_size,written,NULL)written!=data_size)

return(false); return(true); } 逻辑就一句:尺寸不对或写字节数不匹配就失败,匹配才返回真。开 MT5 把这段塞进你自己的管道类,先测本地回环再谈跨账户。

MQL5 / C++
class="macro">#class="kw">import "kernel32.dll"
...
class="type">int WriteFile(class="type">class="kw">ulong fileHandle,class="type">class="kw">double &var,class="type">int bytes,class="type">int &numOfBytes,class="type">class="kw">ulong overlapped);
...
class="macro">#class="kw">import
...
class="type">bool CNamedPipe::WriteDouble(class="type">class="kw">double data)
  {
   class="type">int data_size = class="kw">sizeof(data);
class=class="str">"cmt">//--- 检查参数
   if(!data || data_size<class="num">1) class="kw">return(class="kw">false);
class=class="str">"cmt">//--- 发送数据
   class="type">int written;
   if(!WriteFile(hPipe,data,data_size,written,NULL) || written!=data_size)
      class="kw">return(class="kw">false);
class=class="str">"cmt">//--- ok
   class="kw">return(true);
  }
把多端管道状态交给小布
这些命名管道的连接与吞吐诊断,小布盯盘的 AIGC 已内置,打开对应品种页即可看到多终端链路是否健康,你只管判断报价异动。

常见问题

匿名管道不能走网络且要求两进程有父子关系,命名管道无此限制,可用 \\.\pipe\mt[账号] 这类已知名跨终端连。
Windows 下通过 kernel32.dll 的 CreateNamedPipe() 建命名管道实例,客户端用对应连接函数按名接入。
可在服务端持续推流、客户端计接收帧率与延迟,本篇第 6 节给出实测方法与系统吞吐结论。
可以,小布盯盘内置了多终端链路诊断,能呈现命名管道连接状态和报价流延迟,省去手动写日志排查。
管道仅本机或内网有效,跨公网需另做安全隧道;外汇贵金属杠杆高、滑点快,管道延迟可能倾向放大成交偏差。