mql5/Experts/Examples/Correlation Matrix 3D/Correlation Matrix 3D.mq5

1003 lines
76 KiB
MQL5

2025-08-05 01:57:33 -04:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| 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