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

簡介

這是“面向初學者的 MQL4 語言”系列的第五篇文章。今天我們將學習使用圖形對象,這是個非常強大的開發工具,可以大幅拓寬指標的應用範圍。此外,它們可以用於腳本和 Expert Advisor。我們將學習創建對象,更改其參數以及檢查錯誤。當然,我不可能詳細地描述所有對象,那也太多了。但你會獲得讓你能夠自主學習這些對象的所有必要知識。本文還包含一個逐步引導你創建複雜信號指標的示例。於此基礎上,你可以創建任意信號指標,爲多個指標顯示所有周期上的交易信號。在這裏示例中,很多參數都是可調整的,這樣就能輕鬆更改指標的外觀。



關於圖形對象

使用 MetaTrader 4 終端時,你經常會和它們打交道。圖形對象有很多用途。交易者可以設置支撐位和阻力位、樞軸點、斐波納契位等。我們來看一個簡單的對象用例:

四個圖形對象連接到此圖表:

  • 兩條水平線
  • 一個文本對象
  • 一個對象符號(箭頭)

今天我們要學習使用 MQL4 連接此類對象。想象一下,有多少手動操作可以通過使用對象實現自動化!舉個例子,你曾手動計算過樞軸點、支撐位和阻力位,然後再手動繪製它們嗎?好吧,這個工作量並不算多,但如果在 MQL4 中自動化這個流程,終端會自動計算並繪製對應的價位。你要做的就是雙擊腳本名稱,然後坐等結果。此外,你可以使用圖形對象編寫非常有用的信號指標。



處理對象的概念

在 MQL4 中處理所有圖形對象的算法如下:

  • 創建對象
  • 修改其參數(移動、更改顏色、板框等)
  • 刪除對象

這就是一個特定的“生命週期”。現在我們來詳細說明各個階段。



創建圖形對象

要繪製任何圖形對象,都會用到通用函數 ObjectCreate()。以下是其原型:

bool ObjectCreate(string name, int type, int window, datetime time1, 
                  double price1, datetime time2=0,double price2=0, 
                  datetime time3=0, double price3=0)

如果一切正常,此函數返回true,如果無法創建對象或出現錯誤,則返回 false要找出錯誤代碼,使用函數 GetLastError():

if(ObjectCreate(/* arguments */)==false)
{
   // an error occurred, its code should be recorded into a journal
   Print("Error of calling ObjectCreate():",GetLastError());
}

我們要錯誤代碼做什麼?它會幫助你找到錯誤說明,或可消除錯誤。所有代碼說明都包含在:MQL4 參考 -> 標準常數 -> 錯誤代碼中

我們來仔細看看函數 ObjectCreate() 的所有參數:

  • name- 對象的唯一名稱。不可用相同名稱創建兩個對象。此外,此名稱將在其他函數中用於更改相關對象表徵的參數或移動該對象。
  • type - 對象類型。可創建的所有對象類型都包含在:MQL4 參考 -> 標準常數 -> 對象類型中。注意,是否應使用最後一個函數參數取決於對象類型。再看一遍原型。最後四個參數的值是默認分配的:不同的對象在創建時需要不同的數據量。這很簡單。假設你需要繪製一個點。你需要什麼信息?很明顯,需要點的位置。這就夠了,不是嗎?要繪製一個矩形,我們需要左上角的點和右下角的點的位置。函數 ObjectCreate() 的情況也是如此。這是共通的。所以,它需要一個點的位置來繪製一根水平線,以及兩個點的位置來繪製一個線段。要繪製三角形,則需要三個點。所以我們建議你在創建對象時要正確找出繪製時所需的點數。
  • window - 繪製對象時所在窗口的編號。如需在圖表(即主窗口)上繪製對象,使用0 作爲窗口編號。
  • time1 - 第一個點的 X 座標。終端中的 X 軸顯示時間,所以要在這裏指示時間值。例如,要找出最後一個可用柱的時間,可以使用預定義數組 Time[],具體就是:Time[0]。
  • price1- 第一個點的 Y 座標。終端中的 Y 軸顯示價格,所以要使用價格值。例如,使用預定義數組 Open[]、Close[] 等。
  • other arguments 指兩對類似的座標,用於定義繪製更復雜的對象時所需的點。如果對象很簡單,就無需使用這些參數。



創建對象的示例。繪製線條

現在,爲了更好地理解,我們來繪製幾個線條。我們標記最後一天的最低價和最高價。首先我們需要創建一個新腳本,並更改函數 start():

int  start()
{
   double price=iHigh(Symbol(),PERIOD_D1,0);
   // this useful function returns the maximal price for:
   // * specified security, in our case it is Symbol() - 
   //   active security
   // * specified period, in our case it is PERIOD_D1 (daily)
   // * specified bar, in our case it is 0, the last bar
 
   ObjectCreate("highLine",OBJ_HLINE,0,0,price);
   // let us view all parameters: 
   // "highLine" - the unique object name
   // OBJ_HLINE - object type of the horizontal line
   // 0 - the object is drawn in the main window (chart window)
   // 0 - X coordinate (time), it shouldn't be indicated, because
   //     we are drawing a horizontal line
   // price - Y coordinate (price). It is the maximal price
   
   price=iLow(Symbol(),PERIOD_D1,0);
   // the function is identical with iHigh in arguments, but it returns
   // the minimal price
   
   ObjectCreate("lowLine",OBJ_HLINE,0,0,price);
 
   return(0);
}

當然我們已經忽略了錯誤檢查步驟。所以要是你給兩個對象取了同一個名稱,那可不能怪我。啓動腳本後,顯示如下:

線條是繪製了,但有個地方我不太喜歡。紅色太深了,所以建議使用淺色調。一般來說可以設置線條外觀。



修改對象屬性。設置線條外觀

有一個特殊函數可用於設置已創建圖形對象的參數。這個函數就是ObjectSet()。其原型如下:

bool ObjectSet( string name, int index, double value);

和上一個函數類似,如果一切正常,返回true,如果無法創建對象或出現錯誤,則返回false。例如你指定了一個不存在的對象名稱。我們來看看此函數的參數:

  • name - 已創建對象的名稱。開始修改之前,確保有使用這個名稱的對象存在。
  • index - 要修改的對象屬性的索引。所有索引都可在以下位置找到:MQL4 參考 -> 標準常數 -> 對象屬性。此函數也是通用函數。它的工作原理如下:你指定要修改的屬性以及要分配給此屬性的值。
  • value - 選定屬性應更改至的目標值。例如,如果你要更改顏色,那就在這裏指定一個新顏色。

現在讓我們更改我們的線條,即其顏色、線寬和樣式。更改start() 函數:

int  start()
{
   double price=iHigh(Symbol(),PERIOD_D1,0);
 
   ObjectCreate("highLine",OBJ_HLINE,0,0,price);
   price=iLow(Symbol(),PERIOD_D1,0);
   ObjectCreate("lowLine",OBJ_HLINE,0,0,price);
   
   ObjectSet("highLine",OBJPROP_COLOR,LimeGreen);
   // changing the color of the upper line
   ObjectSet("highLine",OBJPROP_WIDTH,3);
   // now the line will be 3 pixel wide
   
   ObjectSet("lowLine",OBJPROP_COLOR,Crimson);
   // changing the color of the lower line
   ObjectSet("lowLine",OBJPROP_STYLE,STYLE_DOT);
   // now the lower line will be dashed   
 
   return(0);
}

你會在圖表上看到以下內容:



刪除對象

你會經常需要刪除舊的或不需要的對象。以下幾個函數就能實現這個目的:

bool ObjectDelete(string name);

此函數刪除使用指定名稱的對象。如果指示了一個不存在的名稱,則返回“false”。

int ObjectsDeleteAll(int window=EMPTY,int type=EMPTY);

這是一個高級函數,它返回已刪除對象的數量。它還有默認值。如果沒有指定任何參數,終端將刪除活動圖表的所有對象:

ObjectsDeleteAll();
// deleting all objects

如果你在一個子窗口(例如在某個指標的窗口中)中創建了一個對象,可通過在第一個參數中指定此窗口的編號來刪除其所有對象。子窗口稍後會再討論,所以現在我們在第一個參數中指示 0。

如果需要刪除某特定類型的所有對象,那就在第二個參數中指定此類型:

ObjectsDeleteAll(0, OBJ_ARROW);
// deleting all arrows


如何正確使用所有這些東西?

你可能認爲你需要很多知識才能把這些都運用自如。例如,應瞭解對象的所有這些屬性和類型。但是事實上並非如此。所有東西都能在“用戶指南”中找到。

首先打開工具箱 (CTRL+T)。底部有數個選項卡,選擇幫助。假設你需要繪製一個圖形對象,但不知道該怎麼做。此時應使用函數ObjectCreate() 。寫入此函數,將參數留空。現在將光標放在函數名稱內,然後按 F1。“幫助”窗口將顯示關於此函數的信息。這意味着你無需搜索任何東西。現在來看函數說明。函數說明後面是其所有參數的說明。注意參數 type(類型)的說明:

它包含一個鏈接。單擊此鏈接,便可看到現有對象的列表。假設你想要繪製一個橢圓形:

閱讀說明,你會發現需要兩個座標。讓我們開始吧:

int  start()
{
   ObjectCreate("ellipse",OBJ_ELLIPSE,0,Time[100],Low[100],Time[0],High[0]);
   // indicate 2 points for creating an ellipse:
   // * 1st - lower left point
   // * 2nd - upper right point 
 
   return(0);
}

我們也會看到,屬性OBJPROP_SCALE決定了邊的關聯性。所以,如果我們將其設爲 1,我們會獲得一個圓形:

int  start()
{
   ObjectsDeleteAll();
   // clear the chart before drawing
   
   ObjectCreate("ellipse",OBJ_ELLIPSE,0,Time[100],Low[100],Time[0],High[0]);
   
   ObjectSet("ellipse",OBJPROP_SCALE,1.0);
   // change the correlation of sides
   ObjectSet("ellipse",OBJPROP_COLOR,Gold);
   // change the color
 
   return(0);
}

我可以肯定你也不想畫個圓圈,因爲 1:1 比例應該在圖表屬性中設置(右鍵單擊圖表任意空白處,並選擇屬性):

看,一切都很簡單。實際上你可以將光標放在任何關鍵字上,並按F1,之後就會看到“幫助”中的對應信息。所以你無需記住所有類型和屬性的名稱,使用內置”幫助“便可快速有效地編寫代碼。MetaEditor 還有一個非常重要的屬性,可以幫助你編寫代碼:在內置函數中編寫參數時,按 CTRL + SHIFT + 空格鍵。你會看到相關提示和函數原型:



在子窗口中創建圖形對象

如果你需要在子窗口(例如在自定義指標的窗口)中繪製圖形對象,你應該要知道其編號。舉個例子,我們將編寫一個簡單的指標,用其在單獨窗口中繪製一條水平線。創建一個自定義指標並在代碼中添加以下內容:

//+------------------------------------------------------------------+
//|                                   creatingObjectsInSubWindow.mq4 |
//|                                                     Antonuk Oleg |
//|                                            [email protected] |
//+------------------------------------------------------------------+
#property copyright "Antonuk Oleg"
#property link      "[email protected]"
 
#property indicator_separate_window
// indicator will be written in a separate window
#property indicator_minimum 1
// minimal indicator value is 1
#property indicator_maximum 10
// maximal is 10
 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   IndicatorShortName("NiceLine");
   // this simple function sets a short indicator name,
   // you see it in the upper left corner of any indicator.
   // What for do we need it? The function WindowFind searches a subwindow
   // with a specified short name and returns its number.
 
   int windowIndex=WindowFind("NiceLine");
   // finding the window number of our indicator
   
   if(windowIndex<0)
   {
      // if the number is -1, there is an error
      Print("Can\'t find window");
      return(0);
   }  
 
   ObjectCreate("line",OBJ_HLINE,windowIndex,0,5.0);
   // drawing a line in the indicator subwindow
               
   ObjectSet("line",OBJPROP_COLOR,GreenYellow);
   ObjectSet("line",OBJPROP_WIDTH,3);
 
   WindowRedraw();      
   // redraw the window to see the line
 
   return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
   ObjectsDeleteAll();
   // delete all objects
   
   return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
   return(0);
}

啓動指標。沒看到水平線!

我們需要更改圖表週期。

現在能看到了。發生了什麼事?事實上,如果函數 init() 是首次啓動, 那麼函數內是找不到子窗口編號的。原因也許是終端在初始化期間還尚未創建子窗口吧。有一種方法可以避免這種情況 - 創建窗口後,所有操作都在函數 start()中執行,如下所示:

//+------------------------------------------------------------------+
//|                                   creatingObjectsInSubWindow.mq4 |
//|                                                     Antonuk Oleg |
//|                                            [email protected] |
//+------------------------------------------------------------------+
#property copyright "Antonuk Oleg"
#property link      "[email protected]"
 
#property indicator_separate_window
#property indicator_minimum 1
#property indicator_maximum 10
 
bool initFinished=false;
// adding a variable that will remember the initialization state.
// false - there was no initialization
// true - there was initialization
 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
   ObjectsDeleteAll();
   // deleting all objects
   
   return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
   if(initFinished==false)
   {
      IndicatorShortName("NiceLine");
 
      int windowIndex=WindowFind("NiceLine");
   
      if(windowIndex<0)
      {
         // if the subwindow number is -1, there is an error
         Print("Can\'t find window");
         return(0);
      }  
 
      ObjectCreate("line",OBJ_HLINE,windowIndex,0,5.0);
      // drawing a line in the indicator subwindow
               
      ObjectSet("line",OBJPROP_COLOR,GreenYellow);
      ObjectSet("line",OBJPROP_WIDTH,3);
 
      WindowRedraw();      
      // redraw the window to see the line   
      
      initFinished=true;
      // drawing is finished
   }
   
   return(0);
}

現在所有東西都將第一次開始繪製。這裏你應該記住的是,子窗口編號是在函數 start() 中找出的,而不是 init()。



做一些練習

試着使用“幫助”學習一些新的圖形對象類型。之後,編寫一個可以繪製它們並設置參數的腳本。把這個研究清楚,做一些練習,之後再繼續閱讀本文。



編寫一個信號指標。這是什麼?

想象一下這個情景。交易者使用多個指標來做入市的相關決策:移動平均線、拋物線轉向指標和威廉指標。這些是內置指標,見下圖:

交易者不斷通過以下方式評估市場情況:當三個指標之一發出信號時,就是進場的時候。

  • 如果快速移動平均線在慢速移動平均線的上方,就是一個買入信號。反之就是賣出信號。
  • 如果價格在拋物線轉向指標的下方,就是一個賣出信號。反之就是買入信號。
  • 如果 WPR 大於 -20,就是一個買入信號。如果 WPR 小於 -80,就是一個賣出信號。

交易者必須不斷檢查所有條件,還要試着跟蹤多個週期的情況。這是個繁重的工作。所以,一個可以執行所有檢查的信號指標可以幫到他:

今天我們將學會解決這個問題。我們將編寫一個信號指標,你可以很輕鬆地設置它。此外,你可以以此爲基礎輕鬆自行修改你喜愛的指標。



基礎知識

創建此指標時,我們將面臨一些繪製方面的問題。所有圖形對象都是用價格座標和時間座標繪製的。因此,繪製內容一直在變化。爲了使對象保持在一個位置,我們需要不斷更改其座標。但如果你要查看先前的內容並移動圖表位置,信號表也會移位。然而凡事皆有例外。圖形對象中,有一個對象名爲 OBJ_LABEL。它是一個文本標記,但它定位的不是價格和時間,而是像素形式的窗口座標。這很簡單:

我們看到一個常見的文本符號“X”。在其參數中,你可以看到它的座標是用像素來指定的。像素就是屏幕上最小的點。注意,左上角的座標是:x=0, y=0 (0,0)。如果我們增大 x,對象將向右移動,如果我們減小它,則對象向左移動。y座標也是如此。它可以向上向下移動。務必瞭解並記住這個原則。要進行實踐,可以創建一個標記並移動它的位置,看它的座標在屬性中的變化情況。你還可以通過移動圖表位置來查看舊報價。此時標記不會移位。我們可以使用此類標記創建信號指標,而不會有上述不利之處。



文本標記的選項

我們的信號指標將僅使用文本標記。那麼我們來詳細說說這些指標的選項。首先,創建一個新指標(不使用數據緩衝區和參數),並更改函數 init():

int init()
{
   // now we will crate a text mark.
   // for this use the function ObjectCreate.
   // do not indicate coordinates
   ObjectCreate("signal",OBJ_LABEL,0,0,0,0,0);
 
   // change the x-coordinate
   ObjectSet("signal",OBJPROP_XDISTANCE,50);
 
   // change the y-coordinate
   ObjectSet("signal",OBJPROP_YDISTANCE,50);
 
   // to indicate the mark text, use the following function
   ObjectSetText("signal","lambada",14,"Tahoma",Gold);
   // "signal" - object name
   // "lambada" - text
   // 14 - font size
   // Gold - color
 
   return(0);
}

看,一切都很簡單。ObjectCreate()函數將僅在初始化中用於創建所有必要的對象。我們可以根據函數 start() 中每次價格變動,使用 ObjectSetText() 更改對象的外觀。start()。我們還需要更改函數 deinit():

int deinit()
{
   // when deleting the indicator delete all objects
   ObjectsDeleteAll();
 
   return(0);
}

現在啓動指標並查看結果:

我們將使用標記的以下選項:

  • 將字體更改爲 Wingdings,以便能夠使用特殊符號(方塊、圓圈、笑臉等等):

  • 我們將更改標記的顏色和文本
  • 我們將更改標記的位置和大小


使用字體 Wingdings

讓我們用 Wingdings 字體創建一個標記。更改init()函數:

int init()
{
 
   ObjectCreate("signal",OBJ_LABEL,0,0,0,0,0);
   ObjectSet("signal",OBJPROP_XDISTANCE,50);
   ObjectSet("signal",OBJPROP_YDISTANCE,50);
 
   // use symbols from the Wingdings font
   ObjectSetText("signal",CharToStr(164),60,"Wingdings",Gold);
   // CharToStr() - this function returns a line with a single
   // symbol, the code of which is specified in the single argument.
   // Simply select a symbol from the table above and write
   // its number into this function
   // 60 - use large font
   // "Wingdings" - use font Wingdings
 
   return(0);
}

結果如下:



繪製信號表的模型

現在讓我們繪製一個信號表的模型。實際上這個模型是大量的方塊組成的:

int init()
{
   // use 2 cycles. The first cycle, with the counter "x" draws one by one
   // each column from left to wright. The second cycle draws symbols of each
   // column from top downward. At each iteration the cycle will create a mark.
   // These 2 cycles create 9 columns (9 periods) 3 marks each (3 signal types).
   for(intx=0;x<9;x++)
      for(inty=0;y<3;y++)
      {
         ObjectCreate("signal"+x+y,OBJ_LABEL,0,0,0,0,0);
         // create the next mark, Note that the mark name
         // is created "on the fly" and depends on "x" and "y" counters
 
         ObjectSet("signal"+x+y,OBJPROP_XDISTANCE,x*20);
         // change the X coordinate.
         // x*20 - each mark is created at the interval of 20 pixels
         // horizontally and directly depends on the "x" counter
 
         ObjectSet("signal"+x+y,OBJPROP_YDISTANCE,y*20);
         // change the Y coordinate.
         // y*20 - each mark is created at the interval of 20 pixels
         // vertically and directly depends on the "y" counter
 
         ObjectSetText("signal"+x+y,CharToStr(110),20,"Wingdings",Gold);
         // use the 110th symbol code (square)
      }
   
   return(0);
}

模式準備就緒。我們來添加左方和上方的縮進,以便可以看到終端文本:

int init()
{
   for(int x=0;x<9;x++)
      for(int y=0;y<3;y++)
      {
         ObjectCreate("signal"+x+y,OBJ_LABEL,0,0,0,0,0);
         ObjectSet("signal"+x+y,OBJPROP_XDISTANCE,x*20+12);
         // adding a horizontal indent 12 pixels
         ObjectSet("signal"+x+y,OBJPROP_YDISTANCE,y*20+20);
         // adding a vertical indent 20 pixels
         ObjectSetText("signal"+x+y,CharToStr(110),20,"Wingdings",Gold);
      }
 
   return(0);
}


激活此模式

現在讓我們對其中至少一個方塊進行操作。假設左上角的方塊將在分鐘時間範圍 (M1) 上顯示一個移動平均線信號。如果這是個買入信號,那麼這個方塊將變爲綠色。如果是賣出信號,則變爲紅色。我們需要更改函數start():

int start()
{
   // if quick moving average (period - 13) is larger than the slow one,
   // this is a signal to buy. Check the last bar
   if(iMA(Symbol(),1,13,0,0,0,0)>iMA(Symbol(),1,24,0,0,0,0))
      ObjectSetText("signal00",CharToStr(110),20,"Wingdings",YellowGreen);
   // change the color of the mark named "signal00" (the upper left)
   // into green
 
   else
   // else, if the quick MA is smaller than the slow one, this is a signal to sell.
      ObjectSetText("signal00",CharToStr(110),20,"Wingdings",Tomato); 
      // change the color into red
 
   return(0);
}



激活上行

我們繼續進行激活。左方塊指示最小的時間範圍 - M1。現在我們要讓每個方塊指示的時間範圍都大於上一個方塊。所以,第二個方塊顯示 M5 上的信號,第三個方塊顯示 M15 上的信號,以此類推,直至 MN1。當然,這些工作都將在循環中完成。所要更改的內容是名稱和週期。我們有 0 個方塊,所以我們使用 1 個計數器。但我們面臨一個與週期相關的問題,就是週期的變化毫無規律可言。看:

有人會認爲,既然沒有什麼規律,就不能使用循環。並非如此。我們需要做的只是在指標代碼開頭聲明一個特殊數組:

//////////////////////////////////////////////////////////////////////
//
//                                                  signalTable.mq4 
//                                                     Antonuk Oleg 
//                                            [email protected] 
//
//////////////////////////////////////////////////////////////////////
#property copyright "Antonuk Oleg"
#property link      "[email protected]"
 
#property indicator_chart_window
 
intperiod[]={1,5,15,30,60,240,1440,10080,43200};

所有周期都已記錄到這個數組中,現在可以很輕鬆地在循環中使用它們:

int start()
{
   // use a cycle to activate all squares of the first line
   for(int x=0;x<9;x++)
   {
      if(iMA(Symbol(),period[x],13,0,0,0,0)>iMA(Symbol(),period[x],24,0,0,0,0))
         ObjectSetText("signal"+x+"0",CharToStr(110),20,"Wingdings",YellowGreen);
         // "signal"+x+"0" - create a mark name dynamically depending on
         // the counter "x"
      else
         ObjectSetText("signal"+x+"0",CharToStr(110),20,"Wingdings",Tomato); 
   }
 
   return(0);
}

我們將數組 period[] 用作“X”計數器和週期的對應表。想象一下,如果沒有這個小小的數組,我們需要寫多少代碼!好了,第一行信號方塊就緒,如下所示:



添加文字

一切正常,不過弄清楚方塊的時間範圍有點難,所以我們要創建說明性簽名。我們還將使用一個對應性數組,它們將存儲各列的文字:

#property indicator_chart_window
 
int period[]={1,5,15,30,60,240,1440,10080,43200};  
 
stringperiodString[]={"M1","M5","M15","M30","H1","H4","D1","W1","MN1"};

將通過以下循環在init()中創建這些文字:

int init()
{
   for(int x=0;x<9;x++)
      for(int y=0;y<3;y++)
      {
         ObjectCreate("signal"+x+y,OBJ_LABEL,0,0,0,0,0);
         ObjectSet("signal"+x+y,OBJPROP_XDISTANCE,x*20+12);
         ObjectSet("signal"+x+y,OBJPROP_YDISTANCE,y*20+20);
         ObjectSetText("signal"+x+y,CharToStr(110),20,"Wingdings",Gold);
      }
 
   // create writings for periods from left to right
   for(x=0;x<9;x++)
   {
      // everything is as usual
      ObjectCreate("textPeriod"+x,OBJ_LABEL,0,0,0,0,0);
      ObjectSet("textPeriod"+x,OBJPROP_XDISTANCE,x*20+12);
      ObjectSet("textPeriod"+x,OBJPROP_YDISTANCE,10);
      ObjectSetText("textPeriod"+x,periodString[x],8,"Tahoma",Gold);
      // we use the array periodString[], to indicate writings
   }
   
   return(0);
}



添加一些參數

爲了讓指標變得更靈活一些,我們可以添加一些參數,以便用戶可以設置指標的外部視圖:

#property copyright "Antonuk Oleg"
#property link      "[email protected]"
 
#property indicator_chart_window
 
extern int scaleX=20, // horizontal interval at which the squares are created
           scaleY=20, // vertical interval
           offsetX=35, // horizontal indent of all squares
           offsetY=20, // vertical indent
           fontSize=20; // font size
           
int period[]={1,5,15,30,60,240,1440,10080,43200};
string periodString[]={"M1","M5","M15","M30","H1","H4","D1","W1","MN1"};

我們再來更改函數init()start():

int init()
{
   for(int x=0;x<9;x++)
      for(int y=0;y<3;y++)
      {
         ObjectCreate("signal"+x+y,OBJ_LABEL,0,0,0,0,0);
         ObjectSet("signal"+x+y,OBJPROP_XDISTANCE,x*scaleX+offsetX);
         ObjectSet("signal"+x+y,OBJPROP_YDISTANCE,y*scaleY+offsetY);
         ObjectSetText("signal"+x+y,CharToStr(110),fontSize,"Wingdings",Gold);
      }
 
   for(x=0;x<9;x++)
   {
      ObjectCreate("textPeriod"+x,OBJ_LABEL,0,0,0,0,0);
      ObjectSet("textPeriod"+x,OBJPROP_XDISTANCE,x*scaleX+offsetX);
      ObjectSet("textPeriod"+x,OBJPROP_YDISTANCE,offsetY-10);
      ObjectSetText("textPeriod"+x,periodString[x],8,"Tahoma",Gold);
   }
   
   return(0);
}
 
int start()
{
   for(int x=0;x<9;x++)
   {
      if(iMA(Symbol(),period[x],13,0,0,0,0)>iMA(Symbol(),period[x],24,0,0,0,0))
         ObjectSetText("signal"+x+"0",CharToStr(110),fontSize,"Wingdings",YellowGreen);
      else
         ObjectSetText("signal"+x+"0",CharToStr(110),fontSize,"Wingdings",Tomato); 
   }
 
   return(0);
}


激活其他行

第二行指示威廉指標的信號,第三行指示拋物線轉向指標 的信號。修改函數start():

int start()
{
   for(int x=0;x<9;x++)
   {
      if(iMA(Symbol(),period[x],13,0,0,0,0)>iMA(Symbol(),period[x],24,0,0,0,0))
         ObjectSetText("signal"+x+"0",CharToStr(110),fontSize,"Wingdings",YellowGreen);
      else
         ObjectSetText("signal"+x+"0",CharToStr(110),fontSize,"Wingdings",Tomato); 
   }
 
   // activate the second row
   for(x=0;x<9;x++)
   {
      // if the absolute value of WPR is lower than 20, this is a signal to buy
      if(MathAbs(iWPR(Symbol(),period[x],13,0))<20.0)
         ObjectSetText("signal"+x+"1",CharToStr(110),fontSize,"Wingdings",YellowGreen);   
      // if the absolute value of WPR is larger than 80, this is a signal to sell
      else if(MathAbs(iWPR(Symbol(),period[x],13,0))>80.0)
         ObjectSetText("signal"+x+"1",CharToStr(110),fontSize,"Wingdings",Tomato);   
      // else, if there are no signals, a square is painted gray
      else
         ObjectSetText("signal"+x+"1",CharToStr(110),fontSize,"Wingdings",DarkGray);      
   }
 
   // activate the third row
   for(x=0;x<9;x++)
   {
      // if the current price is larger than the value of SAR, this is a signal to buy
      if(iSAR(Symbol(),period[x],0.02,0.2,0)<Close[0])
         ObjectSetText("signal"+x+"2",CharToStr(110),fontSize,"Wingdings",YellowGreen);
      // otherwise, it is a signal to sell
      else
         ObjectSetText("signal"+x+"2",CharToStr(110),fontSize,"Wingdings",Tomato);
   }
 
   return(0);
}


添加信號名稱

現在我們爲各行設置一個名稱。我們像之前那樣用數組在左側創建三個文字圖標。

int period[]={1,5,15,30,60,240,1440,10080,43200};
string periodString[]={"M1","M5","M15","M30","H1","H4","D1","W1","MN1"},
       // create one more array with indicator names
string signalNameString[]={"MA","WPR","SAR"};

更改init():

int init()
{
   for(int x=0;x<9;x++)
      for(int y=0;y<3;y++)
      {
         ObjectCreate("signal"+x+y,OBJ_LABEL,0,0,0,0,0);
         ObjectSet("signal"+x+y,OBJPROP_XDISTANCE,x*scaleX+offsetX);
         ObjectSet("signal"+x+y,OBJPROP_YDISTANCE,y*scaleY+offsetY);
         ObjectSetText("signal"+x+y,CharToStr(110),fontSize,"Wingdings",Gold);
      }
 
   for(x=0;x<9;x++)
   {
      ObjectCreate("textPeriod"+x,OBJ_LABEL,0,0,0,0,0);
      ObjectSet("textPeriod"+x,OBJPROP_XDISTANCE,x*scaleX+offsetX);
      ObjectSet("textPeriod"+x,OBJPROP_YDISTANCE,offsetY-10);
      ObjectSetText("textPeriod"+x,periodString[x],8,"Tahoma",Gold);
   }
   
   // draw signal names from top downwards
   for(y=0;y<3;y++)
   {
      ObjectCreate("textSignal"+y,OBJ_LABEL,0,0,0,0,0);
      ObjectSet("textSignal"+y,OBJPROP_XDISTANCE,offsetX-25);
      ObjectSet("textSignal"+y,OBJPROP_YDISTANCE,y*scaleY+offsetY+8);
      ObjectSetText("textSignal"+y,signalNameString[y],8,"Tahoma",Gold);
   }
   
   return(0);
}



添加更改綁定角的選項

現在我們將添加一個選擇信號指標位置的選項。現在將它綁定到左上角。如果我們更改標記屬性OBJPROP_CORNER,則角將發生更改。此屬性可以取以下值:

  • 0 - 左上角
  • 1 - 右上角
  • 2 - 左下角
  • 3 - 右下角

那麼,我們來添加一個新參數 - corner:

#property indicator_chart_window
 
extern int scaleX=20,
           scaleY=20, 
           offsetX=35, 
           offsetY=20, 
           fontSize=20,
           corner=0; // adding a parameter for choosing a corner

更改函數init():

int init()
{
   // a table of signals
   for(int x=0;x<9;x++)
      for(int y=0;y<3;y++)
      {
         ObjectCreate("signal"+x+y,OBJ_LABEL,0,0,0,0,0);
         ObjectSet("signal"+x+y,OBJPROP_CORNER,corner);
         // change the corner
         ObjectSet("signal"+x+y,OBJPROP_XDISTANCE,x*scaleX+offsetX);
         ObjectSet("signal"+x+y,OBJPROP_YDISTANCE,y*scaleY+offsetY);
         ObjectSetText("signal"+x+y,CharToStr(110),fontSize,"Wingdings",Gold);
      }
 
   // name of timeframes
   for(x=0;x<9;x++)
   {
      ObjectCreate("textPeriod"+x,OBJ_LABEL,0,0,0,0,0);
      ObjectSet("textPeriod"+x,OBJPROP_CORNER,corner);
      // changing the corner
      ObjectSet("textPeriod"+x,OBJPROP_XDISTANCE,x*scaleX+offsetX);
      ObjectSet("textPeriod"+x,OBJPROP_YDISTANCE,offsetY-10);
      ObjectSetText("textPeriod"+x,periodString[x],8,"Tahoma",Gold);
   }
 
   // names of indicators
   for(y=0;y<3;y++)
   {
      ObjectCreate("textSignal"+y,OBJ_LABEL,0,0,0,0,0);
          ObjectSet("textSignal"+y,OBJPROP_CORNER,corner);// change the corner
      ObjectSet("textSignal"+y,OBJPROP_XDISTANCE,offsetX-25);
      ObjectSet("textSignal"+y,OBJPROP_YDISTANCE,y*scaleY+offsetY+8);
      ObjectSetText("textSignal"+y,signalNameString[y],8,"Tahoma",Gold);
   }
   
   return(0);
}


添加新參數

我們可以再添加一些參數,便於靈活地設置指標外觀。所有參數:

  • 所有可用顏色
  • 所有可用符號代碼

首先我們需要在代碼開頭聲明所有這些參數:

extern int scaleX=20,
           scaleY=20,
           offsetX=35,
           offsetY=20,
           fontSize=20,
           corner=0,
           symbolCodeBuy=110, // a symbol code for a buy signal
           symbolCodeSell=110, // sell signal
           symbolCodeNoSignal=110; // no signal
           
extern color signalBuyColor=YellowGreen, // color of the symbol of a buy signal
             signalSellColor=Tomato, // for a sell signal
             noSignalColor=DarkGray, // no signal
             textColor=Gold; // color of all writings

更改函數init():

int init()
{
   // table of signals
   for(int x=0;x<9;x++)
      for(int y=0;y<3;y++)
      {
         ObjectCreate("signal"+x+y,OBJ_LABEL,0,0,0,0,0);
         ObjectSet("signal"+x+y,OBJPROP_CORNER,corner);
         ObjectSet("signal"+x+y,OBJPROP_XDISTANCE,x*scaleX+offsetX);
         ObjectSet("signal"+x+y,OBJPROP_YDISTANCE,y*scaleY+offsetY);
         ObjectSetText("signal"+x+y,CharToStr(symbolCodeNoSignal),
                       fontSize,"Wingdings",noSignalColor);
      }
 
   // names of timeframes
   for(x=0;x<9;x++)
   {
      ObjectCreate("textPeriod"+x,OBJ_LABEL,0,0,0,0,0);
      ObjectSet("textPeriod"+x,OBJPROP_CORNER,corner);
      ObjectSet("textPeriod"+x,OBJPROP_XDISTANCE,x*scaleX+offsetX);
      ObjectSet("textPeriod"+x,OBJPROP_YDISTANCE,offsetY-10);
      ObjectSetText("textPeriod"+x,periodString[x],8,"Tahoma",textColor);
   }
 
   // names of indicators
   for(y=0;y<3;y++)
   {
      ObjectCreate("textSignal"+y,OBJ_LABEL,0,0,0,0,0);
      ObjectSet("textSignal"+y,OBJPROP_CORNER,corner);
      ObjectSet("textSignal"+y,OBJPROP_XDISTANCE,offsetX-25);
      ObjectSet("textSignal"+y,OBJPROP_YDISTANCE,y*scaleY+offsetY+8);
      ObjectSetText("textSignal"+y,signalNameString[y],8,"Tahoma",textColor);
   }
   
   return(0);
}

更改函數start():

int start()
{
   for(int x=0;x<9;x++)
   {
      if(iMA(Symbol(),period[x],13,0,0,0,0)>iMA(Symbol(),period[x],24,0,0,0,0))
         ObjectSetText("signal"+x+"0",CharToStr(symbolCodeBuy),fontSize,
         "Wingdings",signalBuyColor);
      else
         ObjectSetText("signal"+x+"0",CharToStr(symbolCodeSell),fontSize,
         "Wingdings",signalSellColor); 
   }
 
   for(x=0;x<9;x++)
   {
      if(MathAbs(iWPR(Symbol(),period[x],13,0))<20.0)
         ObjectSetText("signal"+x+"1",CharToStr(symbolCodeBuy),fontSize,
         "Wingdings",signalBuyColor);   
      else if(MathAbs(iWPR(Symbol(),period[x],13,0))>80.0)
         ObjectSetText("signal"+x+"1",CharToStr(symbolCodeSell),fontSize,
         "Wingdings",signalSellColor);   
      else
         ObjectSetText("signal"+x+"1",CharToStr(symbolCodeNoSignal),fontSize,
         "Wingdings",noSignalColor);      
   }
 
   for(x=0;x<9;x++)
   {
      if(iSAR(Symbol(),period[x],0.02,0.2,0)<Close[0])
         ObjectSetText("signal"+x+"2",CharToStr(symbolCodeBuy),fontSize,
         "Wingdings",signalBuyColor);
      else
         ObjectSetText("signal"+x+"2",CharToStr(symbolCodeSell),fontSize,
         "Wingdings",signalSellColor);
   }
 
   return(0);
}


更改外部視圖

指標已準備就緒。通過更改輸入函數,我們可以完全改變外部視圖:

extern int scaleX=20,
           scaleY=20,
           offsetX=35,
           offsetY=20,
           fontSize=20,
           corner=2,
           symbolCodeBuy=67, 
           symbolCodeSell=68, 
           symbolCodeNoSignal=73; 
           
extern color signalBuyColor=Gold,
             signalSellColor=MediumPurple,
             noSignalColor=WhiteSmoke,
             textColor=Gold;



家庭作業

試着創建自己的信號條件,並多添加一行。創建多個新參數。例如,一個可以檢測文字(時間範圍和信號名稱)字體大小的參數。根據自己的喜好設置指標的外觀。



總結

今天我們學習了在腳本和指標中使用圖形對象。我們瞭解瞭如何創建對象,修改其參數以及檢查錯誤。理解這些知識後,你就可以自主學習新的圖形對象類型了。你還逐步創建了一個複雜的指標,並可輕鬆對其進行靈活的設置。

之前的文章都屬於“面向初學者的 MQL4 語言”系列:

  1. 面向初學者的 MQL4 語言系列之1——MQL4語言入門
  2. 面向初學者的 MQL4 語言系列之2——MQL4語言入門深入
  3. 面向初學者的 MQL4 語言系列之3——技術指標和內置函數
  4. 面向初學者的 MQL4 語言系列之4——自定義指標1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章