1002 lines
76 KiB
MQL5
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);
|
|
};
|
|
//+------------------------------------------------------------------+
|