MQL5 中创建订单号指标(基础篇)
◍ 正文
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_READ | FILE_WRITE | FILE_ANSI | FILE_SHARE_READ:ANSI 省编码麻烦,SHARE_READ 允许别的程序同时读。首次启动或周期切换时,代码把可读报价上限掐在 rates_total/2,用 FileSeek 从末尾倒推,避免几千行旧数据把缓冲撑爆。外汇与贵金属报价高频跳动,实盘跑这套落盘逻辑前,建议先开模拟盘验证文件增长速率。 |
|---|
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_READ | FILE_WRITE | FILE_ANSI | FILE_SHARE_READ 方式打开,既读也写、且允许其他程序并发读。 |
|---|
若文件体积偏大——超过单行长度 line_string_len 乘以 rates_total/2 字节,就直接用 FileSeek 从末尾倒退 half 窗口,只取最近约 rates_total/2 行,避免把几年前的废报价全灌进数组;文件小则指针归零从头读。 读循环里每行先判长度是否大于 6,才往下解析。卖价起点用两次 StringFind 找空格偏移定位:第一次找到首个空格,第二次从它之后再找一个空格,加 1 即 Bid 字段起始;买价位置同理往后推。外汇与贵金属报价跳动频繁,这种回填方式能让你在 MT5 重开图表时立刻看到历史价差分布,但历史文件若被其他脚本改写,可能读出异常值,属高风险操作,验证前先备份原文件。
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">// 在该行中找到买价的开始位置