
MQL5 中创建订单号指标
简介
交易中,最好是能掌握尽可能多的信息,如此才能得到价格变动的详细图景。您可以使用订单号图表。我们试试在 MQL5 中创建一个订单号图表吧。
本文会讲到两个指标的创建:订单号价格图表,以及绘制包含一个指定订单号的烛形图的“订单号烛形图”图表。已考虑将每个指标接收到的价格值写入文件中,以构造客户端重启后要使用的指标数据(其它程序亦可使用该数据)。
创建订单号指标
我们一起在 MQL5 中编写一个于图表上绘制订单号数据的指标吧。图 1 所示为此类指标的一个示例:
图 1.订单号图表示例
此指标会标绘两条线:Bid 与 Ask 价格。每条线的绘制均可于该指标的选项中关闭。
指标会将从经纪人处接收到的当前交易品种的价格,以下述格式保存于一个文本文件中:Server 时间、Bid 价格及 Ask 价格:
2010.03.26 19:43:02 1.33955 1.33968
文件名称与金融工具名称相对应(比如 EURUSD.txt)。文件路径如下:MT5_Folder\MQL5\Files。文件的附加目录和文件名前缀可于指标选项中指定(存在多个指标时有用,附至交易品种相同的图表)。
想要创建一个指标,启动 MetaTrader 5 客户端并按 F4 键启动 MetaQuotes 语言编辑器。我们开始编写程序代码。
我们会指明:指标应于价格图表下方的某个独立窗口内标绘:
// 指标在独立窗口中展现 #property indicator_separate_window
因为要绘制两条指标线(分别为 Bid 和 Ask 价格),所以我们必须采用两个图形标绘:
// 使用2个图形绘制:卖价线和买价线 #property indicator_plots 2
我们必须指定两个指标的缓冲区,其中包含待于图表上标绘的数据:
//2个指标缓存 #property indicator_buffers 2
我们针对每一条指标线,分别定义其绘图类型 DRAW_LINE (线)、绘图风格 STYLE_SOLID (实线)以及文本标签 "Bid" 及 "Ask":
//卖价线的绘图类型 #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"
我们来指定输入变量,其值可由用户通过指标的选项菜单更改。
// BidLineEnable代表是否显示卖价线 input bool BidLineEnable=true; // 显示卖价线 // AskLineEnable代表是否显示买价线 input bool AskLineEnable=true; // 显示买价线 // path_prefix定义路径和文件名前缀 input string path_prefix=""; // 文件名前缀
BidLineEnable 和 AskLineEnable 变量允许您启用和禁用指标中 Bid 与 Ask 线的显示。path_prefix 变量允许您指定位于文件名前方的前缀。利用此变量,您还可以指定到某个子目录的路径,比如说,如果 path_prefix = "MyBroker/test_",则文件路径如下:"MetaTrader5_Folder\MQL5\Files\MyBroker",对应交易品种“欧元兑美元”,文件名会是 "test_EURUSD.txt"。
我们在全局层面声明那些会在指标各种函数中使用的变量,而这些变量的值则会在指标各次调用之间被保存:
// tick_stored变量是已存储报价的个数 int ticks_stored; // BidBuffer[]和AskBuffer[]数组是指标的缓存 double BidBuffer[],AskBuffer[];
tick_stored 变量将用于存储可用报价的数量。BidBuffer[] 与 AskBuffer[] 为动态数组,用作指标缓冲区;而图表上标绘为 Bid 与 ask 线的价格数据,则存储于这些缓冲区中。
OnInit 函数会指明包含标绘数据的 BidBuffer[] 与 AskBuffer[] 数组。我们指定:指标缓冲区值为零的数据,不得于图表上标绘。
void OnInit() { // BidBuffer[]是一个指标缓存数组 SetIndexBuffer(0,BidBuffer,INDICATOR_DATA); // AskBuffer[]是一个指标缓存数组 SetIndexBuffer(1,AskBuffer,INDICATOR_DATA); // 为卖价线设置EMPTY_VALUE PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0); // 买价线设置EMPTY_VALUE PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0); }
现在我们创建 OnCalculate 函数,并在其被调用时列出传递给该函数的所有参数:
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[])
我们来声明变量:
// file_handle变量定义文件的句柄 // BidPosition和AskPosition是字符串中头寸的卖出和买价; //line_string_len是从文件中读取的字符串的长度,i是循环计数器; int file_handle,BidPosition,AskPosition,line_string_len,i; // last_price_bid是最新的卖出报价 double last_price_bid=SymbolInfoDouble(Symbol(),SYMBOL_BID); // last_price_ask是最新的买入报价 double last_price_ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK); // filename是文件名,the file_buffer是一个字符串, // 用来读取和写入字符串数据的缓存 string filename,file_buffer;
整数型 file_handle 变量会被用于存储文件操作中文件的句柄;BidPosition 与 AskPosition - 会被用于存储字符串开始位置的 Bid 和 Ask 价格;line_string_len - 会被用于存储由文件读取的字符串长度;i 变量则会被用作一个循环计数器。上一次接收到的 Bid 和 Ask 价格值则被存储于 last_price_bid 与 last_price_ask 变量中。文件名字符串变量用于存储文件名,而 file_buffer 字符串则用于文件的读取或写入。
文件名由 path_prefix 变量、金融工具的名称以及 ".txt" 文件扩展名构造而成。与利用加法操作符序连相比,使用 StringConcatenate 函数更为可取,因为它运行更快、更节约内存。
// 由path_prefix变量和交易品种 // 以及“.Txt”构成的文件名 StringConcatenate(filename,path_prefix,Symbol(),".txt");
我们利用 FileOpen 函数打开文件以备后用:
// 打开一个文件供读写,编码类型ANSI,读共享模式 file_handle=FileOpen(filename,FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ);
因为我们会读取并向文件写入,所以我们采用 FILE_READ 与 FILE_WRITE 标志。FILE_ANSI 标志表明会使用 ANSI 代码页(默认 Unicode),FILE_SHARE_READ 标志则意味着允许其它应用程序在使用它时共享其读取权限。
该指标初次启动时没有任何数据(或是图表时段已更改):
// 在首次执行OnCalculate函数时,我们从文件中读取报价 if(prev_calculated==0) { // 读取文件的首行并确定字符串的长度 line_string_len=StringLen(FileReadString(file_handle))+2; // i如果文件很大(包含了大于rates_total/2个报价) if(FileSize(file_handle)>(ulong)line_string_len*rates_total/2) { // 设置文件指针,读取最近的rates_total/2个报价 FileSeek(file_handle,-line_string_len*rates_total/2,SEEK_END); // 将文件指针移动到下一行的开头 FileReadString(file_handle); } // 如果文件大小很小 else { // 将文件指针移动到文件的开头 FileSeek(file_handle,0,SEEK_SET); } // 重置报价计数器 ticks_stored=0; // 一直读到文件结尾 while(FileIsEnding(file_handle)==false) { // 从文件中读取一个字符串 file_buffer=FileReadString(file_handle); // 如果字符串长度大于6,处理该字符串 if(StringLen(file_buffer)>6) { // 在该行中找到卖价的开始位置 BidPosition=StringFind(file_buffer," ",StringFind(file_buffer," ")+1)+1; // 在该行中找到买价的开始位置 AskPosition=StringFind(file_buffer," ",BidPosition)+1; // 如果要绘制卖价线,将其加到BidBuffer[]数组中 if(BidLineEnable) BidBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,BidPosition,AskPosition-BidPosition-1)); // 如果要绘制买价线,将其加到AskBuffer[]数组中 if(AskLineEnable) AskBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,AskPosition)); // 报价计数器值加一 ticks_stored++; } } }
我们会将可通过文件读取的报价的数量限制为图表上柱数的一半。首先,我们从文件中读取字符串并确定其长度。行末尾有两个带 10 与 13 代码的附加字符("newline" 与 "carriage return"),所以我们必须将行长度加 2。
我们假设文件中其余行的平均长度相同。如果文件长度大于 rates_total/2 数量一行的长度结果(即,如果文件包含的报价数量比 rates_total/2 多),则我们只会读取 rates_total/2 最后报价。一定要如此,我们设置远处的文件指针,等于通过 rates_total/2 (来自文件末尾)得出的字符串长度结果,然后读取文件的一行,并将文件指针对准一行的开头。
注意:我们利用 if 操作符来对比两个值,它们存在不同的类型:文件长度为 ulong 类型,而右侧的表达式则具备 int 类型。因此,我们将右侧表达式的显性类型转换为 ulong 类型。
如果文件中包含的报价比 rates_total/2 少,则我们会将文件指针转到文件开头。
我们将报价计数器设置为零,并从文件读取行,直至抵达文件末尾。字符串处理针对那些长度大于 6 个字符的字符串执行 - 此为最小字符串长度,其中包括日期、时间、bid、ask 对应的字符以及它们之间的分隔符。我们从某个字符串提取出 Bid 和 Ask 值,如果对应行应予标绘,则从文件读取,再增加报价计数器的计数。
如果之前读取过该数据,则我们利用 FileSeek 函数将文件指针移至文件末尾(新数据被写入到文件中)。我们利用 StringConcatenate 函数生成字符串,并将利用 FileWrite 函数将其写入一个文件。如果对应行应予标绘并增加报价计数器的计数,则我们会将新的 Bid 与 Ask 价格值添加到 BidBuffer[] 和 AskBuffer[] 数组。
// 如果数据已经被读取过 else { // 将文件指针移动到文件末端 FileSeek(file_handle,0,SEEK_END); // 形成一个要写入文件的字符串 StringConcatenate(file_buffer,TimeCurrent()," ",DoubleToString(last_price_bid,_Digits)," ",DoubleToString(last_price_ask,_Digits)); // 将一个字符串写入文件 FileWrite(file_handle,file_buffer); // 如果要绘制卖价线,将最新的卖价加到BidBuffer[]数组中 if(BidLineEnable) BidBuffer[ticks_stored]=last_price_bid; // 如果要绘制买价线,将最新的买价加到AskBuffer[]数组中 if(AskLineEnable) AskBuffer[ticks_stored]=last_price_ask; // 报价计数器值加一 ticks_stored++; }
有人可能会问,你为什么不从 OnInit 函数内的某个文件读取数据呢?原因如下:BidBuffer[] 与 AskBuffer[] 动态数组长度尚未定义,它会在 OnCalculate 函数被调用时指定。
我们关闭之前打开的文件:
// 关闭文件 FileClose(file_handle);
从文件读取或向 BidBuffer[] 和 AskBuffer[] 数组添加之后,如果报价计数器大于等于图表上的柱数,则会有半数的旧报价被移除,其余报价也会被移离其原位。
// 如果报价数量大于等于图表中的柱形图 if(ticks_stored>=rates_total) { // 移除前半部分tick_stored/2个报价,转移剩余报价 for(i=ticks_stored/2;i<ticks_stored;i++) { // 如果要绘制卖价线,将BidBuffer[]数组后tick_stored/2个元素移到前面来 if(BidLineEnable) BidBuffer[i-ticks_stored/2]=BidBuffer[i]; // 如果要绘制买价线,将AskBuffer[]数组后tick_stored/2个元素移到前面来 if(AskLineEnable) AskBuffer[i-ticks_stored/2]=AskBuffer[i]; } // 改变计数器的值 ticks_stored-=ticks_stored/2; }
指标缓冲区的 BidBuffer[] 与 AskBuffer[] 数组并非时间序列,所以最近的元素索引为 ticks_stored-1,而最近的图表索引为 rates_total-1。为将它们捏合为同一层级,我们利用 PlotIndexSetInteger 函数来平移指数行:
// 移动卖价线使其和价格图表对齐 PlotIndexSetInteger(0,PLOT_SHIFT,rates_total-ticks_stored); // 移动买价线使其和价格图表对齐 PlotIndexSetInteger(1,PLOT_SHIFT,rates_total-ticks_stored);
最近接收到的价格值被存储到 BidBuffer[] 和 AskBuffer[],索引为 rates_total-1 (如果对应行应予标绘),以令其于指标窗口的左上角显示。
// 如果要绘制卖价线,将值存入BidBuffer []数组的最后一个元素中 // 指标窗口中显示最新的卖价 if(BidLineEnable) BidBuffer[rates_total-1]=last_price_bid; // 如果要绘制买价线,将值存入AskBuffer []数组的最后一个元素中 // 在指标窗口中显示最新的买价 if(AskLineEnable) AskBuffer[rates_total-1]=last_price_ask;
OnCalculate 函数完全靠 rates_total 的返回来执行(您可以返回任何非零数值),此函数的代码以一个花括号作为结尾。
// 从OnCalculate()返回一个非零值 return(rates_total); }
订单号指标已经编写完成。本文结尾处链接可供下载该指标的完整源代码。
创建“订单号烛形图”指标
现在我们来编写一个标绘所谓的“订单号烛形图”的指标。传统的烛形图中每个烛形都有对应的指定时间段,“订单号烛形图”则拥有不同的结构:每个烛形都有一些预定义的、从经纪人处接收的订单号数量(等量烛形)。该指标如图 2 所示:
图 2.“订单号烛形”指标
“订单号烛形图”指标以及上面所说的订单号指标,会将所有传入的报价写入文件。数据格式与文件位置详情均相同。文件路径、名称前缀、某烛形的订单号数量以及价格类型(Bid 或 Ask),均可于指标选项中指定。
想要创建一个指标,启动 MetaTrader 5 客户端并按 F4 键启动 MetaQuotes 语言编辑器。
我们指定其应于某独立窗口内标绘:
// 指标将在独立的窗口中绘制 #property indicator_separate_window
此指标只有一个图形标绘:彩色烛形。
// 只用到一个绘图形状,彩色蜡烛线 #property indicator_plots 1
我们需要四个缓冲区来显示彩色烛形和存储价格数据值(开盘价、最高价、最低价和收盘价),以及每个烛形的价格值。我们还需要一个附加缓冲区来存储烛形的颜色索引。
// 我们需要4个缓存存储开盘、最高、最低和收盘价格,以及1个颜色索引缓存 #property indicator_buffers 5
我们来指定绘图类型:DRAW_COLOR_CANDLES - 彩色烛形。
// 确定绘图类型:彩色蜡烛线 #property indicator_type1 DRAW_COLOR_CANDLES
我们来指定烛形中应用的颜色:
// 确定蜡烛线的颜色 #property indicator_color1 Gray,Red,Green
我们来创建枚举类型的 price_types,其中包含下述各值中的一个:Bid 或 Ask:
/ / 声明枚举值 enum price_types ( Bid, Ask )
我们指定输入参数,该参数可由用户通过指标的选项菜单进行修改:
// 输入参数ticks_in_candle确定 // 一根蜡烛线的tick数量 input int ticks_in_candle=16; //蜡烛线的tick数量 // price_types类型的输入参数applied_price,表示 // 用于计算指标的数据:买价或卖价。 input price_types applied_price=0; // 价格 // path_prefix输入参数确定了路径和文件名的前缀 input string path_prefix=""; // 文件名前缀
ticks_in_candle 变量会指定某烛形对应的订单号数量。applied_price 变量会指明某个用于构造烛形图的价格的类型:Bid 或 Ask。历史订单号数据文件的目录和文件名前缀,可在 path_prefix 变量中指定。
应于指标各次调用之间保存的带值变量,均于全局层面声明。
// ticks_stored变量保存报价的数量 int ticks_stored; // TTicksBuffer []数组用来存储价格 // OpenBuffer [], HighBuffer [], LowBuffer [] 以及 CloseBuffer [] 数组 // 用来存储蜡烛线的开盘、最高、最低和收盘价 // ColorIndexBuffer []数组用来存储彩色蜡烛线的索引 double TicksBuffer[],OpenBuffer[],HighBuffer[],LowBuffer[],CloseBuffer[],ColorIndexBuffer[];
tick_stored 变量用于存储可用报价的数量。TicksBuffer[] 数组用于存储接收到的报价,OpenBuffer[]、HighBuffer[]、LowBuffer[] 和 CloseBuffer[] 数组则用于存储将于图表上标绘的烛形价格(开盘、最高、最低和收盘)。ColorIndexBuffer[] 数组用于存储烛形图的颜色索引。
OnInit 函数指明 OpenBuffer[]、HighBuffer[]、LowBuffer[] 和 CloseBuffer[] 数组会用作某指标的缓冲区,ColorIndexBuffer[] 数组中包含一个烛形图颜色索引,而 TicksBuffer[] 数组则用于中间计算:
void OnInit() { // OpenBuffer[]数组是一个指标缓存 SetIndexBuffer(0,OpenBuffer,INDICATOR_DATA); // HighBuffer[]数组是一个指标缓存 SetIndexBuffer(1,HighBuffer,INDICATOR_DATA); // LowBuffer[]数组是一个指标缓存 SetIndexBuffer(2,LowBuffer,INDICATOR_DATA); // CloseBuffer[]数组是一个指标缓存 SetIndexBuffer(3,CloseBuffer,INDICATOR_DATA); // ColorIndexBuffer[]数组是颜色索引的缓存 SetIndexBuffer(4,ColorIndexBuffer,INDICATOR_COLOR_INDEX); // TicksBuffer[]数组用来存储中间计算值 SetIndexBuffer(5,TicksBuffer,INDICATOR_CALCULATIONS);
接下来,我们指定 OpenBuffer[]、HighBuffer[]、LowBuffer[]、CloseBuffer[] 和 ColorIndexBuffer[] 数组为时间序列(即最近的数据索引为 0):
// 设置OpenBuffer[]数组为时间序列 ArraySetAsSeries(OpenBuffer,true); // 设置HighBuffer[]数组为时间序列 ArraySetAsSeries(HighBuffer,true); // 设置LowBuffer[]数组为时间序列 ArraySetAsSeries(LowBuffer,true); // 设置CloseBuffer[]数组为时间序列 ArraySetAsSeries(CloseBuffer,true); // 设置ColorIndexBuffer []数组为时间序列 ArraySetAsSeries(ColorIndexBuffer,true);
此指标缓冲区的值为 0,不得于图表上标绘:
// 开盘价(第0个图形)的空值不应被绘制 PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0); // 最高价(第1个图形)的空值不应被绘制 PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0); // 最低价(第2个图形)的空值不应被绘制 PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,0); // 收盘价(第3个图形)的空值不应被绘制 PlotIndexSetDouble(3,PLOT_EMPTY_VALUE,0);
OnInit 函数的编写已经完成,我们利用一个花括号来作为此函数的结尾。
到了编写 OnCalculate 函数的时候了。我们来指定要被传递至此函数的所有参数:
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) {
//file_handle变量定义文件的句柄 // BidPosition和AskPosition是字符串中头寸的卖出和买价; // line_string_len是从文件中读取的字符串的长度, // CandleNumber确定开盘、最高、最低和收盘价的蜡烛线的编号 // i - 循环计数器; int file_handle,BidPosition,AskPosition,line_string_len,CandleNumber,i; // last_price_bid变量是最近接收到的卖价 double last_price_bid=SymbolInfoDouble(Symbol(),SYMBOL_BID); // last_price_ask变量是最近接收到的买价 double last_price_ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK); // filename是文件名,the file_buffer是一个字符串, // 用来读取和写入字符串数据的缓存 string filename,file_buffer;
整数型 file_handle 变量用于存储文件操作中文件的句柄;BidPosition 与 AskPosition - 用于存储字符串开始位置的 Bid 和 Ask 价格;line_string_len - 是由文件读取的字符串长度; CandleNumber - 是算出的烛形索引; i 变量则用作一个循环计数器。
最近接收到的 Bid 和 Ask 价格值则被存储于 last_price_bid 与 last_price_ask 双型变量中。filename 字符串型变量用于存储文件名,file_buffer 是文件操作中使用的一种字符串。
TicksBuffer[] 数组的大小并非自动设置,这一点与作为指标缓冲区的 OpenBuffer[]、HighBuffer[]、LowBuffer[]、CloseBuffer[] 及 ColorIndexBuffer[] 数组不同。所以,我们将 TicksBuffer[] 数组设置为与 OpenBuffer[]、HighBuffer[]、LowBuffer[]、CloseBuffer[] 及 ColorIndexBuffer[] 数组相同的大小。
// 设置TicksBuffer[]数组的大小 ArrayResize(TicksBuffer,ArraySize(CloseBuffer));
根据 path_prefix 变量、金融工具的名称以及 ".txt" 文件扩展名制备文件名。
// 由path_prefix变量和交易品种 // 以及“.Txt”构成的文件名 StringConcatenate(filename,path_prefix,Symbol(),".txt");
我们利用前一个指标的相关描述,利用参数打开文件。
// 打开一个文件供读写,编码类型ANSI,读共享模式 file_handle=FileOpen(filename,FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ);
如果 OnCalculate 函数被第一次调用,且 TicksBuffer[] 数组中没有任何数据,我们则从文件读取它们:
if(prev_calculated==0) { // 读取文件的首行并确定字符串的长度 line_string_len=StringLen(FileReadString(file_handle))+2; // 如果文件很大(包含了大于rates_total/2个报价) if(FileSize(file_handle)>(ulong)line_string_len*rates_total/2) { // 设置文件指针,读取最近的rates_total/2个报价 FileSeek(file_handle,-line_string_len*rates_total/2,SEEK_END); // 将文件指针移动到下一行的开头 FileReadString(file_handle); } // 如果文件大小很小 else { // 将文件指针移动到文件的开头 FileSeek(file_handle,0,SEEK_SET); } // 重置报价计数器 ticks_stored=0; //一直读到文件结尾 while(FileIsEnding(file_handle)==false) { // 从文件中读取一个字符串 file_buffer=FileReadString(file_handle); // 如果字符串长度大于6,处理该字符串 if(StringLen(file_buffer)>6) { // 在行中找到卖价的开始位置 BidPosition=StringFind(file_buffer," ",StringFind(file_buffer," ")+1)+1; //在行中找到买价的开始位置 AskPosition=StringFind(file_buffer," ",BidPosition)+1; // 如果使用了卖价,将其加到TicksBuffer[]数组中 if(applied_price==0) TicksBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,BidPosition,AskPosition-BidPosition-1)); // 如果使用了买价,将其加到TicksBuffer[]数组中 if(applied_price==1) TicksBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,AskPosition)); // 报价计数器值加一 ticks_stored++; } } }
从文件读取报价的内容此前已有详述,与前一个指标相同。
如果报价在进入 TicksBuffer[] 数组之前已被读取,我们就会向文件写入一个新价格值,向 TicksBuffer[] 数组下达一个新价格并增加报价计数器的计数:
// 如果数据已经被读取过 else { // 将文件指针移动到文件末端 FileSeek(file_handle,0,SEEK_END); // 形成一个要写入文件的字符串 StringConcatenate(file_buffer,TimeCurrent()," ",DoubleToString(last_price_bid,_Digits)," ",DoubleToString(last_price_ask,_Digits)); // 将一个字符串写入文件 FileWrite(file_handle,file_buffer); // 如果使用了卖价,将最新的卖价加到TicksBuffer[]数组中 if(applied_price==0) TicksBuffer[ticks_stored]=last_price_bid; // 如果使用了买价,将最新的买价加到TicksBuffer[]数组中 if(applied_price==1) TicksBuffer[ticks_stored]=last_price_ask; // 报价计数器值加一 ticks_stored++; }
文件的关闭:
// 关闭文件 FileClose(file_handle);
如果存储的报价数量达到或超过了价格图表上的柱数,我们就会移除半数的旧数据并平移其余数据:
// 如果报价数量大于等于图表中的柱形图的数量 if(ticks_stored>=rates_total) { // 移除前半部分tick_stored/2个报价,转移剩余报价 for(i=ticks_stored/2;i<ticks_stored;i++) { // 将数据移到TicksBuffer[]数组开头的tick_stored/2个元素中 TicksBuffer[i-ticks_stored/2]=TicksBuffer[i]; } // 改变报价计数器 ticks_stored-=ticks_stored/2; }
我们来计算每个烛形的 OHLC 值,并将这些值置入相应的指标缓冲区:
// 为CandleNumber分配一个无效的蜡烛线数字 CandleNumber=-1; // 查找所有能构成蜡烛线的价格数据 for(i=0;i<ticks_stored;i++) { //如果这个蜡烛线已经形成 if(CandleNumber==(int)(MathFloor((ticks_stored-1)/ticks_in_candle)-MathFloor(i/ticks_in_candle))) { // 当前报价仍就是当前蜡烛线的收盘价 CloseBuffer[CandleNumber]=TicksBuffer[i]; // 如果当前价格大于当前蜡烛线的最高价, // 那么它将是蜡烛线的新高价 if(TicksBuffer[i]>HighBuffer[CandleNumber]) HighBuffer[CandleNumber]=TicksBuffer[i]; // 如果当前价格小于当前蜡烛线的最低价,那么它将是蜡烛线的新低价 //那么它将是蜡烛线的新低价 if(TicksBuffer[i]<LowBuffer[CandleNumber]) LowBuffer[CandleNumber]=TicksBuffer[i]; // 如果蜡烛线看涨,它的颜色索引将为2(绿色) if(CloseBuffer[CandleNumber]>OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=2; // 如果蜡烛线看跌,它的颜色索引将为1(红色) if(CloseBuffer[CandleNumber]<OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=1; // 如果开盘和收盘价一样,它的颜色索引将为0(灰色) if(CloseBuffer[CandleNumber]==OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=0; } // 如果这个蜡烛线还没被计算过 else { // 让我们来确定蜡烛线的索引 CandleNumber=(int)(MathFloor((ticks_stored-1)/ticks_in_candle)-MathFloor(i/ticks_in_candle)); // 当前报价是蜡烛线的开盘价 OpenBuffer[CandleNumber]=TicksBuffer[i]; // 当前报价是蜡烛线的最高价 HighBuffer[CandleNumber]=TicksBuffer[i]; // 当前报价是蜡烛线的最低价 LowBuffer[CandleNumber]=TicksBuffer[i]; // 当前报价是蜡烛线的收盘价 CloseBuffer[CandleNumber]=TicksBuffer[i]; // 此蜡烛线的颜色索引将为0(灰色) ColorIndexBuffer[CandleNumber]=0; } }
OnCalculate 函数的执行要靠返回非零值来完成,也就是说,TicksBuffer[] 数组已有数据,根本没有必要在下次调用此函数时读取。我们用后花括号作为此函数的结尾。
// 从OnCalculate()返回一个非零值 return(rates_total); }
本文末尾处有一个可下载此指标完整源代码的链接。
总结
我们已于本文中创建两个指标:订单号图表指标及“订单号烛形图”指标。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/60
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.




MT4 不存储历史刻度线数据。您必须自己存储刻度线数据。请在代码库中 搜索,因为应该有多个 MT4 版本可用于 "跳动图"。
感谢您提供这篇有趣的文章。
使用文件缓冲数据是必须的吗?
难道不能使用统计数组在内存中缓冲历史数据吗?
致
下午好!
感谢您的文章和代码。您能告诉我为什么会这样吗?
我使用您的勾选图表,不加改动地编译后粘贴到 EURUSD M1。底部会打开一个窗口,买入价和卖出价会以白色显示(在左上角),但红色和蓝色柱形底部的图表不会出现。
然后,我将图表向左移动,以至于看不到顶部的当前蜡烛图,底部的图表出现了。我将图表移回右边--顶部可以看到当前蜡烛图,但底部没有图表。
MT4 不存储历史刻度线数据。您必须自己存储刻度线数据。请在代码库中 搜索,因为应该有多个 MT4 版本可用于 "跳动图"。