期貨量化交易軟件:基于畫(huà)布的指標(biāo)為通道填充透明度
概述
赫茲量化在本文中,我將介紹一種創(chuàng)建自定義指標(biāo)的方法,其繪圖是利用標(biāo)準(zhǔn)庫(kù)中的 CCanvas 類制作的。 我將著手處理特殊的指標(biāo),其需要用一種純色填充兩條線之間的區(qū)域。 在開(kāi)始之前,我們將了解為什么要用畫(huà)布,這也許是此類指標(biāo)當(dāng)前可用選項(xiàng)的最佳選擇。 之后,我們將看到計(jì)算坐標(biāo)所需的一些圖表屬性,以及涉及操控 CCanvas 的基本過(guò)程。
最終目標(biāo)是結(jié)合到目前為止看到的所有內(nèi)容來(lái)構(gòu)建應(yīng)用透明度的指標(biāo)。 所有工作將只考慮在主圖表窗口。 一旦我們的目標(biāo)達(dá)成,我們就可以將其擴(kuò)展到在子窗口里工作的指標(biāo)。
本文的主題如下:

采用畫(huà)布的原因
圖表窗口屬性
理解圖表窗口屬性
圖表屬性查看器指標(biāo)
坐標(biāo)轉(zhuǎn)換
透明的 DRAW_FILLING
在子窗口指標(biāo)中工作的擴(kuò)展方法
采用畫(huà)布的原因
有人會(huì)問(wèn),為什么要采用畫(huà)布替代已在自定義指標(biāo)中采用的 DRAW_FILLING? 這里至少有兩個(gè)原因:
指標(biāo)的顏色與其它指標(biāo)、蠟燭和圖表對(duì)象的顏色混雜
DRAW_FILLING 不支持透明度

圖表窗口屬性
為了開(kāi)始繪制自定義圖表,我們需要研究一些圖表屬性。 可以在文檔中找到所有屬性 若要獲取這些屬性值,我們需要調(diào)用相應(yīng)的函數(shù) ChartGetInteger 和 ChartGetDouble。 還有一個(gè) ChartGetString,但我們?cè)谶@里不會(huì)用到它。
我們打算把用到的屬性按簡(jiǎn)述列出。 如果我們需要更多,我稍后會(huì)一并列出。
CHART_WIDTH_IN_PIXELS — 圖表窗口的寬度,不包括價(jià)格標(biāo)尺
CHART_HEIGTH_IN_PIXELS — 子窗口的高度,不包括日期標(biāo)尺
CHART_PRICE_MAX — 對(duì)應(yīng)于子窗口頂部的價(jià)格
CHART_PRICE_MIN — 對(duì)應(yīng)于子窗口底部的價(jià)格
CHART_SCALE — 確定柱線之間的間距。 經(jīng)過(guò)一些測(cè)試,我發(fā)現(xiàn)它是兩個(gè)值的冪,由 pow(2, CHART_SCALE) 得到。
CHART_FISRT_VISIBLE_BAR — 圖表上第一根可見(jiàn)的柱線,從左到右。
CHART_VISIBLE_BARS — 圖表上可見(jiàn)柱線的數(shù)量。
理解圖表窗口屬性 這些屬性可在下圖中輕松看到。

屬性 CHART_WIDTH_IN_PIXELS 和 CHART_HEIGTH _IN_PIXELS,我們將用它們來(lái)確定我們需要?jiǎng)?chuàng)建的畫(huà)布對(duì)象的大小,以便制作繪圖。 當(dāng)圖表窗口發(fā)生變化時(shí),如果這些屬性也發(fā)生了變化,我們就需要調(diào)整畫(huà)布大小。 為了更好地理解,我們將創(chuàng)建一個(gè)簡(jiǎn)單的指標(biāo),顯示屬性,以及它們?nèi)绾胃鶕?jù)價(jià)格變化和用戶交互而變化。 我們已開(kāi)始采用畫(huà)布來(lái)了解指標(biāo)繪制過(guò)程。 我已遵循這條路徑創(chuàng)建了我的指標(biāo)。 出于組織原則目的,我建議您也這樣做。

一旦指標(biāo)框架準(zhǔn)備就緒,我們需要將 CCanvas 函數(shù)庫(kù)添加到文件當(dāng)中。 我們可以使用 #include 預(yù)編譯指令來(lái)做到這一點(diǎn)。 然后我們創(chuàng)建 CCanvas 類的實(shí)例。 所有這些都緊跟在指標(biāo) #property 指令之后。 #property copyright "Copyright 2023, Samuel Manoel De Souza" #property link "https://www.mql5.com/en/users/samuelmnl" #property version "1.00" #property indicator_chart_window #include <Canvas/Canvas.mqh> CCanvas Canvas;
操控 CCanvas 時(shí),我們需要做的第一件事是創(chuàng)建一個(gè) OBJ_BITMAP_LABEL,并向其內(nèi)附加資源。 如果您想將其添加到圖表之中,通常在指標(biāo)初始化里,調(diào)用 CreateBitampLabel(...) 方法,即可完成。 最后是刪除 OBJ_BITMAP_LABEL 和附加到它的資源。 如果您想從圖表中刪除它,通常在指標(biāo)逆初始化里,調(diào)用 Destory(void) 方法,即可完成。 與此同時(shí),我們執(zhí)行基本的繪圖過(guò)程,其中包括擦除圖形(清除或設(shè)置資源的默認(rèn)像素值),制作圖形,并更新資源。 畫(huà)布流程的完整生存周期如下圖所示。

為簡(jiǎn)單起見(jiàn),我們將在一個(gè)名為 “Redraw” 的函數(shù)中暫留 "Erase","Draw","Update"。 在代碼中編寫(xiě)所有內(nèi)容,我們得到以下結(jié)構(gòu)。
//+------------------------------------------------------------------+ //| Custom indicator initialization function ? ? ? ? ? ? ? ? ? ? ? ? | //+------------------------------------------------------------------+ int OnInit() ?{ //--- indicator buffers mapping ? Canvas.CreateBitmapLabel(0, 0, "Canvas", 0, 0, 200, 150, COLOR_FORMAT_ARGB_NORMALIZE); //--- ? return(INIT_SUCCEEDED); ?} //+------------------------------------------------------------------+ //| Custom indicator deinitialization function ? ? ? ? ? ? ? ? ? ? ? | //+------------------------------------------------------------------+ void OnDeinit(const int reason) ?{ ? Canvas.Destroy(); ?} //+------------------------------------------------------------------+ //| Custom indicator redraw function ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | //+------------------------------------------------------------------+ void Redraw(void) ?{ ? uint default_color = ColorToARGB(clrBlack); ? uint text_color = ColorToARGB(clrWhite); //--- canvas erase ? Canvas.Erase(default_color); //--- add first draw //--- add second draw //--- add ... draw //--- add last draw //--- canvas update ? Canvas.Update(); ?}
為了顯示屬性,我們將使用 TextOut 方法編寫(xiě)它們。 而這些屬性值將以字符串形式存儲(chǔ)在結(jié)構(gòu)數(shù)組變量當(dāng)中
struct StrProperty ?{ ? string name; ? string value; ?};
結(jié)構(gòu)可以如下。 然后我們可以在循環(huán)中輸出它們的摘要。 由于我們還沒(méi)有數(shù)組,我們將在 Redraw 函數(shù)中將數(shù)組作為參數(shù)傳遞。 然后,重繪函數(shù)將如下所示:
void Redraw(StrProperty &array[]) ?{ ? uint default_color = ColorToARGB(clrBlack); ? uint text_color = ColorToARGB(clrWhite); //--- canvas erase ? Canvas.Erase(default_color); //--- add first draw ? int total = ArraySize(array); ? for(int i=0;i<total;i++) ? ? { ? ? ?int padding = 2; ? ? ?int left = padding, right = Canvas.Width() - padding, y = i * 20 + padding; ? ? ?Canvas.TextOut(left, y, array[i].name, text_color, TA_LEFT); ? ? ?Canvas.TextOut(right, y, array[i].value, text_color, TA_RIGHT); ? ? } //--- canvas update ? Canvas.Update(); ?}
最后我們就可以獲取屬性值,并輸出它們。 如果您的代碼還沒(méi)有OnChartEvent函數(shù)處理程序,則您需要將其加入。 在那里,我們將檢查 CHARTEVENT_CHART_CHANGE 事件 ID。 當(dāng)有事件發(fā)生時(shí),我們聲明一些變量來(lái)獲取屬性值,并將它們傳遞給結(jié)構(gòu)數(shù)組,然后調(diào)用 Redraw 函數(shù)。 那么我們啟程吧。 我們可以編譯指標(biāo),將其添加到圖表中,并操作圖表來(lái)查看畫(huà)布更新。
//+------------------------------------------------------------------+ //| Custom indicator chart event handler function ? ? ? ? ? ? ? ? ? ?| //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) ?{ ? if(id != CHARTEVENT_CHART_CHANGE) ? ? ?return; ? int chart_width ? ? ? ? = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); ? int chart_height ? ? ? ?= (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS); ? int chart_scale ? ? ? ? = (int)ChartGetInteger(0, CHART_SCALE); ? int chart_first_vis_bar = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR); ? int chart_vis_bars ? ? ?= (int)ChartGetInteger(0, CHART_VISIBLE_BARS); ? double chart_prcmin ? ? = ChartGetDouble(0, CHART_PRICE_MIN); ? double chart_prcmax ? ? = ChartGetDouble(0, CHART_PRICE_MAX); //--- ? StrProperty array[] ? ? { ? ? ? ?{"Width", (string)chart_width}, ? ? ? ?{"Height", (string)chart_height}, ? ? ? ?{"Scale", (string)chart_scale}, ? ? ? ?{"First Vis. Bar", (string)chart_first_vis_bar}, ? ? ? ?{"Visible Bars", (string)chart_vis_bars}, ? ? ? ?{"Price Min", (string)chart_prcmin}, ? ? ? ?{"Price Max", (string)chart_prcmax}, ? ? }; ? Redraw(array); ?}
坐標(biāo)轉(zhuǎn)換
此處,我們需要一些基本函數(shù)來(lái)從日期時(shí)間或柱線索引轉(zhuǎn)換為像素坐標(biāo) x,以及把價(jià)格轉(zhuǎn)換為像素坐標(biāo) y,或從 x 轉(zhuǎn)換為柱線索引,以及從 y 轉(zhuǎn)換為價(jià)格(其中一些我們現(xiàn)在不會(huì)用到,但我們可以一次將它們?nèi)恐谱魍瓿桑?有因于此,我們將圖表屬性變量移動(dòng)到全局范圍,而在 OnChartEvent 函數(shù)中,我們只會(huì)更新數(shù)值,并在需要時(shí)調(diào)用 Redraw 函數(shù)。 理想的解決方案是將變量和轉(zhuǎn)換函數(shù)封裝在類或結(jié)構(gòu)中,但現(xiàn)在我們先保持簡(jiǎn)單。 不過(guò),我建議您通過(guò)閱讀文章面向?qū)ο缶幊痰幕A(chǔ)知識(shí)和文檔中的相關(guān)主題(面向?qū)ο缶幊?/span>)來(lái)開(kāi)始學(xué)習(xí) OOP。 我們將在下一次機(jī)會(huì)中用到這些。