mql-for-begginers/Experts/Examples/Correlation Matrix 3D/Correlation Matrix 3D.mq5
2025-07-22 18:30:17 +03:00

1002 lines
76 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <Controls\Picture.mqh>
#include <Canvas\Canvas3D.mqh>
#include <Canvas\DX\DXBox.mqh>
//--- 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; i<count; i++)
m_boxes[i].Shutdown();
}
//+------------------------------------------------------------------+
//| Update boxes |
//+------------------------------------------------------------------+
bool UpdateBoxes()
{
float offset=BAR_WIDTH*(m_data_size-1)/2.0f;
DXMatrix translation;
DXMatrix scale;
DXColor clr;
for(int i=0; i<m_data_size; i++)
for(int j=0; j<m_data_size; j++)
{
int idx=i*m_data_size+j;
float value=(i==j)?0.0f:(float)m_data[idx];
if(fabs(value)<0.001f || (!m_symbol_selected[i] && !m_symbol_selected[j]))
value=0.001f;
//--- check value sign, never use negative scale
if(value>0)
{
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; i<m_data_size; i++,idx++)
{
//--- calculate labels positions in screen space
DXVec4Transform(m_labels_x[idx],DXVector4(i*BAR_WIDTH-offset,0.0,j*(BAR_WIDTH+offset),1.0),projection);
if(m_labels_x[idx].w>0)
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; i<rates_copied; i++)
{
current_price_close=server_rates[i].close;
int time_index = int ((server_rates[i].time-time1)/(60*m_timeframe_minutes));
bool gap_found=((time_index-previous_time_index)>1);
if(gap_found)
{
//--- gap needed to fill with previous close price
if(time_index>=0 || time_index<data_count)
{
fill_bars_needed=time_index-previous_time_index-1;
for(int k=0; k<fill_bars_needed; k++)
{
//rates[previous_time_index+k+1].close=previous_price_close;
rates[previous_time_index+k+1].close=0; // mark gap
total_gap_bars++;
}
//--- current bar
if(rates[time_index].time==server_rates[i].time)
rates[time_index].close=current_price_close;
else
{
PrintFormat("The times are different: %d vs %d",rates[time_index].time,server_rates[i].time);
}
}
}
else
{
//--- no gap, normal set close price
if(time_index>=0 || time_index<data_count)
{
rates[time_index].close=current_price_close;
}
else
{
Print("Error in time_index=%d",time_index);
}
}
//---
previous_time_index = time_index;
previous_price_close = current_price_close;
}
//---
int total_zero_bars=0;
for(int i=0; i<data_count; i++)
{
if(rates[i].close==0)
total_zero_bars++;
}
PrintFormat("Total=%d bars. Symbol=%s, time1=%s, time2=%s. Total gaps filled=%d, Total zero bars=%d",data_count,symbol,
TimeToString(time1,TIME_DATE|TIME_MINUTES|TIME_SECONDS),TimeToString(time2,TIME_DATE|TIME_MINUTES|TIME_SECONDS),
total_gap_bars,total_zero_bars);
//---
return(true);
}
//+------------------------------------------------------------------+
//| FilterNonzeroRates |
//+------------------------------------------------------------------+
bool FilterNonzeroRates(void)
{
if(m_total_symbols==0)
return(false);
if(ArraySize(m_symbols_data)!=m_total_symbols)
return(false);
//---
int rates_count=ArraySize(m_symbols_data[0].rates);
for(int i=0; i<m_total_symbols; i++)
{
if(ArraySize(m_symbols_data[i].rates)!=rates_count)
{
PrintFormat("Symbol: %s Wrong array size=%d vs %d",m_symbols_data[i].name,ArraySize(m_symbols_data[i].rates),rates_count);
DebugBreak();
return(false);
}
}
//---
int total_nonzero_rates=0;
bool rates_flags[];
ArrayResize(rates_flags,rates_count);
ZeroMemory(rates_flags);
for(int i=0; i<rates_count; i++)
{
int counter=0;
for(int k=0; k<m_total_symbols; k++)
{
if(m_symbols_data[k].rates[i].close>0)
counter++;
}
if(counter==m_total_symbols)
{
rates_flags[i]=true;
total_nonzero_rates++;
}
}
//---
total_nonzero_rates=0;
//--- filter nonzero rates
for(int i=0; i<rates_count; i++)
{
if(rates_flags[i]==true)
{
for(int k=0; k<m_total_symbols; k++)
{
m_symbols_data[k].rates[total_nonzero_rates]=m_symbols_data[k].rates[i];
}
total_nonzero_rates++;
}
}
for(int k=0; k<m_total_symbols; k++)
ArrayResize(m_symbols_data[k].rates,total_nonzero_rates);
PrintFormat("rates_count=%d total_nonzero_rates=%d",rates_count,total_nonzero_rates);
//---
return(true);
}
//+------------------------------------------------------------------+
//| PreparePriceReturnsInPoints |
//+------------------------------------------------------------------+
bool PreparePriceReturnsInPoints(int symbol_index)
{
double point_size=SymbolInfoDouble(m_symbols_data[symbol_index].name,SYMBOL_POINT);
if(point_size==0)
{
PrintFormat("Wrong point size =%f for symbol=%s. ",point_size,m_symbols_data[symbol_index].name);
return(false);
}
//---
int data_count=ArraySize(m_symbols_data[symbol_index].rates);
if(data_count==0)
return(false);
//--- prepare price returns in points
int returns_count=data_count-1;
ArrayResize(m_symbols_data[symbol_index].returns_times,returns_count);
ArrayResize(m_symbols_data[symbol_index].returns_prices_points,returns_count);
//---
for(int i=0; i<returns_count; i++)
{
double price1=m_symbols_data[symbol_index].rates[i].close;
double price2=m_symbols_data[symbol_index].rates[i+1].close;
double delta=(price2-price1)/point_size;
if(price1==0 || price2==0)
delta=0;
//---
m_symbols_data[symbol_index].returns_prices_points[i]=delta;
m_symbols_data[symbol_index].returns_times[i]=m_symbols_data[symbol_index].rates[i+1].time;
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| SymbolsDataReady |
//+------------------------------------------------------------------+
bool SymbolsDataReady(datetime start_date)
{
//---
bool all_symbols_synchronized = true;
datetime first_date = start_date;
//---
for(int n_tries=0; n_tries<100 && !IsStopped(); n_tries++, Sleep(50))
{
all_symbols_synchronized=true;
//--- proceed all symbols
for(int i=0; i<m_total_symbols; i++)
{
m_symbols_data[i].synchronized_flag=SeriesInfoInteger(m_symbols_data[i].name,m_timeframe,SERIES_SYNCHRONIZED);
if(m_symbols_data[i].synchronized_flag)
{
if(SeriesInfoInteger(m_symbols_data[i].name,m_timeframe,SERIES_FIRSTDATE,(long&)m_symbols_data[i].history_first_date))
{
if(m_symbols_data[i].history_first_date>first_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; i<m_total_symbols; i++)
if(!m_symbols_data[i].synchronized_flag)
PrintFormat("%s ",m_symbols_data[i].name);
return(false);
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| PrepareData |
//+------------------------------------------------------------------+
bool PrepareData(string symbols, datetime start_date,datetime finish_date)
{
//--- check dates
if(start_date>finish_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_timeframe<PERIOD_H6)
m_time_format|=TIME_MINUTES;
//--- check if symbols exists
string symbols_list[]= {};
StringSplit(symbols,',',symbols_list);
m_total_symbols=0;
m_symbols_used="";
for(int i=0; i<ArraySize(symbols_list); i++)
{
PrintFormat("%d %s ",i,symbols_list[i]);
bool is_custom=false;
if(SymbolExist(symbols_list[i],is_custom)==false)
{
PrintFormat("Error. Symbol %s is not found.",symbols_list[i]);
return(false);
}
m_total_symbols++;
m_symbols_used+=" "+symbols_list[i];
}
//---
if(m_total_symbols==0)
{
Print("No symbols.");
return(false);
}
//--- resize selected flags
ArrayResize(m_symbol_selected,m_total_symbols);
ArrayInitialize(m_symbol_selected,true);
//--- prepare structure for symbols
ArrayResize(m_symbols_data,m_total_symbols);
for(int i=0; i<m_total_symbols; i++)
ZeroMemory(m_symbols_data[i]);
for(int i=0; i<m_total_symbols; i++)
{
m_symbols_data[i].name=symbols_list[i];
m_symbols_data[i].synchronized_flag=false;
}
//--- check data
if(!SymbolsDataReady(start_date))
{
PrintFormat("Error. Symbols not synchronized.");
return(false);
}
//---
m_date_start=start_date;
m_date_finish=finish_date;
//--- Pass 1. prepare time synchronized data with gaps marked with rates[k].close=0;
for(int i=0; i<m_total_symbols; i++)
{
if(CopyRatesTimeSynchronized(m_symbols_data[i].name,m_timeframe,m_date_start,m_date_finish,m_symbols_data[i].rates))
{
double point_size=SymbolInfoDouble(m_symbols_data[i].name,SYMBOL_POINT);
if(point_size==0)
{
PrintFormat("Wrong point size =%f for symbol=%s. ",point_size,m_symbols_data[i].name);
return(false);
}
}
else
{
Print("Error in CopyRatesTimeSynchronized for symbol = %s ",m_symbols_data[i].name);
return(false);
}
}
//--- Pass 2. Filter nonzero quotes for all symbols
FilterNonzeroRates();
//--- Pass 3. Calculate return prices in points for all symbol
for(int i=0; i<m_total_symbols; i++)
{
PreparePriceReturnsInPoints(i);
}
m_data_count=ArraySize(m_symbols_data[0].returns_prices_points);
m_window_size_bars = 100;
if(m_data_count<m_window_size_bars)
{
PrintFormat("Window size=%d is too small.",m_window_size_bars);
return(false);
}
m_index_boundary1 = 0;
m_index_boundary2 = m_data_count-1-m_window_size_bars;
m_index_current = 200;
m_index_step_bars = 1;
m_data_size=m_total_symbols;
//---
return(true);
}
//+------------------------------------------------------------------+
//| CorrelationPearson |
//+------------------------------------------------------------------+
double CorrelationPearson(double &X[], double &Y[])
{
int N = ArraySize(X);
double sum_X = 0, sum_Y = 0, sum_XY = 0;
double squareSum_X = 0, squareSum_Y = 0;
for(int i=0; i<N; i++)
{
sum_X += X[i];
sum_Y += Y[i];
sum_XY += X[i]*Y[i];
squareSum_X += X[i]*X[i];
squareSum_Y += Y[i]*Y[i];
}
double multi_sigma=(N*squareSum_X-sum_X*sum_X)*(N*squareSum_Y-sum_Y*sum_Y);
if(multi_sigma==0)
return(0);
double corr = (double)(N*sum_XY-sum_X*sum_Y)/MathSqrt(multi_sigma);
//---
return(corr);
}
//+------------------------------------------------------------------+
//| CopyData |
//+------------------------------------------------------------------+
bool CopyData(double &data_array[],const int index,int count,double &out_data_array[])
{
int size=ArraySize(data_array);
if(size==0)
return(false);
//---
if(index+count>size)
{
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<count; i++)
{
out_data_array[i]=data_array[i+index];
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| CalculatePearsonCoefficient |
//+------------------------------------------------------------------+
bool CalculatePearsonCoefficient(double &value,const int ind1,const int ind2, const int data_start_index, const int count)
{
value=0.0;
//--- check parameters
if(ind1<0 || ind1>=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; i<m_data_size; i++)
for(int j=0; j<m_data_size; j++)
{
int idx=i*m_data_size+j;
m_data[idx]=0;
m_boxes[idx].Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),DXVector3(-0.45f,0.0,-0.45f),DXVector3(0.45f,1.0f,0.45f));
m_boxes[idx].SpecularColorSet(DXColor(1.0f,1.0f,1.0f,0.5f));
m_boxes[idx].SpecularPowerSet(128.0);
m_canvas.ObjectAdd(&m_boxes[idx]);
}
//--- success
return(true);
}
//+------------------------------------------------------------------+
//| Create |
//+------------------------------------------------------------------+
virtual bool Create(const int width,const int height,color background_color)
{
if(m_data_size<1)
{
PrintFormat("No symbols selected.");
return(false);
}
//--- prepare the chart
ChartSetInteger(0,CHART_SHOW,false);
ChartRedraw();
//--- save sizes
m_width=width;
m_height=height;
if(m_width<1)
m_width=1;
if(m_height<1)
m_height=1;
//---
ResetLastError();
if(!m_canvas.CreateBitmapLabel("CorrelationMatrix3D",0,0,m_width,m_height,COLOR_FORMAT_ARGB_NORMALIZE))
{
Print("Error creating canvas: ",GetLastError());
return(false);
}
//--- set colors
m_background_color=ColorToARGB(background_color);
if((GETRGBR(m_background_color)+GETRGBG(m_background_color)+GETRGBB(m_background_color))/3<128)
m_text_color=ColorToARGB(clrWhite);
else
m_text_color=ColorToARGB(clrNavy);
//---
m_canvas.ProjectionMatrixSet((float)M_PI/6,(float)width/height,0.1f,100.0f);
m_canvas.ViewTargetSet(DXVector3(0.0,0.0,0.0));
m_canvas.ViewUpDirectionSet(DXVector3(0.0,1.0,0.0));
m_canvas.LightColorSet(DXColor(1.0f,1.0f,0.9f,0.55f));
m_canvas.AmbientColorSet(DXColor(0.9f,0.9f,1.0f,0.55f));
m_angle_y=DX_PI/12.0f;
m_angle_x=DX_PI/4.0f;
//--- prepare data and boxes
ArrayResize(m_data,m_data_size*m_data_size);
if(!PrepareBoxes())
return(false);
//--- prepare label points
ArrayResize(m_labels_x,2*m_data_size);
ArrayResize(m_labels_z,2*m_data_size);
//--- redraw data
RedrawData();
//--- succeed
return(true);
}
//+------------------------------------------------------------------+
//| Draw labels |
//+------------------------------------------------------------------+
void DrawLabels()
{
int alignment=TA_CENTER|TA_VCENTER;
//--- set font size
int font_size=int(13.0*m_height/600.0+0.5);
if(font_size<8)
font_size=8;
if(font_size>54)
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; i<m_data_size; i++)
{
tx=(int)(m_width*(0.5f+0.5f*m_labels_x[i].x));
ty=(int)(m_height*(0.5f-0.5f*m_labels_x[i].y));
m_canvas.TextOut(tx,ty,m_symbols_data[i].name,m_text_color,alignment);
}
}
else
{
for(int i=0; i<m_data_size; i++)
{
int idx=i+m_data_size;
tx=(int)(m_width*(0.5f+0.5f*m_labels_x[idx].x));
ty=(int)(m_height*(0.5f-0.5f*m_labels_x[idx].y));
m_canvas.TextOut(tx,ty,m_symbols_data[i].name,m_text_color,alignment);
}
}
//--- draw z axis on right side
dx1=m_labels_z[m_data_size-1].x-m_labels_z[0].x;
dx2=m_labels_z[m_data_size].x-m_labels_z[2*m_data_size-1].x;
//--- draw z axis on left side
if(dx2>dx1)
{
for(int i=0; i<m_data_size; i++)
{
tx=(int)(m_width*(0.5f+0.5f*m_labels_z[i].x));
ty=(int)(m_height*(0.5f-0.5f*m_labels_z[i].y));
m_canvas.TextOut(tx,ty,m_symbols_data[i].name,m_text_color,alignment);
}
}
else
{
for(int i=0; i<m_data_size; i++)
{
int idx=i+m_data_size;
tx=(int)(m_width*(0.5f+0.5f*m_labels_z[idx].x));
ty=(int)(m_height*(0.5f-0.5f*m_labels_z[idx].y));
m_canvas.TextOut(tx,ty,m_symbols_data[i].name,m_text_color,alignment);
}
}
}
//+------------------------------------------------------------------+
//| SetSelected |
//+------------------------------------------------------------------+
void SetSelected(int number)
{
if(number<0 || number>=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; i<m_data_size; i++)
{
for(int j=i; j<m_data_size; j++)
{
if(i==j)
{
m_data[i*m_data_size+j]=1.0;
}
else
{
double value=0;
if(CalculatePearsonCoefficient(value,i,j,m_index_current,m_window_size_bars))
{
m_data[i*m_data_size+j]=value;
m_data[j*m_data_size+i]=value;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Update frame |
//+------------------------------------------------------------------+
void Redraw()
{
//--- update
m_canvas.Render(DX_CLEAR_COLOR|DX_CLEAR_DEPTH,m_background_color);
string str=TimeToString(m_date_current,m_time_format);
m_canvas.FontSet("Arial",64,FW_BLACK);
m_canvas.TextOut(25,15,str,m_text_color,0);
//--- draw axis labels
DrawLabels();
//---
m_canvas.Update();
}
//+------------------------------------------------------------------+
//| RedrawData |
//+------------------------------------------------------------------+
void RedrawData()
{
UpdateCorrelationMatrix();
UpdateCamera();
UpdateBoxes();
//---
Redraw();
}
//+------------------------------------------------------------------+
//| Process mouse moving event |
//+------------------------------------------------------------------+
void OnMouseEvent(int x,int y,uint flags)
{
if((flags&1)==1)
{
if(m_mouse_x_old!=-1)
{
m_angle_y+=(x-m_mouse_x_old)/300.0f;
m_angle_x+=(y-m_mouse_y_old)/300.0f;
if(m_angle_x<-DX_PI*0.49f)
m_angle_x=-DX_PI*0.49f;
if(m_angle_x>DX_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_current<m_index_boundary1)
{
m_index_step_bars *= -1;
m_index_current=m_index_boundary1;
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);
}
else
if(m_index_current>m_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);
};
//+------------------------------------------------------------------+