//+------------------------------------------------------------------+ //| CorrelationMatrix3D.mq5 | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //--- #include #include #include //--- constants #define BAR_WIDTH 1.25f #define BAR_HEIGHT 5.0f //--- input data input color InpBackground = clrWhiteSmoke; // Background color input datetime InpStartDate = D'01.01.2015'; input datetime InpFinishDate = D'01.11.2019'; input string InpSymbolsList = "EURUSD,EURGBP,EURCHF,EURJPY,GBPUSD,GBPCHF,GBPJPY,USDCHF,USDJPY,CHFJPY"; //+------------------------------------------------------------------+ //| sSymbolData | //+------------------------------------------------------------------+ struct sSymbolData { string name; //--- synchronized close prices MqlRates rates[]; //--- percent of empty bars double zero_data_percent; //--- times and returns (in points) datetime returns_times[]; double returns_prices_points[]; //--- array with current data window double current_data_array[]; bool synchronized_flag; datetime history_first_date; }; //+------------------------------------------------------------------+ //| Application window | //+------------------------------------------------------------------+ class CCanvas3DWindow { protected: CPicture m_picture; CCanvas3D m_canvas; //--- int m_width; int m_height; uint m_background_color; uint m_text_color; //--- function data double m_data[]; int m_data_size; //--- source functions data CDXBox m_boxes[]; //--- DXVector4 m_labels_x[]; DXVector4 m_labels_z[]; //--- rotation Y float m_angle_y; float m_angle_x; int m_mouse_x_old; int m_mouse_y_old; //--- price data for correlations calculations int m_total_symbols; string m_symbols_used; sSymbolData m_symbols_data[]; bool m_data_ready; ENUM_TIMEFRAMES m_timeframe; int m_timeframe_minutes; //--- datetime m_date_start; // start date datetime m_date_finish; // finish date //--- datetime m_date_current; // date of data at current index int m_index_current; // current index int m_index_boundary1; // inital index for sliding window int m_index_boundary2; // final index for sliding window int m_data_count; bool m_symbol_selected[]; // array with flags to draw symbol //--- int m_window_size_bars; // window size int m_index_step_bars; // sliding window int m_time_format; // time format public: //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CCanvas3DWindow(void):m_angle_y(0),m_angle_x(0),m_mouse_x_old(-1),m_mouse_y_old(-1) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ ~CCanvas3DWindow(void) { int count=ArraySize(m_boxes); for(int i=0; i0) { DXMatrixTranslation(translation,i*BAR_WIDTH-offset,0.0,j*BAR_WIDTH-offset); DXMatrixScaling(scale,BAR_WIDTH,value*BAR_HEIGHT,BAR_WIDTH); } else { DXMatrixTranslation(translation,i*BAR_WIDTH-offset,value*BAR_HEIGHT,j*BAR_WIDTH-offset); DXMatrixScaling(scale,BAR_WIDTH,-value*BAR_HEIGHT,BAR_WIDTH); } //--- DXMatrixMultiply(translation,scale,translation); m_boxes[idx].TransformMatrixSet(translation); if(i==j) clr=DXColor(0.5,0.5,0.5,1.0); else DXComputeColorRedToGreen((float)m_data[idx]*0.5f+0.5f,clr); m_boxes[idx].DiffuseColorSet(clr); } //--- prepare to scrren space projection matrix DXMatrix projection,view; m_canvas.ViewMatrixGet(view); m_canvas.ProjectionMatrixGet(projection); DXMatrixMultiply(projection,view,projection); for(int j=-1,idx=0; j<=1; j+=2) for(int i=0; i0) DXVec4Scale(m_labels_x[idx],m_labels_x[idx],1.0f/m_labels_x[idx].w); DXVec4Transform(m_labels_z[idx],DXVector4(j*(BAR_WIDTH+offset),0.0,i*BAR_WIDTH-offset,1.0),projection); if(m_labels_z[idx].w>0) DXVec4Scale(m_labels_z[idx],m_labels_z[idx],1.0f/m_labels_z[idx].w); } //--- return(true); } //+------------------------------------------------------------------+ //| Update boxes | //+------------------------------------------------------------------+ bool UpdateCamera() { DXVector4 camera=DXVector4(0.0f,0.0f,-30.0f,1.0f); DXVector4 light =DXVector4(0.25f,-0.25f,1.0f,0.0f); DXMatrix rotation; DXMatrixRotationX(rotation,m_angle_x); DXVec4Transform(camera,camera,rotation); DXVec4Transform(light,light,rotation); DXMatrixRotationY(rotation,m_angle_y); DXVec4Transform(camera,camera,rotation); DXVec4Transform(light,light,rotation); m_canvas.ViewPositionSet(DXVector3(camera)); m_canvas.LightDirectionSet(DXVector3(light)); //--- return(true); } //+------------------------------------------------------------------+ //| CopyRatesTimeSynchronized | //+------------------------------------------------------------------+ bool CopyRatesTimeSynchronized(string symbol,ENUM_TIMEFRAMES timeframe,datetime time1,datetime time2,MqlRates &rates[]) { //--- check parameters if(time1>=time2) { PrintFormat("Wrong times. Start time=%s, Finish time=%s",TimeToString(time1,TIME_DATE|TIME_MINUTES|TIME_SECONDS),TimeToString(time2,TIME_DATE|TIME_MINUTES|TIME_SECONDS)); return(false); } //--- ResetLastError(); MqlRates server_rates[]; ArraySetAsSeries(server_rates,false); int rates_copied=CopyRates(symbol,timeframe,time1,time2,server_rates); if(rates_copied==-1) { PrintFormat("CopyRates error, rates_copied=%d Error=%d",rates_copied,GetLastError()); return(false); } //--- uint time_delta = uint(time2-time1); int data_needed = int(time_delta/(60*m_timeframe_minutes))+1; //--- prepare rates with correct continous times data ArrayResize(rates,data_needed); ArraySetAsSeries(rates,false); ZeroMemory(rates); //--- prepare times data int data_count=0; datetime time=time1; while(time<=time2) { rates[data_count].time=time; data_count++; time+=m_timeframe_minutes*60; } //--- analyze data gaps int total_gap_bars=0; int previous_time_index = 0; int fill_bars_needed=0; double current_price_close=0; double previous_price_close=0; for(int i=0; i1); if(gap_found) { //--- gap needed to fill with previous close price if(time_index>=0 || time_index=0 || time_index0) counter++; } if(counter==m_total_symbols) { rates_flags[i]=true; total_nonzero_rates++; } } //--- total_nonzero_rates=0; //--- filter nonzero rates for(int i=0; ifirst_date) first_date = m_symbols_data[i].history_first_date; continue; } else Print("Symbol '",m_symbols_data[i].name,"' first date is not available"); } else Print("Symbol '",m_symbols_data[i].name,"' is not synchronized"); //--- try to check all_symbols_synchronized=false; datetime tmp[1]; CopyTime(m_symbols_data[i].name,m_timeframe,first_date,1,tmp); } //--- all symbols synchonized if(all_symbols_synchronized) break; //--- Print("Try again #",n_tries); } //--- check all synchronized if(!all_symbols_synchronized) { Print("Some symbols not ready to use:"); for(int i=0; ifinish_date) { PrintFormat("Error in dates settings: start date=%s, finish date=%s",TimeToString(start_date,m_time_format),TimeToString(finish_date,m_time_format)); return(false); } //--- m_timeframe=PERIOD_D1; m_timeframe_minutes=GetTimeframeMinutes(m_timeframe); m_time_format=TIME_DATE; if(m_timeframesize) { PrintFormat("Error in index=%d count=%d ArraySize(data_array)=%d",index,count,ArraySize(data_array)); return(false); } //--- ArrayResize(out_data_array,count); ZeroMemory(out_data_array); for(int i=0; i=m_total_symbols) return(false); if(ind2<0 || ind2>=m_total_symbols) return(false); //--- bool res1=CopyData(m_symbols_data[ind1].returns_prices_points,data_start_index,count,m_symbols_data[ind1].current_data_array); bool res2=CopyData(m_symbols_data[ind2].returns_prices_points,data_start_index,count,m_symbols_data[ind2].current_data_array); //--- check data int size1=ArraySize(m_symbols_data[ind1].current_data_array); if(size1==0) return(false); //--- if(res1 && res2) value=CorrelationPearson(m_symbols_data[ind1].current_data_array,m_symbols_data[ind2].current_data_array); else return(false); //--- return(true); } //+------------------------------------------------------------------+ //| Prepare box geometry | //+------------------------------------------------------------------+ virtual bool PrepareBoxes(void) { ArrayResize(m_boxes,m_data_size*m_data_size); //--- create boxes for(int i=0; i54) font_size=54; m_canvas.FontSizeSet(font_size); m_canvas.FontSet("Arial",font_size,FW_SEMIBOLD); //--- draw x axis on right side float dx1=m_labels_x[m_data_size-1].x-m_labels_x[0].x; float dx2=m_labels_x[m_data_size].x-m_labels_x[2*m_data_size-1].x; int tx=0,ty=0; if(dx1>dx2) { for(int i=0; idx1) { for(int i=0; i=ArraySize(m_symbol_selected)) return; //--- m_symbol_selected[number]=!m_symbol_selected[number]; } //+------------------------------------------------------------------+ //| UpdateCorrelationMatrix | //+------------------------------------------------------------------+ void UpdateCorrelationMatrix() { if(ArraySize(m_symbols_data)==0) return; //--- calculate matrix of Pearson coefficients for(int i=0; iDX_PI*0.49f) m_angle_x=DX_PI*0.49f; //--- RedrawData(); } //--- m_mouse_x_old=x; m_mouse_y_old=y; } else { m_mouse_x_old=-1; m_mouse_y_old=-1; } } //+------------------------------------------------------------------+ //| Process chart change event | //+------------------------------------------------------------------+ void OnChartChange(void) { //--- get current chart window size int w=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS); int h=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS); //--- update size everywhere it needed if(w!=m_width || h!=m_height) { m_width =w; m_height=h; if(m_width<1) m_width=1; if(m_height<1) m_height=1; m_canvas.Resize(w,h); DXContextSetSize(m_canvas.DXContext(),w,h); m_canvas.ProjectionMatrixSet((float)M_PI/6,(float)m_width/m_height,0.1f,100.0f); Redraw(); } } //+------------------------------------------------------------------+ //| Timer handler | //+------------------------------------------------------------------+ void OnTimer(void) { //--- update time m_index_current += m_index_step_bars; //--- reverse time if(m_index_currentm_index_boundary2) { m_index_step_bars *= -1; m_index_current=m_index_boundary2; m_date_current=m_symbols_data[0].returns_times[m_index_current+m_window_size_bars]; PrintFormat("REVERSE TIME %s at index=%d",TimeToString(m_date_current,m_time_format),m_index_current); } m_date_current=m_symbols_data[0].returns_times[m_index_current+m_window_size_bars]; //--- RedrawData(); } }; CCanvas3DWindow *ExtAppWindow; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,1); //--- get current chart window size int width =(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS); int height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS); //--- create canvas ExtAppWindow=new CCanvas3DWindow(); if(!ExtAppWindow.PrepareData(InpSymbolsList,InpStartDate,InpFinishDate)) { PrintFormat("Error. Rates data is not loaded."); return(INIT_FAILED); } if(!ExtAppWindow.Create(width,height,InpBackground)) { return(INIT_FAILED); } //--- set timer EventSetMillisecondTimer(10); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- destroy delete ExtAppWindow; //--- revert chart showing mode ChartSetInteger(0,CHART_SHOW,true); } //+------------------------------------------------------------------+ //| OnTick function | //+------------------------------------------------------------------+ void OnTick() { ExtAppWindow.RedrawData(); } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- if(id==CHARTEVENT_KEYDOWN) { if(lparam==27) ExpertRemove(); if(lparam>='0' && lparam<='9') { int index=(int)(lparam-'1'); if(index<0) index=9; ExtAppWindow.SetSelected(index); } } if(id==CHARTEVENT_CHART_CHANGE) ExtAppWindow.OnChartChange(); //--- process mouse moving if(id==CHARTEVENT_MOUSE_MOVE) ExtAppWindow.OnMouseEvent((int)lparam,(int)dparam,(uint)sparam); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { ExtAppWindow.OnTimer(); } //+------------------------------------------------------------------+ //| GetTimeframeMinutes | //+------------------------------------------------------------------+ int GetTimeframeMinutes(ENUM_TIMEFRAMES timeframe) { switch(timeframe) { case PERIOD_M1: return(1); case PERIOD_M2: return(2); case PERIOD_M3: return(3); case PERIOD_M4: return(4); case PERIOD_M5: return(5); case PERIOD_M6: return(6); case PERIOD_M10: return(10); case PERIOD_M12: return(12); case PERIOD_M15: return(15); case PERIOD_M20: return(20); case PERIOD_M30: return(30); case PERIOD_H1: return(60); case PERIOD_H2: return(2*60); case PERIOD_H3: return(3*60); case PERIOD_H4: return(4*60); case PERIOD_H6: return(6*60); case PERIOD_H8: return(8*60); case PERIOD_H12: return(12*60); case PERIOD_D1: return(24*60); case PERIOD_W1: return(7*24*60); default: return(0); } //--- return(0); }; //+------------------------------------------------------------------+