目錄
簡介
我們都知道,外匯交易者使用貨幣對報價進行交易。所謂貨幣對,就是把一個國家的貨幣值以另一個國家的貨幣來表示。很容易就能在多個貨幣對中找到相同的貨幣。因為貨幣的價值是由它所在國家的經(jīng)濟狀況決定的,我們可能會問,當國家經(jīng)濟有變化的時候,它的貨幣在不同的貨幣對中的影響是否是一致的,看起來答案肯定是這樣的,但這只是當只有一個國家的經(jīng)濟狀態(tài)有變時的完美特例,現(xiàn)實生活中我們的世界是不斷變化的,一個國家的經(jīng)濟變化會直接或者間接地影響到全球的經(jīng)濟。
在互連網(wǎng)上,您可以找到很多信息是關(guān)于分析貨幣價格在不同貨幣對中的改變以及尋找不同貨幣對之間的相互關(guān)聯(lián)的。在本站中,您可以找到關(guān)于交易貨幣對籃子的文章 [1, 2, 3]。盡管如此,分析時間序列之間相互關(guān)聯(lián)的問題依然存在,在本文中,我提出了開發(fā)一種圖形化分析時間序列之間相互關(guān)聯(lián)的工具,它可以可視化所分析的貨幣對報價的時間序列之間的相互關(guān)聯(lián)。
1. 設(shè)定任務(wù)
在我們開始工作之前,讓我們定義我們的目標。我們最后想要得到哪種工具?首先,它應(yīng)當是一個圖形面板,包含著傳入的時間序列之間相互關(guān)聯(lián)的圖形。這個工具應(yīng)當是足夠多功能的,可以用于處理不同數(shù)量的時間序列。
為了在面板上分析時間序列,我們將為每個時間序列構(gòu)建一個分布柱形圖,我們還將準備成對顯示的散點圖,用來分析時間序列以尋找相互關(guān)聯(lián)。在散點圖上會加上趨勢線,作為可視化的參考。
圖形的布局將是一個交叉表的形式,這樣可以提高整個工具的可讀性。這種方法統(tǒng)一了數(shù)據(jù)的表現(xiàn)形式,并且從視覺方面來看更加簡單。所述工具的布局如下所示。
2. 創(chuàng)建基類
2.1. '基礎(chǔ)'
當開發(fā)這樣的工具時,我們應(yīng)當記住,用戶可能會操作不同數(shù)量的交易資產(chǎn)。我相信,最佳的可視化方案是基于區(qū)塊的表示方法,其中使用標準的圖形來作為構(gòu)建一個統(tǒng)一相互關(guān)聯(lián)表格的“磚石”。
我們將會從準備圖表構(gòu)建基礎(chǔ)開始,來開發(fā)我們的工具。MetaTrader 5 基礎(chǔ)發(fā)布部分含有 CGraphic 類,可以用于構(gòu)建科學圖表,這篇文章 [4] 提供了這個類的詳細描述,我們將會以它為基礎(chǔ)來構(gòu)建我們的圖形圖表。讓我們創(chuàng)建 CPlotBase 基類,并且使它繼承于 CGraphic 標準類,在這個類中,我們將創(chuàng)建用于生成圖形畫布的方法。將會有兩個這樣的方法: 第一個用于使用給定的邊寬來畫方形區(qū)域,而第二個用于使用給定的坐標來畫長方形區(qū)域。我們還將加上用于在圖形的側(cè)邊顯示文字的方法 (它們將會幫助我們顯示資產(chǎn)的名稱)。另外,讓我們加上用于在時間序列圖上改變顯示顏色的方法。
我們應(yīng)該記住,我們所使用的 CGraphic 基類不是派生于 CObject 類的,并且沒有包含在圖形中重新分配、隱藏和顯示對象的方法,類似的方法在圖形面板上有廣泛使用,所以,我們需要在創(chuàng)建的類中加上這些方法,這樣我們的工具就和構(gòu)建圖形面板的標準類兼容了。
class CPlotBase : public CGraphic {protected: long m_chart_id; int m_subwin; public: CPlotBase(); ~CPlotBase(); virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int size); virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2); virtual bool SetTimeseriesColor(uint clr, uint timeserie=0); virtual void TextUp(string text, uint clr); virtual void TextDown(string text, uint clr); virtual void TextLeft(string text, uint clr); virtual void TextRight(string text, uint clr); virtual bool Shift(const int dx,const int dy); virtual bool Show(void); virtual bool Hide(void); };
在類的構(gòu)造函數(shù)中,刪除圖形的縱向顯示并且設(shè)置在軸上的最少標簽數(shù)量。
CPlotBase::CPlotBase() { HistoryNameWidth(0); HistorySymbolSize(0); m_x.MaxLabels(3); m_y.MaxLabels(3); }
在附件中可以找到所有類方法的完整代碼。
2.2. 散點圖
下一步,開發(fā)用于顯示散點圖的 CScatter 類。這個類將只包含兩個方法,用于創(chuàng)建和更新時間序列的數(shù)據(jù)。
class CScatter : public CPlotBase {public: CScatter(); ~CScatter(); int AddTimeseries(const double ×eries_1[],const double ×eries_2[]); bool UpdateTimeseries(const double ×eries_1[],const double ×eries_2[],uint timeserie=0); };
向 AddTimeseries 曲線創(chuàng)建方法傳入的是散點圖所依賴的兩個分析資產(chǎn)的時間序列,CGraphic 標準類可以根據(jù)兩個數(shù)據(jù)數(shù)組顯示一個點形圖。我們將會使用這個功能。在方法的開始,根據(jù)兩個數(shù)據(jù)數(shù)組創(chuàng)建一個點曲線,如果曲線創(chuàng)建失敗,就返回 '-1' 的結(jié)果退出函數(shù)。如果成功創(chuàng)建了曲線,設(shè)置曲線點的大小并設(shè)置趨勢線顯示的標志。在進行所有操作之后,方法會返回所創(chuàng)建曲線的索引。
int CScatter::AddTimeseries(const double ×eries_1[],const double ×eries_2[]) { CCurve *curve=CGraphic::CurveAdd(timeseries_1,timeseries_2,CURVE_POINTS); if(curve==NULL) return -1; curve.PointsSize(2); curve.TrendLineVisible(true); return (m_arr_curves.Total()-1); }
為了更新曲線數(shù)據(jù),我們創(chuàng)建了 UpdateTimeseries 方法,傳給它的參數(shù)是兩個用于創(chuàng)建曲線的數(shù)據(jù)數(shù)組和需要更新數(shù)據(jù)曲線的索引。在函數(shù)的開始,檢查指定的曲線編號是否有效,如果編號是錯誤指定的,就退出函數(shù),返回 'false'。
然后,比較所得到時間序列的維度,如果數(shù)組的大小不同或者數(shù)組是空的,退出函數(shù)并返回 'false'。
下一步是指定根據(jù)索引的曲線對象的指針,如果指針不正確,就退出函數(shù)返回 'false'。
在全部檢查之后,把時間序列傳給曲線并退出函數(shù)返回 'true'。
bool CScatter::UpdateTimeseries(const double ×eries_1[],const double ×eries_2[], uint timeserie=0) { if((int)timeserie>=m_arr_curves.Total()) return false; if(ArraySize(timeseries_1)!=ArraySize(timeseries_2) || ArraySize(timeseries_1)==0) return false; CCurve *curve=m_arr_curves.At(timeserie); if(CheckPointer(curve)==POINTER_INVALID) return false; curve.Update(timeseries_1,timeseries_2); return true; }
2.3. 柱形圖
柱形圖是另一個用于構(gòu)建我們工具的“磚塊”,我們需要 CHistogram 類來創(chuàng)建它。與 CScatter 類似, 這個類也要有它自己的曲線數(shù)據(jù)生成和更新方法,但是,和前者不同,這個類使用一個時間序列來構(gòu)建曲線。構(gòu)造這些方法的原則和前一個類的方法類似,
請記住,CGraphic 基類只能以它的標準形式構(gòu)建柱形圖。為了增加構(gòu)建與市場概況類似的垂直柱形圖的功能,我們將必須重寫 HistogramPlot 方法。另外,我們應(yīng)當加上 e_orientation 變量,用于保存柱形圖構(gòu)建的類型,并且重寫圖形畫布創(chuàng)建方法,加上可以指定柱形圖類型的功能。
我們的類和 CGpraphic 基類的另一個區(qū)別是我們?nèi)〉玫某跏紨?shù)據(jù)的類型,在基類中,會把取得的數(shù)值數(shù)組直接輸出到圖形中。我們的類得到的是時間序列,在處理柱形圖之間,會需要處理獲得的數(shù)據(jù),準備用于構(gòu)建柱形圖的數(shù)據(jù)是在 CalculateHistogramArray 方法中進行的, 柱形圖列的數(shù)量是由 SetCells 方法設(shè)置的,并且會被保存在 i_cells 變量中。
class CHistogram : public CPlotBase {private: ENUM_HISTOGRAM_ORIENTATION e_orientation; uint i_cells;public: CHistogram(); ~CHistogram(); bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int size, ENUM_HISTOGRAM_ORIENTATION orientation=HISTOGRAM_HORIZONTAL); bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2, ENUM_HISTOGRAM_ORIENTATION orientation=HISTOGRAM_HORIZONTAL); int AddTimeserie(const double ×erie[]); bool UpdateTimeserie(const double ×erie[],uint timeserie=0); bool SetCells(uint value) { i_cells=value; }protected: virtual void HistogramPlot(CCurve *curve); bool CalculateHistogramArray(const double &data[],double &intervals[],double &frequency[], double &maxv,double &minv);};
CalculateHistogramArray 方法是基于 MQL5 參考中的算法,又增加了一些小的功能。在方法的開始,檢查初始數(shù)據(jù)是否足夠來繪制柱形圖,確定最小值和最大值,計算每個間隙的范圍寬度,并準備數(shù)組用于保存間隔和頻率。
隨后,會在循環(huán)中設(shè)置間隔并把頻率數(shù)組清空為零。
在下一個循環(huán)中,迭代時間序列并對符合對應(yīng)間隔的數(shù)值進行計數(shù)。
最后,規(guī)范化頻率,把符合條件計數(shù)轉(zhuǎn)換為在時間序列中元素總數(shù)的百分比。
bool CHistogram::CalculateHistogramArray(const double &data[],double &intervals[],double &frequency[], double &maxv,double &minv) { int size=ArraySize(data); if(size<(int)i_cells*10) return (false); minv=data[ArrayMinimum(data)]; maxv=data[ArrayMaximum(data)]; double range=maxv-minv; double width=range/i_cells; if(width==0) return false; ArrayResize(intervals,i_cells); ArrayResize(frequency,i_cells); for(uint i=0; i<i_cells; i ) { intervals[i]=minv (i 0.5)*width; frequency[i]=0; } for(int i=0; i<size; i ) { uint ind=int((data[i]-minv)/width); if(ind>=i_cells) ind=i_cells-1; frequency[ind] ; } for(uint i=0; i<i_cells; i ) frequency[i]*=(100.0/(double)size); return (true); }
柱形圖是使用 HistogramPlot 方法畫在圖形中的,這個函數(shù)是根據(jù) CGraphic 基類的算法構(gòu)建的,為了使用時間序列以及柱形圖的生成方向進行了修改。
在方法的開始,準備用于繪制柱形圖的數(shù)據(jù),為此,我們從曲線數(shù)據(jù)中取得時間序列并調(diào)用 CalculateHistogramArray 方法。在這個函數(shù)成功執(zhí)行之后,我們就得到了柱形圖的寬度,并可以檢查構(gòu)成數(shù)據(jù)的數(shù)組大小。
下一步,根據(jù)柱形圖的顯示類型格式化軸上的數(shù)值。
最后,在圖形區(qū)域中循環(huán)顯示圖表列。
CHistogram::HistogramPlot(CCurve *curve) { double data[],intervals[],frequency[]; double max_value, min_value; curve.GetY(data); if(!CalculateHistogramArray(data,intervals,frequency,max_value,min_value)) return; int histogram_width=fmax(curve.HistogramWidth(),2); if(ArraySize(frequency)==0 || ArraySize(intervals)==0) return; switch(e_orientation) { case HISTOGRAM_HORIZONTAL: m_y.AutoScale(false); m_x.Min(intervals[ArrayMinimum(intervals)]); m_x.Max(intervals[ArrayMaximum(intervals)]); m_x.MaxLabels(3); m_x.ValuesFormat('%.0f'); m_y.Min(0); m_y.Max(frequency[ArrayMaximum(frequency)]); m_y.ValuesFormat('%.2f'); break; case HISTOGRAM_VERTICAL: m_x.AutoScale(false); m_y.Min(intervals[ArrayMinimum(intervals)]); m_y.Max(intervals[ArrayMaximum(intervals)]); m_y.MaxLabels(3); m_y.ValuesFormat('%.0f'); m_x.Min(0); m_x.Max(frequency[ArrayMaximum(frequency)]); m_x.ValuesFormat('%.2f'); break; } CalculateXAxis(); CalculateYAxis(); int originalY=m_height-m_down; int originalX=m_width-m_right; int yc0=ScaleY(0.0); int xc0=ScaleX(0.0); uint clr=curve.Color(); for(uint i=0; i<i_cells; i ) { if(!MathIsValidNumber(frequency[i]) || !MathIsValidNumber(intervals[i])) continue; if(e_orientation==HISTOGRAM_HORIZONTAL) { int xc=ScaleX(intervals[i]); int yc=ScaleY(frequency[i]); int xc1 = xc - histogram_width/2; int xc2 = xc histogram_width/2; int yc1 = yc; int yc2 = (originalY>yc0 && yc0>0) ? yc0 : originalY; if(yc1>yc2) yc2 ; else yc2--; m_canvas.FillRectangle(xc1,yc1,xc2,yc2,clr); } else { int yc=ScaleY(intervals[i]); int xc=ScaleX(frequency[i]); int yc1 = yc - histogram_width/2; int yc2 = yc histogram_width/2; int xc1 = xc; int xc2 = (originalX>xc0 && xc0>0) ? xc0 : originalX; if(xc1>xc2) xc2 ; else xc2--; m_canvas.FillRectangle(xc1,yc1,xc2,yc2,clr); } } }
所有類和方法的完整代碼在附件中。
2.4. 用于操作時間序列的類
為了構(gòu)建工具,我們還需要另一個“磚塊”來下載所需的歷史數(shù)據(jù)以及準備時間序列用于畫圖,這項工作將是在 CTimeserie 類中進行的。在初始化類的時候,會傳給它資產(chǎn)名稱、時段和使用的價格類型。另外,還需要創(chuàng)建改變資產(chǎn)名稱、時段、使用的價格類型以及歷史深度的方法。
class CTimeserie : public CObject {protected: string s_symbol; ENUM_TIMEFRAMES e_timeframe; ENUM_APPLIED_PRICE e_price; double d_timeserie[]; int i_bars; datetime dt_last_load; public: CTimeserie(void); ~CTimeserie(void); bool Create(const string symbol=NULL, const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT, const ENUM_APPLIED_PRICE price=PRICE_CLOSE); void SetBars(const int value) { i_bars=value; } void Symbol(string value) { s_symbol=value; dt_last_load=0; } void Timeframe(ENUM_TIMEFRAMES value) { e_timeframe=value; dt_last_load=0; } void Price(ENUM_APPLIED_PRICE value) { e_price=value; dt_last_load=0; } string Symbol(void) { return s_symbol; } ENUM_TIMEFRAMES Timeframe(void) { return e_timeframe; } ENUM_APPLIED_PRICE Price(void) { return e_price; } virtual bool UpdateTimeserie(void); bool GetTimeserie(double ×erie[]) { return ArrayCopy(timeserie,d_timeserie)>0; } };
主要的數(shù)據(jù)準備工作是在 UpdateTimeserie 方法中完成的。在這個方法的開始,檢查當前的柱上是否已經(jīng)載入了所需的信息,如果信息已經(jīng)載入完畢,就退出函數(shù)返回 'true'。如果需要準備數(shù)據(jù),根據(jù)指定的價格上傳所需的歷史數(shù)據(jù),如果無法上傳信息,就退出函數(shù)返回 'false'。請注意,我們對價格本身并沒有興趣,關(guān)注的是它的變化。所以上傳的歷史數(shù)據(jù)要比指定的數(shù)量多一個柱。在下一步,我們在循環(huán)中在每個柱上重新計算價格的變化并把它保存到數(shù)組中。以后,用戶就可以通過使用 GetTimeserie 方法來取得這些信息了。
bool CTimeserie::UpdateTimeserie(void) { datetime cur_date=(datetime)SeriesInfoInteger(s_symbol,e_timeframe,SERIES_LASTBAR_DATE); if(dt_last_load>=cur_date && ArraySize(d_timeserie)>=i_bars) return true; MqlRates rates[]; int bars=0,i; double data[]; switch(e_price) { case PRICE_CLOSE: bars=CopyClose(s_symbol,e_timeframe,1,i_bars 1,data); break; case PRICE_OPEN: bars=CopyOpen(s_symbol,e_timeframe,1,i_bars 1,data); case PRICE_HIGH: bars=CopyHigh(s_symbol,e_timeframe,1,i_bars 1,data); case PRICE_LOW: bars=CopyLow(s_symbol,e_timeframe,1,i_bars 1,data); case PRICE_MEDIAN: bars=CopyRates(s_symbol,e_timeframe,1,i_bars 1,rates); bars=ArrayResize(data,bars); for(i=0;i<bars;i ) data[i]=(rates[i].high rates[i].low)/2; break; case PRICE_TYPICAL: bars=CopyRates(s_symbol,e_timeframe,1,i_bars 1,rates); bars=ArrayResize(data,bars); for(i=0;i<bars;i ) data[i]=(rates[i].high rates[i].low rates[i].close)/3; break; case PRICE_WEIGHTED: bars=CopyRates(s_symbol,e_timeframe,1,i_bars 1,rates); bars=ArrayResize(data,bars); for(i=0;i<bars;i ) data[i]=(rates[i].high rates[i].low 2*rates[i].close)/4; break; } if(bars<=0) return false; dt_last_load=cur_date; if(ArraySize(d_timeserie)!=(bars-1) && ArrayResize(d_timeserie,bars-1)<=0) return false; double point=SymbolInfoDouble(s_symbol,SYMBOL_POINT); for(i=0;i<bars-1;i ) d_timeserie[i]=(data[i 1]-data[i])/point; return true; }
所有類和它們方法的完整代碼都在附件中。
3. 組裝 PairPlot
在創(chuàng)建了 '磚塊' 之后, 我們就可以開始開發(fā)我們的工具了。讓我們創(chuàng)建 CPairPlot 類,它派生于 CWndClient 類。這種方法將可以使在使用標準的 CAppDialog 類所創(chuàng)建的圖形面板中使用我們的工具更加容易 (這種應(yīng)用程序的詳細情況可以在下列文章中找到 [5,6]).
在我們類的 'private' 部分,聲明 CPlotBase 類對象的指針數(shù)組用于保存圖形的指針,CArrayObj 類對象用于保存時間序列對象的指針,以及用于保存所使用的時段、價格、柱形圖方向、歷史深度和在圖形中顯示資產(chǎn)名稱顏色的變量。
class CPairPlot : public CWndClient {private: CPlotBase *m_arr_graphics[]; CArrayObj m_arr_symbols; ENUM_TIMEFRAMES e_timeframe; ENUM_APPLIED_PRICE e_price; int i_total_symbols; uint i_bars; ENUM_HISTOGRAM_ORIENTATION e_orientation; uint i_text_color; public: CPairPlot(); ~CPairPlot(); bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2, const string &symbols[],const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT, const int bars=1000, const uint cells=10, const ENUM_APPLIED_PRICE price=PRICE_CLOSE); bool Refresh(void); bool HistogramOrientation(ENUM_HISTOGRAM_ORIENTATION value); ENUM_HISTOGRAM_ORIENTATION HistogramOrientation(void) { return e_orientation; } bool SetTextColor(color value); virtual bool Shift(const int dx,const int dy); virtual bool Show(void); virtual bool Hide(void); };
類方法聲明在 'public' 部分,類的初始化是在 Create 方法中進行的,需要傳入的參數(shù)有圖形 ID, 對象名稱, 應(yīng)用的子窗口索引, 創(chuàng)建的坐標, 使用的交易品種數(shù)組, 使用的時段, 價格, 歷史深度和柱形圖的列數(shù)。
在方法的開始,檢查傳入的資產(chǎn)名稱數(shù)組和指定的歷史深度,如果它們不能達到我們的最低要求,就退出函數(shù)返回 'false'。然后保存輸入?yún)?shù)的值用于畫圖。
bool CPairPlot::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2, const string &symbols[],const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT, const int bars=1000, const uint cells=10, const ENUM_APPLIED_PRICE price=PRICE_CLOSE) { i_total_symbols=0; int total=ArraySize(symbols); if(total<=1 || bars<100) return false; e_timeframe=timeframe; i_bars=bars; e_price=price;
下一步,在循環(huán)中為每個資產(chǎn)創(chuàng)建 CTimeserie 類的實例。如果不能為每個指定的資產(chǎn)創(chuàng)建時間序列,就退出函數(shù)并返回 'false'。
for(int i=0;i<total;i ) { CTimeserie *temp=new CTimeserie; if(temp==NULL) return false; temp.SetBars(i_bars); if(!temp.Create(symbols[i],e_timeframe,e_price)) return false; if(!m_arr_symbols.Add(temp)) return false; } i_total_symbols=m_arr_symbols.Total(); if(i_total_symbols<=1) return false;
在成功完成了準備工作之后,就繼續(xù)直接創(chuàng)建圖形對象。首先,調(diào)用父類的 Create 方法,然后,使 m_arr_graphics 數(shù)組(用于保存圖形指針)的大小設(shè)為與分析的資產(chǎn)數(shù)量相等。根據(jù)全部資產(chǎn)和所分析資產(chǎn)的數(shù)量大小來計算每個圖的寬度和高度,
隨后,使用兩個嵌套的循環(huán)來在所有要分析的資產(chǎn)上迭代,使用圖形對象創(chuàng)建表格。在有相同名稱的資產(chǎn)交叉時創(chuàng)建柱形圖,在其它情況下創(chuàng)建散點圖。如果所有的對象都成功創(chuàng)建,就退出函數(shù)返回 'true'。
if(!CWndClient::Create(chart,name,subwin,x1,y1,x2,y2)) return false; if(ArraySize(m_arr_graphics)!=(i_total_symbols*i_total_symbols)) if(ArrayResize(m_arr_graphics,i_total_symbols*i_total_symbols)<=0) return false; int width=Width()/i_total_symbols; int height=Height()/i_total_symbols; for(int i=0;i<i_total_symbols;i ) { CTimeserie *timeserie1=m_arr_symbols.At(i); if(timeserie1==NULL) continue; for(int j=0;j<i_total_symbols;j ) { string obj_name=m_name '_' (string)i '_' (string)j; int obj_x1=m_rect.left j*width; int obj_x2=obj_x1 width; int obj_y1=m_rect.top i*height; int obj_y2=obj_y1 height; if(i==j) { CHistogram *temp=new CHistogram(); if(CheckPointer(temp)==POINTER_INVALID) return false; if(!temp.Create(m_chart_id,obj_name,m_subwin,obj_x1,obj_y1,obj_x2,obj_y2,e_orientation)) return false; m_arr_graphics[i*i_total_symbols j]=temp; temp.SetCells(cells); } else { CScatter *temp=new CScatter(); if(CheckPointer(temp)==POINTER_INVALID) return false; if(!temp.Create(m_chart_id,obj_name,m_subwin,obj_x1,obj_y1,obj_x2,obj_y2)) return false; CTimeserie *timeserie2=m_arr_symbols.At(j); if(timeserie2==NULL) continue; m_arr_graphics[i*i_total_symbols j]=temp; } } } return true; }
Refresh 方法是用于更新時間序列數(shù)據(jù)和在圖形中顯示數(shù)據(jù)的。在方法的開始,會在循環(huán)中更新所有時間序列的數(shù)據(jù)。請注意,當您構(gòu)建一個分布圖時,時間序列是成對使用的,所以,數(shù)據(jù)應(yīng)當可以比較。為了確保數(shù)據(jù)的兼容性,圖形對象的數(shù)據(jù)不會更新,而只要在更新至少一個時間序列出現(xiàn)錯誤的時候,方法就會返回 'false'。
在更新了時間序列數(shù)據(jù)之后,會再進行循環(huán)把更新過的時間序列傳給圖形對象。注意,要使用 'false' 參數(shù)來調(diào)用圖形對象的 Update 方法。這樣的調(diào)用可以確保圖形對象更新的時候不必更新應(yīng)用程序正運行在上方的圖形,這種方法會在更新每個圖形對象時排除掉之前更新過的,這樣可以減少終端的負載并減少函數(shù)的執(zhí)行時間。圖形在更新了所有的圖形元素后、在退出函數(shù)之前再更新一次。
bool CPairPlot::Refresh(void) { bool updated=true; for(int i=0;i<i_total_symbols;i ) { CTimeserie *timeserie=m_arr_symbols.At(i); if(timeserie==NULL) continue; updated=(updated && timeserie.UpdateTimeserie()); } if(!updated) return false; for(int i=0;i<i_total_symbols;i ) { CTimeserie *timeserie1=m_arr_symbols.At(i); if(CheckPointer(timeserie1)==POINTER_INVALID) continue; double ts1[]; if(!timeserie1.GetTimeserie(ts1)) continue; for(int j=0;j<i_total_symbols;j ) { if(i==j) { CHistogram *temp=m_arr_graphics[i*i_total_symbols j]; if(CheckPointer(temp)==POINTER_INVALID) return false; if(temp.CurvesTotal()==0) { if(temp.AddTimeserie(ts1)<0) continue; } else { if(!temp.UpdateTimeserie(ts1)) continue; } if(!temp.CurvePlotAll()) continue; if(i==0) temp.TextUp(timeserie1.Symbol(),i_text_color); if(i==(i_total_symbols-1)) temp.TextDown(timeserie1.Symbol(),i_text_color); if(j==0) temp.TextLeft(timeserie1.Symbol(),i_text_color); if(j==(i_total_symbols-1)) temp.TextRight(timeserie1.Symbol(),i_text_color); temp.Update(false); } else { CScatter *temp=m_arr_graphics[i*i_total_symbols j]; if(CheckPointer(temp)==POINTER_INVALID) return false; CTimeserie *timeserie2=m_arr_symbols.At(j); if(CheckPointer(timeserie2)==POINTER_INVALID) continue; double ts2[]; if(!timeserie2.GetTimeserie(ts2)) continue; if(temp.CurvesTotal()==0) { if(temp.AddTimeseries(ts1,ts2)<0) continue; } else if(!temp.UpdateTimeseries(ts1,ts2)) continue; if(!temp.CurvePlotAll()) continue; if(i==0) temp.TextUp(timeserie2.Symbol(),i_text_color); if(i==(i_total_symbols-1)) temp.TextDown(timeserie2.Symbol(),i_text_color); if(j==0) temp.TextLeft(timeserie1.Symbol(),i_text_color); if(j==(i_total_symbols-1)) temp.TextRight(timeserie1.Symbol(),i_text_color); temp.Update(false); } } } ChartRedraw(m_chart_id); return true; }
之前,我已經(jīng)提到過,圖形元素是基于 CGraphic 類而不是派生于 CObject 類,因為這個原因,我們在 CPlotBase 基類中加入了 Shift, Hide 和 Show 方法,因為同樣的原因,我們也必須在 CPairPlot 類中重寫對應(yīng)的方法。所有類和它們方法的完整代碼都在附件中。
4. 使用 CPairPlot 類的實例
現(xiàn)在,在做了這么多工作之后,是時候看看結(jié)果了。為了運行演示工具,讓我們制作一個指標來顯示相互關(guān)聯(lián)圖形,例如對于最新的1000個燭形,在每個新柱時顯示。
上面已經(jīng)提到,這個工具是為了圖形面板創(chuàng)建的,所以,讓我們首先創(chuàng)建派生于 CAppDialog 類的 CPairPlotDemo 類。操作 CAppDialog 類的詳細情況可以在文章 [5, 6] 中找到。在此,我將只會指出使用這個工具所特有的地方。
在 'private' 部分聲明 CPairPlot 類的實例,在 'public' 部分,聲明 Create 方法,其中含有所有工具初始化和運行時所需的輸入?yún)?shù)。在此,我們還要聲明 Refresh 和 HistogramOrientation 方法,它們會調(diào)用我們工具中的對應(yīng)方法。
class CPairPlotDemo : public CAppDialog {private: CPairPlot m_PairPlot;public: CPairPlotDemo(); ~CPairPlotDemo(); bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2,const string &symbols[],const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT, const int bars=1000, const uint cells=10); bool Refresh(void); bool HistogramOrientation(ENUM_HISTOGRAM_ORIENTATION value) { return m_PairPlot.HistogramOrientation(value); } ENUM_HISTOGRAM_ORIENTATION HistogramOrientation(void) { return m_PairPlot.HistogramOrientation(); } };
在 Create 方法中,首先調(diào)用父類中的相應(yīng)方法,然后調(diào)用元件實例的相同方法并把指針加到 CPairPlot 的類實例中的控件元件集合。
bool CPairPlotDemo::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2,const string &symbols[],const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT, const int bars=1000, const uint cells=10) { if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return false; if(!m_PairPlot.Create(m_chart_id,m_name 'PairPlot',m_subwin,0,0,ClientAreaWidth(),ClientAreaHeight(),symbols,timeframe,bars,cells)) return false; if(!Add(m_PairPlot)) return false; return true; }
現(xiàn)在,讓我們創(chuàng)建指標。我們指標中使用的參數(shù)有,使用逗號分隔的資產(chǎn)名稱字符串, 分析歷史深度的柱數(shù),柱形圖的列數(shù)和指向。
input string i_Symbols = 'EURUSD, GBPUSD, EURGBP';input uint i_Bars = 1000;input uint i_Cells = 50;input ENUM_HISTOGRAM_ORIENTATION i_HistogramOrientation = HISTOGRAM_HORIZONTAL;
在全局變量中聲明 CPairPlotDemo 類的實例。
CPairPlotDemo *PairPlot;
在 OnInit 函數(shù)中,根據(jù)指標外部參數(shù)中的字符串創(chuàng)建使用資產(chǎn)的數(shù)組,然后創(chuàng)建一個 CPairPlotDemo 類的實例,傳給它指定的柱形圖指向然后再調(diào)用它的 Create 方法。在成功初始化之后,使用 Run 方法載入類并運行,并使用 Refresh 方法更新數(shù)據(jù)。
int OnInit() { string symbols[]; int total=StringSplit(i_Symbols,',',symbols); if(total<=0) return INIT_FAILED; for(int i=0;i<total;i ) { StringTrimLeft(symbols[i]); StringTrimRight(symbols[i]); } PairPlot=new CPairPlotDemo; if(CheckPointer(PairPlot)==POINTER_INVALID) return INIT_FAILED; if(!PairPlot.HistogramOrientation(i_HistogramOrientation)) return INIT_FAILED; if(!PairPlot.Create(0,'Pair Plot',0,20,20,620,520,symbols,PERIOD_CURRENT,i_Bars,i_Cells)) return INIT_FAILED; if(!PairPlot.Run()) return INIT_FAILED; PairPlot.Refresh(); return INIT_SUCCEEDED; }
在 OnCalculate 函數(shù)中,在每個新柱處調(diào)用 Refresh 方法。在 OnChartEvent 和 OnDenint 函數(shù)中將會調(diào)用相應(yīng)的類方法。
在附件中有所有函數(shù)和類的完整代碼。
下面您將可以看到指標是如何工作的。
結(jié)論
在本文中,我們提供了一個很有趣的工具,它允許交易者快速而簡單地看到幾乎任意數(shù)量的交易資產(chǎn)之間的相互關(guān)聯(lián)。這個工具的主要目的是分析交易資產(chǎn)而開發(fā)各種套利策略。當然,這個工具本身還不足以開發(fā)一個完整功能的交易系統(tǒng),但是它可以在開發(fā)交易系統(tǒng)的第一階段大量使用 - 尋找相關(guān)的資產(chǎn)和它們的依賴關(guān)系。
參考
- 在外匯交易市場上操作貨幣籃子
- 測試當交易貨幣對籃子時出現(xiàn)的形態(tài)Part I
- 測試當交易貨幣對籃子時出現(xiàn)的形態(tài)第二部分
- 顯示這個!MQL5 圖形庫與 R 語言的 'plot' 類似
- 如何創(chuàng)建任意復(fù)雜度的圖形面板
- 增強面板:加上了透明度,修改背景色以及繼承于CAppDialog/CWndClient
本文中使用的程序
#
| 名稱
| 類型
| 描述 |
1 | PlotBase.mqh | 類庫 | Base class for building graphs |
2 | Scatter.mqh | 類庫 | 用于創(chuàng)建散點圖的類 |
3 | Histogram.mqh | 類庫 | 用于創(chuàng)建柱形圖的類
|
4 | PairPlot.mqh | 類庫 | PairPlot 工具類 |
5 | PairPlotDemo.mqh | 類庫 | 用于演示工具連接的類 |
6 | PairPlot.mq5 | 指標 | 用于演示工具工作的指標 |