MQL5 中创建订单号指标(基础篇)
📘

MQL5 中创建订单号指标(基础篇)

第 1/2 篇

◍ 正文

MQL5 中创建订单号指标 MetaTrader 5 — 指标 | 27 九月 2013, 08:43

  • 654

23 Denis Zyatkevich

「用订单号图表补全价格拼图」

做价格行为分析时,单看 K 线往往缺一块:你不知道某一根棒线背后对应哪一笔真实成交。把订单号直接挂到价格序列上,能把零散的流动信息拼成一张可回溯的图。 MQL5 里可以拆出两个独立指标来做这件事:一个负责把订单号铺成价格图表,另一个专门画含某个指定订单号的单根烛形,叫“订单号烛形图”。 两个指标都会把收到的价格值写进文件。这样 MT5 客户端重启后指标数据不丢,别的程序也能直接读这份落盘数据继续用。外汇与贵金属杠杆高、滑点跳空频繁,这类明细图只作辅助研判,不构成方向结论。

把逐笔报价落盘成独立窗口指标

想在 MT5 图表下方单独开一个窗口,把经纪商推过来的 Bid / Ask 逐笔存成文件并画线,核心是指标属性声明与两个缓冲区的配合。下面这段预处理指令直接决定了它跑在副图、画两条实线,标签分别为 Bid(红)与 Ask(蓝): [CODE] #property indicator_separate_window #property indicator_plots 2 #property indicator_buffers 2 #property indicator_type1 DRAW_LINE #property indicator_color1 Red #property indicator_style1 STYLE_SOLID #property indicator_label1 "Bid" #property indicator_type2 DRAW_LINE #property indicator_color2 Blue #property indicator_style2 STYLE_SOLID #property indicator_label2 "Ask" [/CODE] 用户可调的三个 input 很实用:BidLineEnable / AskLineEnable 控制单线显隐,path_prefix 能给文件加前缀或子目录。比如设成 "MyBroker/test_",EURUSD 的落盘路径就变成 MQL5\Files\MyBroker\test_EURUSD.txt,多图表同品种挂多个实例也不会写串。 OnInit 里只做一件事:把 BidBuffer[]、AskBuffer[] 绑成 INDICATOR_DATA 缓冲,并把 PLOT_EMPTY_VALUE 设成 0,保证缓冲值为 0 的点不往图上画。注意动态数组长度此时还没定,得等到 OnCalculate 被调用、rates_total 已知之后才分配,所以千万别在 OnInit 里读历史文件——那是新手最容易踩的坑。

文件读写用 FileOpen 带 FILE_READFILE_WRITEFILE_ANSIFILE_SHARE_READ:ANSI 省编码麻烦,SHARE_READ 允许别的程序同时读。首次启动或周期切换时,代码把可读报价上限掐在 rates_total/2,用 FileSeek 从末尾倒推,避免几千行旧数据把缓冲撑爆。外汇与贵金属报价高频跳动,实盘跑这套落盘逻辑前,建议先开模拟盘验证文件增长速率。
MQL5 / C++
class="macro">#class="kw">property indicator_separate_window
class="macro">#class="kw">property indicator_plots class="num">2
class="macro">#class="kw">property indicator_buffers class="num">2
class="macro">#class="kw">property indicator_type1 DRAW_LINE
class="macro">#class="kw">property indicator_color1 Red
class="macro">#class="kw">property indicator_style1 STYLE_SOLID
class="macro">#class="kw">property indicator_label1 "Bid"
class="macro">#class="kw">property indicator_type2 DRAW_LINE
class="macro">#class="kw">property indicator_color2 Blue
class="macro">#class="kw">property indicator_style2 STYLE_SOLID
class="macro">#class="kw">property indicator_label2 "Ask"
class="kw">input class="type">bool BidLineEnable=true;
class="kw">input class="type">bool AskLineEnable=true;
class="kw">input class="type">class="kw">string path_prefix="";
class="type">int ticks_stored;
class="type">class="kw">double BidBuffer[],AskBuffer[];
class="type">void OnInit()
  {
   SetIndexBuffer(class="num">0,BidBuffer,INDICATOR_DATA);
   SetIndexBuffer(class="num">1,AskBuffer,INDICATOR_DATA);
   PlotIndexSetDouble(class="num">0,PLOT_EMPTY_VALUE,class="num">0);
   PlotIndexSetDouble(class="num">1,PLOT_EMPTY_VALUE,class="num">0);
  }

◍ 从外部文件回填历史报价的初始化逻辑

指标首次加载时,OnCalculate 的 prev_calculated 为 0,这时需要从本地 txt 文件把历史 Bid/Ask 报价读回内存,而不是等实时 tick 慢慢攒。文件命名规则是 path_prefix 拼接当前品种名再加 .txt,用 FileOpen 以 FILE_READFILE_WRITEFILE_ANSIFILE_SHARE_READ 方式打开,既读也写、且允许其他程序并发读。

若文件体积偏大——超过单行长度 line_string_len 乘以 rates_total/2 字节,就直接用 FileSeek 从末尾倒退 half 窗口,只取最近约 rates_total/2 行,避免把几年前的废报价全灌进数组;文件小则指针归零从头读。 读循环里每行先判长度是否大于 6,才往下解析。卖价起点用两次 StringFind 找空格偏移定位:第一次找到首个空格,第二次从它之后再找一个空格,加 1 即 Bid 字段起始;买价位置同理往后推。外汇与贵金属报价跳动频繁,这种回填方式能让你在 MT5 重开图表时立刻看到历史价差分布,但历史文件若被其他脚本改写,可能读出异常值,属高风险操作,验证前先备份原文件。

MQL5 / C++
class="type">int OnCalculate(const class="type">int rates_total,
                const class="type">int prev_calculated,
                const class="type">class="kw">datetime &time[],
                const class="type">class="kw">double &open[],
                const class="type">class="kw">double &high[],
                const class="type">class="kw">double &low[],
                const class="type">class="kw">double &close[],
                const class="type">long &tick_volume[],
                const class="type">long &volume[],
                const class="type">int &spread[])
class=class="str">"cmt">// file_handle变量定义文件的句柄
class=class="str">"cmt">// BidPosition和AskPosition是字符串中头寸的卖出和买价;
class=class="str">"cmt">//line_string_len是从文件中读取的字符串的长度,i是循环计数器;
class="type">int file_handle,BidPosition,AskPosition,line_string_len,i;
class=class="str">"cmt">// last_price_bid是最新的卖出报价
class="type">class="kw">double last_price_bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
class=class="str">"cmt">// last_price_ask是最新的买入报价
class="type">class="kw">double last_price_ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
class=class="str">"cmt">// filename是文件名,the file_buffer是一个字符串,
class=class="str">"cmt">// 用来读取和写入字符串数据的缓存
class="type">class="kw">string filename,file_buffer;
class=class="str">"cmt">// 由path_prefix变量和交易品种
class=class="str">"cmt">// 以及“.Txt”构成的文件名
StringConcatenate(filename,path_prefix,Symbol(),".txt");
class=class="str">"cmt">// 打开一个文件供读写,编码类型ANSI,读共享模式
file_handle=FileOpen(filename,FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ);
 class=class="str">"cmt">// 在首次执行OnCalculate函数时,我们从文件中读取报价
if(prev_calculated==class="num">0)
  {
   class=class="str">"cmt">// 读取文件的首行并确定字符串的长度
   line_string_len=StringLen(FileReadString(file_handle))+class="num">2;
   class=class="str">"cmt">// i如果文件很大(包含了大于rates_total/2个报价)
   if(FileSize(file_handle)>(class="type">class="kw">ulong)line_string_len*rates_total/class="num">2)
    {
     class=class="str">"cmt">// 设置文件指针,读取最近的rates_total/2个报价
     FileSeek(file_handle,-line_string_len*rates_total/class="num">2,SEEK_END);
     class=class="str">"cmt">// 将文件指针移动到下一行的开头
     FileReadString(file_handle);
    }
   class=class="str">"cmt">// 如果文件大小很小
   else
    {
     class=class="str">"cmt">// 将文件指针移动到文件的开头
     FileSeek(file_handle,class="num">0,SEEK_SET);
    }
   class=class="str">"cmt">// 重置报价计数器
   ticks_stored=class="num">0;
   class=class="str">"cmt">// 一直读到文件结尾
   class="kw">while(FileIsEnding(file_handle)==false)
   {
     class=class="str">"cmt">// 从文件中读取一个字符串
     file_buffer=FileReadString(file_handle);
     class=class="str">"cmt">// 如果字符串长度大于6,处理该字符串
     if(StringLen(file_buffer)>class="num">6)
      {
       class=class="str">"cmt">// 在该行中找到卖价的开始位置
       BidPosition=StringFind(file_buffer," ",StringFind(file_buffer," ")+class="num">1)+class="num">1;
       class=class="str">"cmt">// 在该行中找到买价的开始位置

常见问题

把订单号做成独立窗口指标,不和K线挤在一起,一眼就能对照哪根棒对应哪笔单。
在品种独立窗口写文件落盘逐笔数据,下次直接读文件回填,不用每次重新抓。
小布可自动读取品种页的订单号窗口并标注异常密集区,你只管判断是不是流动性异动。
多是外部文件时间字段格式和本地时区不一致,统一用UTC毫秒存再回填就能对齐。
有用但高风险,跳空段常缺报单号,建议只信连续报价段的号,别拿缺口区硬猜。