面向初學者的 MQL4 語言系列之4——自定義指標

簡介

這是“面向初學者的 MQL4 語言”系列的第四篇文章。今天我們將學習編寫自定義指標。我們將熟悉指標特徵的分類,瞭解這些特徵如何影響指標,學習新函數和優化方法,最後就是編寫我們自己的指標。此外,本文末尾處提供了有關編程風格的建議。如果這是你閱讀的第一篇“面向初學者”文章,那你最好抽空讀讀之前的幾篇文章。此外,確保你已經正確理解了之前學過的材料,因爲本文不介紹基礎知識。



指標的類型

現在我將向你展示目前存在的指標種類。當然你已經看過不少指標了,不過現在我要你注意的是指標的特徵參數,所以我們要對特徵 和參數做一個小小的分類。它會幫助你編寫自定義指標。那麼,我們來看第一個簡單的指標:


它是移動平均線 (MA),一個廣泛使用的技術指標。注意以下重要情況:

  • 此指標在圖表窗口中繪製
  • 此指標僅顯示一個值
  • 此指標值的範圍是無限的,且取決於當前價格
  • 線條採用特定的顏色寬度樣式繪製(實線)

現在我們來看另一個指標:


它是威廉指標 (%R)。注意以下重要情況:

  • 此指標在單獨子窗口中繪製
  • 與上一種情況類似,此指標僅顯示一個值
  • 指標值範圍有嚴格的限制
  • 繪製的線條採用另一種樣式顏色寬度

因此,存在以下指標屬性:

  • 此指標在圖表窗口單獨子窗口中繪製。現在我們試着理解爲何在圖表上繪製移動平均線,而在單獨窗口中繪製威廉指標 (%R) 。區別就在於顯示值的範圍。注意,第二個指標顯示的值範圍爲 到 -100。現在想象一下將這些值顯示在圖表窗口中。會發生什麼情況??你看不到這條線,因爲價格範圍大大收窄。在我們的案例中,這個範圍是 0.6805 到 0.7495。但這還不是全部。實際上,價格是正數,而我們的值是負數。如果指標的值超出了活動圖表的價格範圍,那麼指標就會在單獨子窗口中繪製。如果指標值的範圍與活動圖表的價格範圍大致相同(例如不同種類的移動平均線),那麼指標就會在圖表窗口中繪製。以後便可根據這個簡單的邏輯設置此指標的參數。下面是一張圖片:

  • 在單獨子窗口中繪製的指標可能有嚴格限制的範圍。這意味着終端會設置一個固定的比例來顯示指標值;即便存在超出這個範圍的值,你也看不到它們。如果你禁用了此參數,終端會自動更改這個比例,以便包括相關指標的所有值。看下圖:

  • 指標會使用不同的顏色樣式寬度來顯示其值。對於這種情況,你在終端中設置指標繪製時應該就已經司空見慣了。這裏有一個限制:如果使用的線寬大於 1,那麼你只能使用一種線型 - 實線。

還有個指標:


如你所見,這個交易量指標是採用直方圖的形式繪製的。因此,有多種顯示指標值的方式。下面是另一個類型的示例:


指標分形是採用特殊符號繪製的。現在看看以下指標:


這個指標叫鱷魚指標。注意,這個指標同時繪製三個值(平衡線)。它是如何運作的?實際上,任何指標(有些例外情況,後文中再討論)在顯示值時都要使用數據緩衝區

數據緩衝區其實基本上就是個簡單的數組。其特殊性在於,這個數組的一部分是由終端進行管理的。終端會更換這個數組,每當收到一個新柱,就會進行一次輪換。這樣做的目的是讓每個數組元素都對應一個特定的柱。一個指標中顯示的數據緩衝區的最大數量是 8。現在這種方式看上去可能有些奇怪,但很快你就會明白除此以外別無他途。只需記住,鱷魚指標中的每條線都有一個對應的單獨數據緩衝區。每個數據緩衝區都有其自己的參數,終端就是根據這些參數繪製緩衝區的。我們的示例中有三個緩衝區,相關說明如下:

  1. 第一個緩衝區用寬度 3 的綠色實線繪製。
  2. 第二個緩衝區用寬度 1 的紅色虛線繪製。
  3. 第三個緩衝區用寬度 2 的藍色實線繪製。

無需爲每個指標繪製一個緩衝區。它可用於中間計算。這就是緩衝區數量可能大於你所見數量的原因。但數據緩衝區最重要的屬性是每個緩衝區元素都應對應圖表上的一個特定柱。只要記住這點就好。很快你就會看到它在代碼中的應用。

現在讓我們總結一下,爲我們這段小插曲畫上句點。任何指標都使用以下參數:

  • 一個或多個數據緩衝區(儘管並非必要),用於顯示其值或用於中間計算。每個循序輪換的緩衝區都有其自己的參數,其中定義了該緩衝區的繪製方式以及是否要繪製。例如,以直方圖、符號還是線條的形式繪製值;採用哪種顏色和線型;
  • 指標應繪製在何處(在圖表窗口中還是在子窗口中)。
  • 如果在子窗口中繪製指標,是否應限制範圍,或是否應自動調節比例。

確保你清楚地理解所有這些參數。現在我們將使用嚮導創建一個自定義指標。



創建自定義指標

啓動 MetaEditor,選擇文件 -> 新建


然後會顯示一個 Expert Advisor 嚮導 窗口,選擇自定義指標,單擊下一步


填寫字段名稱作者鏈接。到這裏一切都還平淡無奇,但現在你可以添加參數了。這是什麼?

參數是可由用戶設置的常用變量。重要的是,這些變量可用於指標代碼中。參數的應用顯而易見 - 讓用戶能夠設置指標操作的某些方面。可以是任何想要設置的方面。例如,要使用的時間範圍、操作模式、供計算平均值的柱數等。

舉個例子,讓我們試着添加一個參數,此參數將顯示計算指標值所處理的柱數。它可以用在什麼地方?試想一下,由於所做的計算太多,你的指標讓處理器嚴重過載。而你經常更改圖表的時間範圍並僅查看最新的 100 - 200 個柱。那麼你無需進行其他浪費時間的計算。這種情況下,此參數就能幫到你。當然,我們的指標中沒有會浪費計算機資源的複雜東西。這只是一個使用指標參數的形式。

要添加參數,單擊添加 (1)。之後可更改變量名稱 (2)。在我們的示例中,我們用它來替代 barsToProcess。你也可更改初始值 (3),即默認值。將其更改爲 100。此外你也可更改變量類型,但我們的示例中無需更改任何東西,因爲 int 類型就很契合我們的意圖。執行所有必要的更改後,單擊下一步


基本上這就搞定了。現在要說明的是應如何繪製指標:在單獨窗口還是圖表窗口中繪製。也可限制範圍。選中單獨窗口中的指標。以下是空字段指數(數據緩衝區)。此處可添加必要的數據緩衝區數(最多 8 個)。此外,以後可隨時添加或刪除緩衝區,更改代碼。單擊添加添加緩衝區。現在可更改繪製緩衝區的方式:線條直方圖截面箭頭。我們不更改任何東西,所以我們的類型是線條。設置顏色並單擊確定

這樣,你的第一個指標就完成了!好吧,它並沒有繪製任何東西,但它是代碼!帶源代碼的文件位於帶指標的文件夾內:MetaTrader4\experts\indicators



讓我們來逐行進行分析:

現在我們來看看 Meta Editor 創建的內容:

//+------------------------------------------------------------------+//|                                             myFirstIndicator.mq4 |//|                                                     Antonuk Oleg |//|                                                   [email protected] |//+------------------------------------------------------------------+

和往常一樣,開頭是一行註釋,內含你早先寫入的信息。下一段:

#property copyright "Antonuk Oleg"

還記得第二篇文章中的預處理器指令 #define 嗎?我們用它來聲明常量。這裏是另一個指令,用於表明指標的特定屬性。本例中它用於表明作者身份。請注意,它開頭使用了特殊符號 #,後跟關鍵字 property(沒有空格)。之後是我們要設置的具體屬性,本例中是版權 copyright,最後是此屬性的。本例中此值是包括你的姓名的行。可使用指令 #property 設置指標的很多特定方面。現在就讓我們來看一下。默認情況下,所有這些屬性都將進行設置。來看下一段代碼:

#property link      "[email protected]"

這個指令表達的是作者的聯繫方式。你可能會問這個信息(作者姓名和聯繫信息)在哪,因爲任何地方都看不到這個信息。其實它包括在可執行文件裏。如果你將此可執行文件以常見文本的格式打開查看,你就會看到這個信息:


下一段:

#property indicator_separate_window

這個指令是說指標必須在單獨子窗口中繪製。如你所見,這個指令和上一個不同,它不含額外的參數。

#property indicator_buffers 1

這個指令表明指標將使用多少個數據緩衝區。你可能已經注意到,指令在某些方面與常用函數很相像:它們也接受一些參數,並執行一些操作作爲響應。但這裏有個重要的差別:指令在一開始就執行了(在編譯之前)

#property indicator_color1 DarkOrchid

這個指令指示第一個緩衝區的默認顏色。注意,緩衝區的編號是從 1 開始的,不是從 0。試着記住這一點,以免後來產生混淆。這個顏色是使用衆多預定義名稱的其中一個來指定的。你可以在幫助中查看所有可用顏色的關鍵字:MQL4 參考 -> 標準常數 -> 網頁顏色。類似的,你也可以指示其他緩衝區的顏色,只要更改緩衝區編號就行了。

extern int       barsToProcess=100;

這是我們的指標參數。我們已經在嚮導中設置了這個參數。注意,它和常用變量的唯一區別就是關鍵字 extern 在變量類型的前面。這是指標啓動時,這個參數表現在用戶眼前的樣子:


下一段:

double ExtMapBuffer1[];

這是個常用數組。但沒有指示維數,也沒有執行初始化。這個數組之後將被設置爲一個數據緩衝區。

現在我們來聲明和描述函數。與常見腳本不同,每個指標有三個函數,而不是 1 個:

  • init() - 這個函數僅在我們啓動指標時由終端調用一次。其用途是讓指標做好操作準備,設置數據緩衝區,檢查參數(用戶編寫的內容)以及其他準備活動。此函數不是必要函數。如果沒有在此函數中執行代碼,你可以刪除它。
  • deinit() - 此函數在你從圖表中刪除指標時被調用,也僅調用一次。你應做好指標終止操作的準備。例如,關閉打開的文件,刪除文件中的圖形對象(別擔心,你會知道怎麼做的)。此函數也不是必要函數。
  • start() - 和在腳本中時不同,這個函數在指標中時,價格每變動一次,它就被調用一次。也即是說,噹噹前貨幣對的新報價顯示到指標連接的圖表時,就將調用此函數。此外,此函數是在指標啓動時調用的,即在調用函數 init() 之後。

我們來看看各個函數發生的情況:

int init()
{
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMapBuffer1);
 
   return(0);
}

這裏我們看到調用兩個重要函數以設置數據緩衝區:

SetIndexStyle(0,DRAW_LINE);

此函數設置如何繪製數據緩衝區。第一個參數指示要應用更改的緩衝區。請注意,這個函數(及類似函數)中,緩衝區的編號是從 0 開始的,而不像指令中從 1 開始。這很重要,務必小心。第二個參數指示如何繪製選定緩衝區。本例中我們使用的是常數 DRAW_LINE,它表示緩衝區將被繪製成一根線條。當然還有其他常數,我們之後再討論。

SetIndexBuffer(0,ExtMapBuffer1);

此函數將一個數組“綁定”到一個緩衝區編號。也就是說,此函數表示帶指定編號的緩衝區將使用指定數組存儲數據。所以,更改此數組的元素時,也會更改緩衝區的值。實際上數組就是個數據緩衝區。第一個參數是要綁定的數組的名稱

return(0);

函數執行結束,返回 0 - 初始化成功。

int deinit()
{
//----//----return(0);
}

取消初始化函數默認爲空。

int start()
{
   int counted_bars=IndicatorCounted();
//----//----return(0);
}

現在是最重要的函數 - 主要代碼就在這裏。注意:變量 counted_bars 已預先聲明,它由函數 IndicatorCounted() 進行初始化。此變量通常用於優化和加快指標操作,具體情況將在後文中進行分析。現在讓我們在指標窗口中繪製點東西。

完成指標

我們來決定要顯示的內容。指標將向我們顯示什麼內容?很簡單的東西。首先我們來繪製隨機數。爲什麼不呢?繪製隨機數可以保證 50% 的盈利信號。

讓我們在函數 init() 中寫入一段用於初始化隨機數發生器的代碼:

int init()
{
 
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMapBuffer1);
 
   // initialization of the generator of random numbersMathSrand(TimeLocal());
 
   return(0);
}

初始化就緒,接下來是函數 start()

int start()
{
   int counted_bars=IndicatorCounted();
 
   for(int i=0;i<Bars;i++)
   {
      ExtMapBuffer1[i]=MathRand()%1001;
   }
   
   return(0);
}

編譯 - F7。啓動終端,找到導航器面板,選擇自定義指標部分,然後雙擊我們指標的名稱:


此指標將被連接到活動圖表:


看,一切順利。現在讓我們看看代碼要做什麼工作:

for(int i=0;i<Bars;i++)

我們使用循環 for 以遍歷數據緩衝區的所有元素。由於每個特定柱對應緩衝區的各個元素,我們使用了循環,循環從零柱(最後一個可用柱)開始到第一個可用柱結束,每次循環都比變量Bars 少一個柱(因爲我們從 0 開始對柱計數)。

{ ExtMapBuffer1[i]=MathRand()%1001; }

每次迭代,計數器增加 1,在我們從最後一個可用柱移至第一個可用柱的同時,向各個緩衝區元素(與特定柱相對應)分配一個 0 到 1000 以內的隨機數。如果你覺得難以理解特定緩衝區元素如何對應特定柱,試着用以下方式更改循環,然後在終端中查看結果:

for(int i=0;i<Bars;i++) { ExtMapBuffer1[i]=i; }

現在指標將顯示各個柱的編號,我們來看一下:


我們看到,柱編號從最後一個柱開始到第一個柱結束一直在增大(從 0 到 Bars(條柱數))。希望現在你理解了緩衝區元素與圖表上的條柱的對應關係。

現在我們回到“隨機”指標的代碼這裏。如果你用這個指標用了至少幾分鐘時間,你會看到指標每次跳動都會繪製完全不同的圖表。也就是說,每次跳動都會重新計算上一次計算過的內容。這就給我們造成了不便,因爲我們甚至連上一次跳動時發生的情況都看不到。不過沒關係,因爲沒人會去使用這種指標 - 我們只是學習編寫它而已。還有一點。想象一下,你的指標要執行大量複雜計算,而僅僅計算一個柱就需要大量的處理器資源。這種情況下,如果出現新的價格,你的指標將計算每個可用柱的值,即便之前就已經算過。清楚了嗎?不是僅計算一次,是一次次反覆計算。消除此類不合理的資源浪費問題的過程,我們稱之爲優化

如何解決這個問題?通常我們用以下方式來解決。首先針對所有可用燭臺計算指標,然後僅在收到報價時,才僅針對最後一個燭臺重新計算該值。這個方法比較合理 - 沒有不必要的操作。現在我們來優化函數 start(),讓它執行以下操作:

int start()
{
   int counted_bars=IndicatorCounted(),
       limit;
 
   if(counted_bars>0)
      counted_bars--;
   
   limit=Bars-counted_bars;
  
   for(int i=0;i<limit;i++)
   {
      ExtMapBuffer1[i]=MathRand()%1001;
   }
   
   return(0);
}

讓我們來逐行進行分析:

int counted_bars=IndicatorCounted(),

我們聲明變量 counted_bars,此變量將存儲指標計算的柱數。實際上,函數 IndicatorCounted() 返回的是上一次調用函數 start() 之後未發生變化的柱數。所以,如果這是第一次調用 start(),IndicatorBars() 將返回 0,因爲所有柱對我們來說都是新的。如果不是第一次調用,只有最後一個柱發生了更改,那麼 IndicatorBars() 將返回一個等於 Bars-1 的數。

limit;

這是另一個變量,它將用作一個限制器,即幫助循環提前完成,忽略已計算的燭臺。

if(counted_bars>0) counted_bars--;

前面已經說過,如果 IndicatorCounted() 返回 0,則表示函數 start() 還是初次調用,所有柱對我們來說都是“新的”(未針對這些柱計算指標)。但如果不是第一次調用 start(),則將返回等於 Bars-1 的值。所以,這個條件與這種情況相關聯。之後我們將變量 counted_bars 減小 1。既然只有最後一個柱可被更改,那我們爲什麼還要這麼做?事實上,在某些情況下,上一個柱的最後一次價格變動會保持未被處理的狀態,因爲最後一次價格變動時,處理的是倒數第二次價格變動。自定義指標未被調用,也未被計算。所以我們要將變量 counted_bars 減 1,以消除這種情況。

limit=Bars-counted_bars;

這裏我們會將需要重新計算的最後一批柱的數量分配給變量 limit(限制器)。既然變量 counted_bars 存儲着已計算燭臺的數量,我們只需找到 Bars(可用柱總數)和 counted_bars 之間的差值,便可定義必須計算的燭臺數

for(int i=0;i<limit;i++)
{
   ExtMapBuffer1[i]=MathRand()%1001;
}

循環自身基本沒有變化。我們僅更改實施條件。現在,循環將在計算器 i 小於 limit 時執行。

優化到此結束。如果查看一下指標的更新版本,你會發現每當收到一個新的價格變動時,只有最後一個柱的值會發生變化。試着經常執行這種優化,即便你的指標並沒有執行復雜困難的計算。這是個高級優化方法。

還記得我們在嚮導中添加的指標參數 barsToProcess 嗎?現在就是用到它的時候了。我們只需要在循環前添加幾行:

int start()
{
   int counted_bars=IndicatorCounted(),
       limit;
 
   if(counted_bars>0)
      counted_bars--;
   
   limit=Bars-counted_bars;
   
   if(limit>barsToProcess)
      limit=barsToProcess;
  
   for(int i=0;i<limit;i++)
   {
      ExtMapBuffer1[i]=MathRand()%1001;
   }
   
   return(0);
}

看到了吧,一切都是那麼地簡單。我們檢查一下,看 limit 是否大於 barsToProcess。如果是,通過分配減小限制器。這樣一來,如果我們設置 barsToProcess=100,你就會看到以下圖片:


如你所見,僅計算我們設置的柱數。

我們的指標基本上準備就緒了。但我們還沒獲得進入市場的明確信號。所以我們需要進一步加大確定性。爲此我們將使用水平線

水平線是指標用特定樣式、顏色和寬度繪製的水平線條。這裏要注意的是,一個柱上的水平線最大數量爲 8。此外你可以使用指令函數設置水平線。如果要默認設置水平線,最好使用指令。要使水平線在指標操作期間發生動態變化,就用函數。那麼我們來設置兩個水平線:第一個在點 800 上,第二個在點 200 上。爲此,我們要在指標代碼開頭添加幾個指令:

//+------------------------------------------------------------------+//|                                             myFirstIndicator.mq4 |//|                                                     Antonuk Oleg |//|                                                   [email protected] |//+------------------------------------------------------------------+#property copyright "Antonuk Oleg"#property link      "[email protected]"#property indicator_level1 800.0#property indicator_level2 200.0#property indicator_levelcolor LimeGreen
#property indicator_levelwidth 2#property indicator_levelstyle 0#property indicator_separate_window

我們來分析這些新指令:

#property indicator_level1 800.0

這個指令表示水平線 1 應放在點 800.0 處。注意,緩衝區的編號從 1 開始,就像用於設置緩衝區的指令那樣。要設置另一個水平線,只需更改指令末尾的水平線編號:

#property indicator_level2 200.0

設置水平線的外部形式時,有一個重要的限制條件。你不能單獨設置各個水平線。所有設置都會完全應用到所有水平線。如果需要單獨設置各個水平線,應使用對象(完全不用水平線),具體會在下一篇文章中詳述。

#property indicator_levelcolor LimeGreen

此指令設置用於繪製所有水平線的顏色

#property indicator_levelwidth 2

此指令設置繪製所有水平線時所用的線寬。寬度可設範圍爲 到 5。別忘了,如果寬度大於 1,水平線將用實線繪製。如果需要另一種水平線繪製樣式,僅使用寬度 1。

#property indicator_levelstyle STYLE_SOLID

此指令設置繪製線條的樣式。有以下預設常量:

  • STYLE_SOLID - 實線
  • STYLE_DASH - 虛線
  • STYLE_DOT - 點線
  • STYLE_DASHDOT - 點劃線
  • STYLE_DASHDOTDOT - 雙點式點劃線

我們的“隨機”指標開發完畢了。現在讓我們用更恰當的名稱保存源文件 - randomIndicator.mq4。再次重新編輯源文件。這個指標在下一部分中也會用到。最終版本如下:


函數 iCustom

現在我們詳細說明一個非常有用的函數 - iCustom。它的作用是獲取任何自定義指標的值。記住,對於內置指標,我們要使用可以與上一篇文章中所述技術指標配合使用的函數(例如:iADX()、iMACD 等)。對於所有其他指標(自定義指標),使用函數 iCustom。此函數是通用函數,可以應用於任何滿足以下要求的自定義指標:

  • 指標採用可執行文件 (*.ex4) 格式編譯。
  • 指標在文件夾 MetaTrader 4\experts\indicators 中

函數原型具有以下格式:

double iCustom( string symbol, int timeframe, string name, ..., int mode, int shift);

參數:

  • symbol(交易品種) - 定義應採用哪種金融證券(貨幣對)計算自定義指標值。如果你需要當前(活動)的證券(圖表),則使用 NULL(或 0)。
  • timeframe(時間範圍) - 定義指標將用於哪個時間範圍(週期)。對當前時期使用 0 或以下常量之一(PERIOD_M1、PERIOD_M5、PERIOD_M15、PERIOD_M30、PERIOD_H1、PERIOD_H4、PERIOD_D1、PERIOD_W1、PERIOD_MN1)。
  • name(名稱) - 自定義指標的可執行文件名稱。只需指定名稱:切勿輸入擴展名 (.ex4) 或文件路徑 (experts/indicators/)。例如,如果自定義指標的可執行文件名稱爲“RandomIndicator.ex4”,你應該輸入“RandomIndicator”。這裏的寄存器無關緊要。意思就是輸入“RANDOMindicator”就可以了。
  • ...- 這裏你應該指定自定義指標參數的所有值。例如,我們的指標 RandomIndicator 中僅有一個參數 - barsToProcess。即在我們的示例中,我們在這裏輸入 100(或任何其他合適的值)。如果參數超過一個,將以它們在自定義指標中的聲明順序指定它們,並用逗號隔開。現在我們將嘗試根據此函數編寫一個指標,這樣你會理解地更深。
  • mode - 自定義指標的操作模式。實際上它是你要獲取的數據緩衝區的編號值。編號從 0 開始(與指令中不同)。如果自定義指標僅有一個數據緩衝區,此參數應等於 0。
  • shift - 定義自定義指標要應用到的柱。

使用示例:

ExtMapBuffer[0]=iCustom(NULL,PERIOD_H1,"Momentum",14,0,0);
 
// assign to the first element of the array ExtMapBuffer the value of the custom // indicator Momentum on the last available bar. We use here the active // security on hour chart. The name of the executable file: Momentum. // This indicator has only one parameter - period. In our case the period // is equal to 14. This indicator has only one data buffer, so we use zero, // in order to get access to its values.
double signalLast=iCustom("EURUSD",PERIOD_D1,"MACD",12,26,9,1,0);
 
// declare a new variable signalLast and assign to it the value of the custom // indicator индикатора MACD on the last available bar. We use the pair EURUSD on // a daily chart. The name of the executable file: MACD. This indicator has 3 parameters: // period for quick average, period for slow average and period for a signal line. // This indicator also has 2 data buffers. The first one is with values of the main line. The second one // with values of a signal line. In our case we take the value of the signal line.


信號指示器

現在我們將編寫另一個簡單指標。那麼,想象一下以下場景。你已經編寫了一個相當複雜的,帶有很多數據緩衝區的指標。其中不少數據緩衝區顯示在單獨窗口中,其他的用於中間計算。你瞭解買入和賣出的確切信號。但問題是很難跟蹤這些信號。你需要一直盯着監控器,試着找到水平線上方或下方的十字線。因此你決定再編寫一個指標來幫你做這個工作,並僅向你顯示入場信號。例如,這些信號可以是表明你要建倉的方向的箭頭。至於想要顯示信號指標的正確位置,那是不可能的!我們的情況比較簡單,但仍然和第一種情況類似。

我們將基於前一個指標 RandomIndicator 編寫一個信號指標。首先我們需要定義入場條件 - 這裏我們需要用到我們的水平線。那麼條件將如下:

  • 如果一條線移至上水平線 (800.0) 的上方,那就買入
  • 如果一條線移至下水平線 (200.0) 的下方,那就賣出

現在是時候編寫一個新的指標了。使用 Expert Advisor 嚮導創建一個新的自定義指標。像上一個例子中那樣,添加一個額外的參數:


最後一步(繪製自定義指標程序的屬性)應該是這樣的:


首先添加兩個將用於通過箭頭的形式繪製買入和賣出信號的數據緩衝區。將數據緩衝區的類型更改爲箭頭。更改顏色和符號代碼。以下是所有可用符號代碼:

我們不需要在單獨窗口中繪製指標,因爲我們要在圖表窗口中繪製信號。

我們使用兩個數據緩衝區,因爲我們無法僅使用一個緩衝區繪製不同的箭頭(符號)。每個以符號形式顯示的數據緩衝區都僅可用一個符號繪製。現在讓我們集中注意力,認真分析一下指標初始化代碼:

int init()
{
//---- indicatorsSetIndexStyle(0,DRAW_ARROW);
   SetIndexArrow(0,236);
   SetIndexBuffer(0,ExtMapBuffer1);
   SetIndexEmptyValue(0,0.0);
   SetIndexStyle(1,DRAW_ARROW);
   SetIndexArrow(1,238);
   SetIndexBuffer(1,ExtMapBuffer2);
   SetIndexEmptyValue(1,0.0);
//----return(0);
}

注意,現在針對數據緩衝區繪製類型使用了另一個常數 - DRAW_ARROW

SetIndexStyle(0,DRAW_ARROW);

我們還看到兩個用於設置符號繪製的新函數。SetIndexArrow 用於設置什麼符號將用來代表緩衝區。第一個參數是緩衝區編號,第二個是代表指標的符號代碼

SetIndexArrow(0,236);

SetIndexEmptyValue 用於指示“空”值。這表示我們要指定一個值,出現這個值時,無需繪製任何東西。這個函數在我們的示例中很好用,因爲不會在每個柱上都生成信號。它的工作方式如下:當我們不需要在當前柱上繪製數組時,你可以向對應的數據緩衝區元素分配一個“空”值,在我們的例子中,這個值是 0。此函數的第一個參數是數據緩衝區的編號。第二個是“空”值

SetIndexEmptyValue(0,0.0);

剩餘的初始化代碼設置緩衝區的方式類似於我們早先分析過的“隨機”指標。現在我們來完成函數 start() 中的代碼。

int start()
{
   int counted_bars=IndicatorCounted(),
       limit;
 
   if(counted_bars>0)
      counted_bars--;
   
   limit=Bars-counted_bars;
   
   if(limit>barsToProcess)
      limit=barsToProcess;
  
   for(int i=0;i<limit;i++)
   {
      double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i);
      
      if(randomValue>800.0)
         ExtMapBuffer1[i]=High[i]+5*Point;
      else
         ExtMapBuffer1[i]=0.0;
         
      if(randomValue<200.0)
         ExtMapBuffer2[i]=Low[i]-5*Point;         
      else
         ExtMapBuffer2[i]=0.0;         
   }
   
   return(0);
}

從“隨機”指標直至循環的整個代碼將重複執行。這段代碼實際上是任何指標中的標準代碼,一直在重複使用,最多進行一些細微的更改。現在我們來詳細分析這個循環:

   for(int i=0;i<limit;i++)
   {
      double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i);
      
      if(randomValue>800.0)
         ExtMapBuffer1[i]=High[i]+5*Point;
      else
         ExtMapBuffer1[i]=0.0;
         
      if(randomValue<200.0)
         ExtMapBuffer2[i]=Low[i]-5*Point;         
      else
         ExtMapBuffer2[i]=0.0;         
   }

首先我們聲明變量 randomValue(隨機值)並向其分配我們的“隨機”指標在當前柱上的值。爲此我們使用了函數 iCustom

// get the value of the "random" indicator on the i-th bar. Use the active chart on the current period// The name of the executable file of indicator: RandomIndicator. Single parameter of "random" indicator // is number of bars for calculation. In our indicator there is also analogous variable, that is why // we use it. In "random" indicator only 1 data buffer, so we use 0, for getting// access to its values.

如果“隨機”指標的值在上水平線 (800) 上方,就表示是買入信號:

if(randomValue>800.0)
   ExtMapBuffer1[i]=High[i]+5*Point;
// if there is signal to buy, assign to current element of data buffer the highest
// value of the current bar. Besides add 5 points, so that the arrow were a little higher 
// than the current price. The predetermined variable Point is used to get automatically
// a multiplier for presenting points. Otherwise we would have to write something like
// this: ExtMapBuffer1[i]=High[i]+0.0005; 

否則,如果沒有買入信號:

else
   ExtMapBuffer1[i]=0.0;
 
// if no Buy signal, assign to the current element of data
// buffer "empty" value, which is equal to 0.0.
// Now no symbol will be shown on this bar.

如果“隨機”指標的值在下水平線 (200) 下方,就表示是賣出信號:

if(randomValue<200.0)
   ExtMapBuffer2[i]=Low[i]-5*Point;
 
// if it is signal to sell, assign to the current element of data buffer the lowest
// value of the current bar. Besides diminish the value by 5 points, so that the arrow were 
// a little lower than the current price.

否則,如果沒有賣出信號:

else
   ExtMapBuffer2[i]=0.0;
 
// if no Sell signal, assign to the current element of data
// buffer "empty" value. Now no symbol will be shown on this bar.

這就是循環。編譯指標並在終端中啓動它:




關於風格

我說的可不是領帶與外套和襯衫的那種搭配風格,儘管那種風格總是那麼符合潮流。如果你寫代碼不是爲了自用自看,那麼編程風格就非常重要。實際上,每個開發人員都有其自己的編程風格。每個人都用自己的方式設計循環,使用不同的縮進(或根本不使用縮進),聲明變量等。你應該找到屬於你自己的,今後一直會使用的編程風格。我想給你幾個建議,幫你把代碼寫的易讀易懂:

  • 不要在一行內編寫很多操作(用分號隔開)(;)
  • 用英語編寫變量名和函數名
  • 在變量名中,使用大寫字母作爲分隔符
  • 變量名和函數名中避免過多使用縮寫和縮略詞
  • 縮進一定的長度,使代碼塊均等
  • 在各個(循環或條件的)新主體中執行額外的縮進
  • 將同一類型的變量進行分組
  • 對較長較難的代碼塊進行適當的註釋
  • 對你編寫的函數(其分配、參數)進行適當的註釋

總結

今天你學到了一些新東西。你編寫了兩個簡單的指標。好吧,它們並沒什麼用處,但我又不是在教你怎麼成功交易!你已經瞭解了指標的操作方式以及指標擁有的參數和屬性。你還學到了如何設置緩衝區和使用它們。你認識了好幾個新函數。函數 iCustom 非常重要,即使在 Expert Advisor 中也會進一步用到。如果遇到難題,再讀一遍本文,試着加深理解。如果還有問題,請馬上在論壇提出或寫下對本文的評論。


原文鏈接:https://www.mql5.com/zh/articles/1500

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章