![MQL5 中创建订单号指标](https://c.mql5.com/i/articles/overlay.png)
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
![针对初学者以 MQL5 编写“EA 交易”的分步指南](https://c.mql5.com/2/0/create_EA_step_by_step_MQL5.png)
![MQL5 中如何调用指标](https://c.mql5.com/2/0/Calling_indicators_MQL5.png)
![如何在另一指标的基础上编写一个指标](https://c.mql5.com/2/0/indicator_based_on_other_MQL5__1.png)
![MQL5 中对象创建和析构的顺序](https://c.mql5.com/2/0/recycle_ava__1.png)