步入新軌:MQL5的自定義指標 [MT4]
作者:
MT4 來源:
cxh99.com 發(fā)布時間:2012年05月28日 點擊數(shù):
【
收藏到本網(wǎng)的會員中心】
- 原文來自MQL5官方論壇:http://articles.mql4.com/865作者:Автор: Андрей中文翻譯:bcsunwww
--------------------------------------------------------
介紹
我們終于有機會試試新交易終端- MetaTrader 5了。毫無疑問,它是值得關(guān)注的,與它的前身相比,它有許多新的特性。這個平臺的重要優(yōu)勢有: 編程語言上的根本修改,允許用面向?qū)ο蟮姆椒ň幊獭_€允許豐富的結(jié)構(gòu)化編程。
代碼執(zhí)行速度比MetaTrader 4快多了。
顯示必要信息的能力有了本質(zhì)的增加。 新終端和編程語言的許多新特性和能力無法一一列出,一些新鮮的東西值得我們用專門的文章分別討論。暫時還沒有面向?qū)ο蟮木幊檀a,對于開發(fā)者而言,還有很多重要的課題。在這篇文章里,我們研究一下指標,他們的結(jié)構(gòu)、畫法、類型和編程細節(jié)等與MQL4的對比。這篇文章并不復雜,而且,這里研究的東西可以用后面的附件直接在終端里驗證。 1、一般的結(jié)構(gòu)
與MQL4相比,指標的一般結(jié)構(gòu)沒有改變。
象以前一樣,有三個函數(shù):初始化函數(shù)、數(shù)據(jù)處理過程、卸載函數(shù)
象以前一樣,許多指標參數(shù)可以用屬性(#property)定義。他們有許多是專門為指標設(shè)計的,屬性和輸入?yún)?shù)象以前一樣,在全局背景下定義。
例如,讓我們看一下彩色RSI指標的執(zhí)行。下面的代碼是刪節(jié)版,完整版在Color_RSI.mq5里。
//+------------------------------------------------------------------+
//|Color_RSI.mq5 |
//+------------------------------------------------------------------+
// information properties
#property copyright "TheXpert"
#property link"theforexpert@gmail.com"
#property version "1.00"
// Indicator description -- the total length should not exceed 511 characters with CR symbols
#property description "Indicator programming demo in MQL5"
#property description "RSI indicator custom painting"
上面指定的屬性顯示在指標信息面板上(屬性標簽“Common”)。看起來象下面這樣:
// the indicator properties#property indicator_separate_window
// indicator will be displayed in the separate subwindow
//#property indicator_minimum 0
//#property indicator_maximum 100#property indicator_buffers 2
// buffers used#property indicator_plots 1
// buffers displayed#property indicator_color1 DarkSalmon, DeepSkyBlue
// we use 2 colors#property indicator_type1 DRAW_COLOR_LINE
// and special color style[/pre]These properties are the indicator properties. The other properties description can be found in the help.
//---- buffersdouble Values[]; // buffer for valuesdouble ValuesPainting[];
// buffer for color indexesdouble BufferForCopy[];
// additional buffer for copying
// indicator input parameter
sinput string _1 = "RSI Parameters";
input intRSIPeriod= 5;
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE;
input string _2 = "Color settings";
input colorDown = DarkSalmon;
input colorUp = DeepSkyBlue;
// variable to store indicator handleint RSIHandle;
這里是指標輸入?yún)?shù)和全局變量(不要和客戶終端全局變量搞混)。指標輸入?yún)?shù)用標識符“input”指定。
現(xiàn)在為指標設(shè)定枚舉是可能的了,有時對于避免選錯參數(shù)很有用。例如:可以用下拉框把AppliedPrice參數(shù)有效值列出。
因此,所有的枚舉,包括用戶定義的,將在同一下拉框中列出。例如下面的參數(shù):
//...enum DayOfWeek{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday};
input DayOfWeek Day;
//...
將會象下面這樣顯示:
int OnInit(){
// set the indicator buffers
// set Values as a buffer for displaying SetIndexBuffer(0, Values, INDICATOR_DATA);
// set ValuesPainting as buffer for colors SetIndexBuffer(1, ValuesPainting, INDICATOR_COLOR_INDEX);
// Set beginning of Values buffer drawing PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, RSIPeriod);
// Set indicator name IndicatorSetString(INDICATOR_SHORTNAME, "Color RSI(" + string(RSIPeriod) + ")");
// Set empty value for Values buffer PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
// Set colors for the buffer PlotIndexSetInteger(0, PLOT_LINE_COLOR, 0, Down);
PlotIndexSetInteger(0, PLOT_LINE_COLOR, 1, Up);
RSIHandle = iRSI(NULL, 0, RSIPeriod, AppliedPrice);
ArrayResize(BufferForCopy, 3); // Set the indexation order for buffers (as series)
ArraySetAsSeries(Values, true);
ArraySetAsSeries(ValuesPainting, true);
ArraySetAsSeries(BufferForCopy, true);
return(0);
}
OnInit是指標初始化函數(shù)。在這里我們配置指標緩存數(shù)和他們的屬性,并且定義指標變量(不能在屬性段定義或必須動態(tài)設(shè)定的)。還有原始數(shù)據(jù)的初始化,包括指標句柄的分配。
// Function for data calculation
// note that the function parameters can be renamed
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[])
{
int toCount = (int)MathMin(rates_total, rates_total - prev_calculated + 1);
if (prev_calculated < 0 || prev_calculated > rates_total)
{
toCount = rates_total;
}
int copied = CopyBuffer(RSIHandle, 0, 0, toCount, Values);
if (copied == -1)
{
Print("Error while data copying. Error №", GetLastError());
return 0;
}
// Coloring. Yes, now it became so simple
for (int i = toCount - 2; i >= 0; --i)
{
if (Values[i + 1] != EMPTY_VALUE &&
Values > Values[i + 1]) ValuesPainting = 1;
else ValuesPainting = 0;
}
return rates_total;
}
OnCalculate是數(shù)據(jù)計算函數(shù)。這個函數(shù)有兩種形式。這里是它的標準形式: // the function is not obligatory in a code/*void OnDeinit(){}*/
OnDeinit是指標反初始化函數(shù)。通常為了釋放資源是必須的,如文件句柄。一些其他情況下不是必須的。
2、指標的兩種形式
第一種是標準形式,象我們在MQL4中用過的一樣,只有一點修改。OnCalculate函數(shù)替代了Start函數(shù)。標準形式象下面這樣:
int OnCalculate(const int rates_total,
// Arrays size const int prev_calculated,
// Bars processed on the previous callconst datetime& time[],
// Data for the current chart and timeframe...
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int& spread[])
{ return rates_total;
}
為了減少準備數(shù)據(jù)拷貝的代碼量,圖表數(shù)據(jù)作為數(shù)組直接傳入指標參數(shù)。此外,有效K線數(shù)作為函數(shù)的第一個參數(shù),自上次調(diào)用或首次調(diào)用以來處理過的K線數(shù)作為第二個參數(shù)。 在第一次指標調(diào)用時這個參數(shù)為0值。這個參數(shù)是IndicatorCounted()函數(shù)的替代,這對于許多開發(fā)者來說是不方便的。 第二種形式是MQL4里i<…>OnArray類函數(shù)的替代和擴展。終端示例里有一個這種類型的指標——Custom Moving Average.這類指標是為用戶選擇的數(shù)據(jù)處理過程準備的,包括自定義指標。這類指標數(shù)據(jù)處理函數(shù)看起來象這樣:
int OnCalculate (const int rates_total,// the size of the price[] array
const int prev_calculated,// bars calculated in the previous call
const int begin,// where notional data start from
const double& price[] // data array for calculation
{ return rates_total;}
最后一個參數(shù)是用戶選擇的待處理數(shù)據(jù)。如果你想使用一個有很多數(shù)據(jù)緩存的指標,第一個指標緩存將用做數(shù)據(jù)處理。
第一個指標的數(shù)據(jù)意味著這個指標將被應用到首先附加在選定圖表上的指標。
先前指標的數(shù)據(jù)意味著這個指標將被應用到最后附加在選定圖表上的指標。
這些指標可以用來組合所有堆棧。例如,用Custom Moving Average指標可以得到三重平滑均線,利用第一條指標數(shù)據(jù)進行處理,然后第二個指標平滑第一個,然后第三個指標平滑第二個:
有很多執(zhí)行這種形式的標準指標。因此,當你看到applied_price_or_handle這樣的指標提示:
它表示epY指標的執(zhí)行方式是利用客戶數(shù)據(jù)計算——數(shù)據(jù)句柄做為applied_price_or_handle參數(shù)傳送。
{ // ... RSIHandle = iRSI(NULL, 0, RSIPeriod, AppliedPrice);
SmoothHandle = iMA(NULL, 0, SmoothPeriod, 0, MODE_EMA, RSIHandle); // ...}
這種形式有另一種新應用——寫通用指標的能力。這樣指標的一個例子是附件中的Direction_Brush.mq5。
結(jié)果顯示在上圖中。這個例子中,趨勢顏色被分離到一個獨立的實體,在另一個指標中執(zhí)行。
當然,他們的應用是有限制的,因為只能用在0緩存指標中。盡管如此,我認為這種指標是有用的。
另一方面,當你寫一個自定義指標時,你應該充分考慮它,因為在0緩存指標中主要信息處理允許避免在一個指標中執(zhí)行多個功能。
粗略瀏覽一下,應用范圍并不像看起來那樣狹窄: 指標參數(shù)表著色(頂、方向、水平、片段等),包括風格形象;
不同條件下的不同信號;
收集并顯示統(tǒng)計數(shù)據(jù)——例如,數(shù)據(jù)分布;
通用指標的結(jié)構(gòu)可以用一個緩存計算——例如,平均線、ZIGZAG。
上面提到的特征并不全面。我想許多另人印象深刻的設(shè)計以后會被發(fā)現(xiàn)。
3、存取數(shù)據(jù)
在MQL5中數(shù)據(jù)存取原則已經(jīng)改變。重點變化在數(shù)組,做為一個結(jié)果,計算速度取得了引人注目的增長,現(xiàn)在,不必為每個值創(chuàng)建一個數(shù)組并調(diào)用iCustom函數(shù)了。取而代之的是,調(diào)用一個函數(shù)就可以得到必要的數(shù)據(jù)了,然后直接使用已拷貝到指定數(shù)組的想要的數(shù)據(jù)。 數(shù)據(jù)拷貝使用系統(tǒng)函數(shù)CopyBuffer執(zhí)行。在幫助文件中可以找到這個函數(shù)的描述。
指標和靜態(tài)數(shù)組能拷貝的最大數(shù)據(jù)量由數(shù)組的大小決定。動態(tài)數(shù)組的大小在拷貝數(shù)據(jù)量超過它的大小時能夠改變。除此以外,還有許多存取歷史數(shù)據(jù)的函數(shù): Function
Description
CopyBuffer
Gets data of a specified buffer of a certain indicator in the necessary quantity.
CopyRates
Gets history data of MqlRates structure of a specified symbol-period in specified quantity into the rates_array array.
CopyTime
The function gets to time_array history data of bar opening time for the specified symbol-period pair in the specified quantity.
CopyOpen
The function gets into open_array the history data of bar open prices for the selected symbol-period pair in the specified quantity.
CopyHigh
The function gets into high_array the history data of highest bar prices for the selected symbol-period pair in the specified quantity.
CopyLow
The function gets into low_array the history data of minimal bar prices for the selected symbol-period pair in the specified quantity.
CopyClose
The function gets into close_array the history data of bar close prices for the selected symbol-period pair in the specified quantity.
CopyTickVolume
The function gets into volume_array the history data of tick volumes for the selected symbol-period pair in the specified quantity.
CopyRealVolume
The function gets into volume_array the history data of trade volumes for the selected symbol-period pair in the specified quantity.
CopySpread
The function gets into spread_array the history data of spread values for the selected symbol-period pair in the specified quantity.
祥細描述在幫助文件里有。
這個數(shù)據(jù)只在指標的一種形式里傳遞,其他的有他們自己的形式。
基于歷史數(shù)據(jù)數(shù)組沒有必要雙重存在的事實,建議使用一個動態(tài)的無指標的緩存存儲數(shù)據(jù)。
4、指標緩存
指標緩存數(shù)沒有限制。
現(xiàn)在你沒有必要思考怎樣容納正確的信息、怎樣高效執(zhí)行中間計算、如何創(chuàng)建指標組了。但是我們不能忘記緩存的存儲是需要內(nèi)存的。因此,如果你指定了一個歷史縱深達1000000柱的終端并在一分鐘圖表上附加上一組“厚重的”指標組,那么,當終端吃掉上G的內(nèi)存時你不要驚訝。 緩存的本質(zhì)經(jīng)歷了一些改變。使用的緩存數(shù)量在屬性段指定。#property indicator_buffers 2 // buffers used
這個值相當于緩存的數(shù)量。
顯示的緩存數(shù)量在屬性段設(shè)定:#property indicator_plots 1 // buffers displeyed
這里是些細節(jié)。許多繪制樣式只需要一個INDICATOR_DATA緩存來繪制。然而,有些樣式需要幾個指標緩存來繪制。
象下面的繪制樣式: DRAW_HISTOGRAM2 –需要2個指標緩存(HistogramSample.mq5)

DRAW_FILLING --需要2個指標緩存(CrossMa.mq5)
- ~
DRAW_BARS --需要4個指標緩存(BarsSample.mq5)
- ~
DRAW_CANDLES --需要4個指標緩存(CandleSample.mq5)
現(xiàn)在所有指標緩存可以分為三類:
INDICATOR_DATA –在圖表上顯示數(shù)據(jù)的緩存,這些緩存為制圖和iCustom而準備。他們需要事先聲明。在過于隨意的命令下,代碼編譯會成功,但附加到圖表時會失敗;
INDICATOR_COLOR_INDEX –存儲顏色緩存。對于INDICATOR_DATA類型的彩色緩存索引的存儲是必需的有一個特殊的顏色類型。這樣的緩存(我們稱為顏色緩存)應該在使用它的主緩存后聲明;
INDICATOR_CALCULATIONS –這種類型緩存是為存儲輔助計算結(jié)果而準備的。他們不在圖表上顯示。
int OnInit()
{
// ...
SetIndexBuffer(0, V2, INDICATOR_DATA);
SetIndexBuffer(1, V2C,INDICATOR_COLOR_INDEX);
SetIndexBuffer(2, V4, INDICATOR_DATA);
SetIndexBuffer(3, V4C,INDICATOR_COLOR_INDEX);
SetIndexBuffer(4, V1, INDICATOR_CALCULATIONS);
SetIndexBuffer(5, V3, INDICATOR_CALCULATIONS);
// ...
return 0;
}
客戶指標還有一些特征:
圖表上顯示的指標的是可讀的。指標索引必須與聲明的一致;
顏色存儲指標是可讀的,但不都是,例如,上面指標代碼中,V2C指標可讀并可獲得必要數(shù)據(jù),但V4不能;
象MQL4一樣,緩存不能做為中間計算,如果你想存取外部數(shù)據(jù)緩存,要聲明為INDICATOR_DATA。
對于不可能獲得的緩存請求,會產(chǎn)生錯誤碼4806。
讓我們探討一下顏色緩存的細節(jié):
在MQL4中,為每一個顏色建立一個緩存是必須的,但現(xiàn)在用顏色樣式,你可以為一個緩存指定63種顏色。在某些情況下,可以優(yōu)化使用的緩存數(shù)量,并節(jié)省內(nèi)存。它也打開了書寫指標的新能力,特別在顯示風格方面。
另外,這種改革,在某些時候,與MQL4比,極大的簡化了幾種顏色的應用邏輯,最明顯的例子——用顏色區(qū)分趨勢。在MQL4里,這種簡單的例子的實施需要3個緩存,并且編程復雜。
#property indicator_color1 DarkSalmon, DeepSkyBlue // we use 2 colors
#property indicator_type1 DRAW_COLOR_LINE
// and special color drawing style//---- buffersdouble Values[];
// buffer for valuesdouble ValuesPainting[];
// buffer for color indexesint OnInit(){
// Registering buffers
// Values as drawing buffer SetIndexBuffer(0, Values, INDICATOR_DATA);
// ValuesPainting as color storage buffer SetIndexBuffer(1, ValuesPainting, INDICATOR_COLOR_INDEX);
// … return(0);}
// function for data calculationint OnCalculate(/*…*/){ int toCount = (int)MathMin(rates_total, rates_total - prev_calculated + 1);
int copied = CopyBuffer(RSIHandle, 0, 0, rates_total, Values);
if (copied == -1) {Print("Error while data copying. Error №", GetLastError());return 1; }
// Colouring. Yes, now it became so simple
for (int i = toCount - 2; i >= 0; --i)
{if (Values > Values[i + 1]) ValuesPainting = 1;
else
ValuesPainting = 0; }
return rates_total;}
更多的代碼,我們得到下面的圖:

用顏色類型繪圖時,你應該注意細節(jié)。
對于顏色緩存,當使用動態(tài)顏色定義表時,最大的顏色數(shù)限制由indicator_colorN屬性決定。例如:
#property indicator_color1 DarkSalmon, DeepSkyBlue // we use 2 colors 然而,你可以象下面這樣做:
#roperty indicator_color1Red, Red, Red, Red, Red, Red, Red, Red, //… 你可以寫成:
#define C Red#property indicator_color1C, C, C, C, C, C, C, C, C, C, C, C, C, C, //… 一個緩存的最大顏色數(shù)是63。當顏色數(shù)超過最大值時,指標不能顯示。這是一個顯示風格的例子,使
緩存顏色表包涵最大顏色數(shù)是2,即使你動態(tài)設(shè)置了較大的顏色數(shù)。 你用顏色類型繪制指標時需注意一些細節(jié)。
總的來說,繪圖機會引人注目的增長了,非常了不起。
5、數(shù)組
在提到數(shù)組索引之前,有必要說明一下數(shù)據(jù)排序類型——AsSeries屬性。它不能由一些數(shù)組類型定義。
因此,需要的顏色數(shù)要寫在一行——屬性定義行。然后他們能動態(tài)改變。我發(fā)現(xiàn)的最短的顏色——“紅色”。
這個標識不能由多維和靜態(tài)數(shù)組設(shè)定。對于傳遞到OnCalculate函數(shù)的數(shù)組類型可以設(shè)置一個這樣的標識。使用CopyBuffer拷貝的數(shù)據(jù)不依靠AsSeries屬性。,但是,對于不同的緩存,執(zhí)行方式是不同的。
對于指標緩存,必須拷貝整個有效歷史縱深數(shù)據(jù)。必須記住這一點。
對于動態(tài)和靜態(tài)數(shù)組,數(shù)據(jù)拷貝方式是從現(xiàn)在拷貝到過去。
CopyBuffer函數(shù)根據(jù)需要改變動態(tài)緩存(除指標的)的大小。
不建議使用靜態(tài)數(shù)組拷貝數(shù)據(jù)。
總而言之,我建議你經(jīng)常檢查,怎樣拷貝數(shù)據(jù)、怎樣放置他們。最簡單安全的方法是: 為所有使用存儲歷史數(shù)據(jù)的緩存設(shè)置AsSeries屬性;
對于不同的緩存要注意他們的結(jié)構(gòu);
經(jīng)常檢查出錯代碼LastError的值。
另外,我強烈建議學習幫助文件,學習關(guān)于CopyBuffer函數(shù)和所有關(guān)于AsSeries屬性的函數(shù)的內(nèi)容。
6、IndicatorCounted函數(shù)
現(xiàn)在關(guān)于IndicatorCounted函數(shù)的辯論逐漸消失了,因為這個值被我們直接定義為上次函數(shù)調(diào)用的返回值。
通常總是返回當前函數(shù)調(diào)用所包涵的K線數(shù)rates_total值。
int OnCalculate(const int rates_total,// array size
const int prev_calculated,// bars processed after last call//...
)
{ return rates_total;}
然而,如果自從上次調(diào)用函數(shù)價格數(shù)據(jù)改變了(例如,歷史數(shù)據(jù)被調(diào)用或歷史數(shù)據(jù)空白被填充),那么輸入?yún)?shù)prev_calculated會被終端置為0值。 還有,如果OnCalculate函數(shù)返回0值,那么指標數(shù)據(jù)不會在終端的數(shù)據(jù)窗口中顯示。因此,如果你在調(diào)用歷史數(shù)據(jù)期間或通訊失敗后看到和執(zhí)行指標,需返回1而不是0。 另一個有用的特征是可以決定在指標中計算多少K線。這在EA中計算大量數(shù)據(jù)很有用。這個函數(shù)是BarsCalculated,它的細節(jié)在幫助文件中可以找到。
這個函數(shù)還有一個有用的應用。如果指標沒有被調(diào)用,調(diào)用過程可能要花一些時間——在建立指標句柄和使用它計算之間的時間。
初始化和預先計算需要時間。它由計算速度和指標細節(jié)決定。在這期間,CopyBuffer函數(shù)調(diào)用產(chǎn)生一個錯誤碼4806——“需要的數(shù)據(jù)沒有發(fā)現(xiàn)”。
BarsCalculated函數(shù)能用于決定指標數(shù)據(jù)拷貝是否有效。int WPRHandle = iWPR(NULL, 0, WPRPeriod);
int copied = CopyBuffer(WPRHandle, 0, 0, bars, Values);
int err = GetLastError();
if (-1 == copied) {
if (4806 == err){
for (int i = 0; i < 1000; ++i)
{
if (BarsCalculated(WPRHandle) > 0) break;
}
copied = CopyBuffer(WPRHandle, 0, 0, bars, Values);
err = GetLastError();
}
}
if (-1 == copied) {
Print("Error when trying to get WPR values, last error is ", err, " bars ", bars);
return 0; } //...
綜上所述,我想說在這篇文章里,只討論了一些細節(jié)。但我希望基本方面在這里已經(jīng)展現(xiàn)。如果這篇文章總能成為你找到細節(jié)信息的指引,那就太好了。如果你發(fā)現(xiàn)了文章中的任何錯誤,或發(fā)現(xiàn)了其他重要的東西,請通知我,我將試著改正并盡可能提高這篇文章。我正計劃列出最近的改變,除此以外,我希望在注釋里出現(xiàn)一些有用的信息。這是我們仔細閱讀它的原因。
附錄
Color.mqh –包涵文件,把這個文件拷貝到MQL5/Include文件夾。這個文件是Toned_WPR指標需要的。
Color.mq5 --庫文件,把這個文件拷貝到MQL5/Libraries文件夾。這個文件是Toned_WPR指標需要的
上面的文件都是指標。
ACKNOWLEDGMENTS
感謝
另外,我要感謝Mr. Victor Rustamov (granit77)閱讀手稿,有益的討論和建議。
概要 [ 此帖被bcsunwww在2009-11-01 00:02重新編輯 ]