步入新軌:MQL5的自定義指標
作者:MT4 來源:cxh99.com 發布時間:2012年05月28日
- 原文來自MQL5官方論壇:http://articles.mql4.com/865作者:Автор: Андрей中文翻譯:bcsunwww
--------------------------------------------------------
介紹
我們終于有機會試試新交易終端- MetaTrader 5了。毫無疑問,它是值得關注的,與它的前身相比,它有許多新的特性。這個平臺的重要優勢有: 編程語言上的根本修改,允許用面向對象的方法編程。還允許豐富的結構化編程。
代碼執行速度比MetaTrader 4快多了。
顯示必要信息的能力有了本質的增加。 新終端和編程語言的許多新特性和能力無法一一列出,一些新鮮的東西值得我們用專門的文章分別討論。暫時還沒有面向對象的編程代碼,對于開發者而言,還有很多重要的課題。在這篇文章里,我們研究一下指標,他們的結構、畫法、類型和編程細節等與MQL4的對比。這篇文章并不復雜,而且,這里研究的東西可以用后面的附件直接在終端里驗證。 1、一般的結構
與MQL4相比,指標的一般結構沒有改變。
象以前一樣,有三個函數:初始化函數、數據處理過程、卸載函數
象以前一樣,許多指標參數可以用屬性(#property)定義。他們有許多是專門為指標設計的,屬性和輸入參數象以前一樣,在全局背景下定義。
例如,讓我們看一下彩色RSI指標的執行。下面的代碼是刪節版,完整版在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;
這里是指標輸入參數和全局變量(不要和客戶終端全局變量搞混)。指標輸入參數用標識符“input”指定。
現在為指標設定枚舉是可能的了,有時對于避免選錯參數很有用。例如:可以用下拉框把AppliedPrice參數有效值列出。
因此,所有的枚舉,包括用戶定義的,將在同一下拉框中列出。例如下面的參數:
//...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是指標初始化函數。在這里我們配置指標緩存數和他們的屬性,并且定義指標變量(不能在屬性段定義或必須動態設定的)。還有原始數據的初始化,包括指標句柄的分配。
// 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是數據計算函數。這個函數有兩種形式。這里是它的標準形式: // the function is not obligatory in a code/*void OnDeinit(){}*/
OnDeinit是指標反初始化函數。通常為了釋放資源是必須的,如文件句柄。一些其他情況下不是必須的。
2、指標的兩種形式
第一種是標準形式,象我們在MQL4中用過的一樣,只有一點修改。OnCalculate函數替代了Start函數。標準形式象下面這樣:
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;
}
為了減少準備數據拷貝的代碼量,圖表數據作為數組直接傳入指標參數。此外,有效K線數作為函數的第一個參數,自上次調用或首次調用以來處理過的K線數作為第二個參數。 在第一次指標調用時這個參數為0值。這個參數是IndicatorCounted()函數的替代,這對于許多開發者來說是不方便的。 第二種形式是MQL4里i<…>OnArray類函數的替代和擴展。終端示例里有一個這種類型的指標——Custom Moving Average.這類指標是為用戶選擇的數據處理過程準備的,包括自定義指標。這類指標數據處理函數看起來象這樣:
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;}
最后一個參數是用戶選擇的待處理數據。如果你想使用一個有很多數據緩存的指標,第一個指標緩存將用做數據處理。
第一個指標的數據意味著這個指標將被應用到首先附加在選定圖表上的指標。
先前指標的數據意味著這個指標將被應用到最后附加在選定圖表上的指標。
這些指標可以用來組合所有堆棧。例如,用Custom Moving Average指標可以得到三重平滑均線,利用第一條指標數據進行處理,然后第二個指標平滑第一個,然后第三個指標平滑第二個:
有很多執行這種形式的標準指標。因此,當你看到applied_price_or_handle這樣的指標提示:
它表示epY指標的執行方式是利用客戶數據計算——數據句柄做為applied_price_or_handle參數傳送。
{ // ... RSIHandle = iRSI(NULL, 0, RSIPeriod, AppliedPrice);
SmoothHandle = iMA(NULL, 0, SmoothPeriod, 0, MODE_EMA, RSIHandle); // ...}
這種形式有另一種新應用——寫通用指標的能力。這樣指標的一個例子是附件中的Direction_Brush.mq5。
結果顯示在上圖中。這個例子中,趨勢顏色被分離到一個獨立的實體,在另一個指標中執行。
當然,他們的應用是有限制的,因為只能用在0緩存指標中。盡管如此,我認為這種指標是有用的。
另一方面,當你寫一個自定義指標時,你應該充分考慮它,因為在0緩存指標中主要信息處理允許避免在一個指標中執行多個功能。
粗略瀏覽一下,應用范圍并不像看起來那樣狹窄: 指標參數表著色(頂、方向、水平、片段等),包括風格形象;
不同條件下的不同信號;
收集并顯示統計數據——例如,數據分布;
通用指標的結構可以用一個緩存計算——例如,平均線、ZIGZAG。
上面提到的特征并不全面。我想許多另人印象深刻的設計以后會被發現。
3、存取數據
在MQL5中數據存取原則已經改變。重點變化在數組,做為一個結果,計算速度取得了引人注目的增長,現在,不必為每個值創建一個數組并調用iCustom函數了。取而代之的是,調用一個函數就可以得到必要的數據了,然后直接使用已拷貝到指定數組的想要的數據。 數據拷貝使用系統函數CopyBuffer執行。在幫助文件中可以找到這個函數的描述。
指標和靜態數組能拷貝的最大數據量由數組的大小決定。動態數組的大小在拷貝數據量超過它的大小時能夠改變。除此以外,還有許多存取歷史數據的函數: 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.
祥細描述在幫助文件里有。
這個數據只在指標的一種形式里傳遞,其他的有他們自己的形式。
基于歷史數據數組沒有必要雙重存在的事實,建議使用一個動態的無指標的緩存存儲數據。
4、指標緩存
指標緩存數沒有限制。
現在你沒有必要思考怎樣容納正確的信息、怎樣高效執行中間計算、如何創建指標組了。但是我們不能忘記緩存的存儲是需要內存的。因此,如果你指定了一個歷史縱深達1000000柱的終端并在一分鐘圖表上附加上一組“厚重的”指標組,那么,當終端吃掉上G的內存時你不要驚訝。 緩存的本質經歷了一些改變。使用的緩存數量在屬性段指定。#property indicator_buffers 2 // buffers used
這個值相當于緩存的數量。
顯示的緩存數量在屬性段設定:#property indicator_plots 1 // buffers displeyed
這里是些細節。許多繪制樣式只需要一個INDICATOR_DATA緩存來繪制。然而,有些樣式需要幾個指標緩存來繪制。
象下面的繪制樣式: DRAW_HISTOGRAM2 –需要2個指標緩存(HistogramSample.mq5)

DRAW_FILLING --需要2個指標緩存(CrossMa.mq5)
- ~
DRAW_BARS --需要4個指標緩存(BarsSample.mq5)
- ~
DRAW_CANDLES --需要4個指標緩存(CandleSample.mq5)
現在所有指標緩存可以分為三類:
INDICATOR_DATA –在圖表上顯示數據的緩存,這些緩存為制圖和iCustom而準備。他們需要事先聲明。在過于隨意的命令下,代碼編譯會成功,但附加到圖表時會失??;
INDICATOR_COLOR_INDEX –存儲顏色緩存。對于INDICATOR_DATA類型的彩色緩存索引的存儲是必需的有一個特殊的顏色類型。這樣的緩存(我們稱為顏色緩存)應該在使用它的主緩存后聲明;
INDICATOR_CALCULATIONS –這種類型緩存是為存儲輔助計算結果而準備的。他們不在圖表上顯示。
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指標可讀并可獲得必要數據,但V4不能;
象MQL4一樣,緩存不能做為中間計算,如果你想存取外部數據緩存,要聲明為INDICATOR_DATA。
對于不可能獲得的緩存請求,會產生錯誤碼4806。
讓我們探討一下顏色緩存的細節:
在MQL4中,為每一個顏色建立一個緩存是必須的,但現在用顏色樣式,你可以為一個緩存指定63種顏色。在某些情況下,可以優化使用的緩存數量,并節省內存。它也打開了書寫指標的新能力,特別在顯示風格方面。
另外,這種改革,在某些時候,與MQL4比,極大的簡化了幾種顏色的應用邏輯,最明顯的例子——用顏色區分趨勢。在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;}
更多的代碼,我們得到下面的圖:

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