Article-20596-MQL5-MVC-Symb.../iCorrelationTable.mq5
2026-03-30 13:46:46 +07:00

521 lines
26 KiB
MQL5

//+------------------------------------------------------------------+
//| iCorrelationTable.mq5 |
//| Copyright 2023, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property indicator_separate_window
#property indicator_buffers 0
#property indicator_plots 0
#define CHART_FLOAT_WIDTH 750 // Ширина открываемого графика символов
#define CHART_FLOAT_HEIGHT 500 // Высота открываемого графика символов
//+------------------------------------------------------------------+
//| Включаемые библиотеки |
//+------------------------------------------------------------------+
#include "Controls\Controls.mqh" // Библиотека элементов управления
//--- input parameters
input(name="Bars Total (at least 10)") uint InpBarsTotal = 1000; // Количество баров данных для расчёта корреляции (не менее 10)
input(name="Timeframe") ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; // Таймфрейм данных для расчёта корреляции
input(name="Symbols for Correlation") string InpSymbols = "EURUSD,GBPUSD,USDJPY,USDCHF,AUDUSD,NZDUSD,USDCAD"; // Символы для расчёта корреляции
//--- global variables
string ExtSymbolsArray[]; // Массив символов для расчёта корреляции
matrix ExtPricesData; // Матрица данных символов (цены Close)
uint ExtBarsTotal; // Количество баров данных для расчёта корреляции
matrix ExtCorrelationMatrix; // Матрица рассчитанных парных корреляций между всеми символами
bool ExtDataReady; // Флаг готовности данных по всем символам
long ExtSymbolsChart; // Идентификатор нового графика для символов корреляции
CTableControl *ExtTableCtrl; // Указатель на объект CTableControl
CTableView *ExtTableView; // Указатель на объект визуального представления таблицы
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
//--- Идентификатор открываемого графика
ExtSymbolsChart=0;
//--- Ищем подокно графика
int wnd=ChartWindowFind();
//--- Заполняем массив символов из указанных во входном параметре InpSymbols
string sep=","; // разделитель в виде символа
ushort u_sep; // код символа разделителя
//--- получим код разделителя
u_sep=StringGetCharacter(sep,0);
//--- получим из строки InpSymbols подстроки по разделителю u_sep и запишем их в массив ExtSymbolsArray
StringSplit(InpSymbols,u_sep,ExtSymbolsArray);
//--- Распечатаем в журнале набор символов для расчёта корреляции
Print("\nSymbols Array:");
ArrayPrint(ExtSymbolsArray);
//--- Включаем все символы в обзор рынка
SymbolsSelect(ExtSymbolsArray);
//--- Получаем данные символов (не менее 10 баров) для расчёта корреляции
ExtBarsTotal=(InpBarsTotal<10 ? 10 : InpBarsTotal);
ExtDataReady=GetAndCalculateData(ExtBarsTotal);
//--- Создаём графический элемент управления таблицами
int w=500;
int h=138;
ExtTableCtrl=new CTableControl("TableControl0",0,wnd,8,8,w-0,h-0);
if(ExtTableCtrl==NULL)
{
Print("Error. Failed to create TableControl object");
return INIT_FAILED;
}
//--- На графике обязательно должен быть один главный элемент
ExtTableCtrl.SetAsMain();
//--- Можно установить параметры созданного элемента управления таблицами
ExtTableCtrl.SetID(0); // Идентификатор
ExtTableCtrl.SetName("Table Control 0"); // Наименование
//--- Если данные символов и их корреляции успешно получены,
//--- создаём объект таблицы 0 (компонент Model + View) внутри элемента управления таблицами
//--- из вышесозданной матрицы ExtCorrelationMatrixSymmetric и
//--- string-массива символов ExtSymbolsArray как заголовков столбцов
if(ExtDataReady && !CreateTable(ExtTableCtrl))
return INIT_FAILED;
//--- Всё успешно
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int32_t reason)
{
//--- Удаляем элемент управления таблицами и уничтожаем менеджер общих ресурсов библиотеки
delete ExtTableCtrl;
CCommonManager::DestroyInstance();
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int32_t rates_total,
const int32_t prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int32_t &spread[])
{
//--- Получаем данные пока не будут готовы
ExtDataReady=GetAndCalculateData(ExtBarsTotal);
if(!ExtDataReady)
{
Print("The symbol data and their correlations have not yet been obtained. Waiting for the next tick...");
return 0;
}
//--- Если таблица ещё не создана
//--- создаём объект таблицы 0 (компонент Model + View) внутри элемента управления таблицами
//--- из вышесозданной матрицы ExtCorrelationMatrixSymmetric и
//--- string-массива символов ExtSymbolsArray как заголовков столбцов
if(ExtTableView==NULL && !CreateTable(ExtTableCtrl))
return 0;
//--- Обновляем данные в таблице с установкой цветов корреляции
UpdateTableValuesAndColors(ExtTableCtrl.GetTableView(0),ExtCorrelationMatrix);
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//--- Раз в полторы минуты получаем данные по символам из массива
static int count=0;
count++;
if(count>=3000)
{
double array[];
for(int i=0;i<(int)ExtSymbolsArray.Size();i++)
CopyClose(ExtSymbolsArray[i],InpTimeframe,0,ExtBarsTotal,array);
count=0;
}
//--- Вызываем обработчик OnTimer элемента управления таблицами
ExtTableCtrl.OnTimer();
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int32_t id,
const long &lparam,
const double &dparam,
const string &sparam)
{
//--- Вызываем обработчик OnChartEvent элемента управления таблицами
ExtTableCtrl.OnChartEvent(id,lparam,dparam,sparam);
if(id>=CHARTEVENT_CUSTOM)
{
//--- Преобразуем идентификатор полученного пользовательского события к значениям стандартных событий
ENUM_CHART_EVENT chart_event=ENUM_CHART_EVENT(id-CHARTEVENT_CUSTOM);
//--- Если событие щелчка по графическому объекту
if(chart_event==CHARTEVENT_OBJECT_CLICK)
{
//--- Если в имени события (значение sparam) присутствует наименование строки таблицы (начинается с "TableCellView")
if(StringFind(sparam,"TableCellView")==0)
{
//--- Получаем номер строки и столбца из параметров сорбытия
int row=(int)lparam;
int col=(int)dparam;
string sep=";"; // разделитель в виде символа
ushort u_sep; // код символа разделителя
string result[]; // массив для получения строк
//--- Получим код разделителя и разделим sparam на подстроки
u_sep=StringGetCharacter(sep,0);
int n=StringSplit(sparam,u_sep,result);
//--- Должно быть три подстроки
if(n==3)
{
//--- Получаем символ строки и символ столбца
string row_symb=result[1];
string col_symb=result[2];
//--- Если график ещё не открыт - открываем его
if(ExtSymbolsChart==0 || !IsExistChart(ExtSymbolsChart))
ExtSymbolsChart=OpenCharts(row_symb,col_symb);
//--- Если график уже открыт
if(ExtSymbolsChart!=0)
{
//--- Устанавливаем символы для двух объектов-графиков и перерисовываем график
ObjectSetString(ExtSymbolsChart,"ChartRowSymbol",OBJPROP_SYMBOL,row_symb);
ObjectSetString(ExtSymbolsChart,"ChartColSymbol",OBJPROP_SYMBOL,col_symb);
ChartRedraw(ExtSymbolsChart);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Включает символы из массива в обзор рынка |
//+------------------------------------------------------------------+
bool SymbolsSelect(string &array[])
{
bool res=true;
for(int i=0;i<(int)array.Size();i++)
res &=SymbolSelect(array[i],true);
return res;
}
//+------------------------------------------------------------------+
//| Возвращает символ по индексу массива |
//+------------------------------------------------------------------+
string GetSymbolByIndex(const int index,string &array[])
{
int total=(int)array.Size();
if(index<0 || index>total-1)
return StringFormat("%s: Error. Invalid index (%d)",__FUNCTION__,index);
return array[index];
}
//+------------------------------------------------------------------+
//| Заполняет матрицу данных символов |
//+------------------------------------------------------------------+
bool SymbolsDataMatrixFill(const ENUM_TIMEFRAMES timeframe,string &array[],matrix &data,const int data_count)
{
//--- В цикле по количеству символов в массиве array
int total=(int)array.Size();
for(int i=0; i<total; i++)
{
//--- получаем цены закрытия в количестве data_count
double close[];
int copied=CopyClose(array[i], timeframe, 0, data_count, close);
if(copied!=data_count)
return false;
//--- Записываем цены в строку матрицы так, чтобы 0 ячейка строки соответствовала 0 бару
for(int j=0; j<data_count; j++)
{
string symbol=GetSymbolByIndex(i,array);
int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
data[i][data_count-1-j]=NormalizeDouble(close[j],digits);
}
}
return true;
}
//+------------------------------------------------------------------+
//|Рассчитывает симметричную матрицу корреляций между всеми символами|
//+------------------------------------------------------------------+
bool SymbolsCorrelationMatrixSymmetric(const matrix &data, matrix &correlation)
{
int symb_total=(int)data.Rows(); // количество символов
//--- Устанавливаем размер матрицы корреляций
if(!correlation.Resize(symb_total,symb_total))
return false;
//--- Внешний цикл по всем символам (строкам)
for(int i=0;i<symb_total;i++)
{
//--- Получаем временной ряд цен для символа i
vector vi=data.Row(i);
//--- Внутренний цикл по всем символам (столбцам)
for(int j=0;j<symb_total;j++)
{
//--- Если символы в строке и столбце одинаковы, то это корреляция с собой
if(i==j)
correlation[i][j]=1.0;
//--- Символы в строке и столбце различаются
else
{
//--- Получаем временной ряд цен для символа j и считаем корреляцию между символами i и j
vector vj=data.Row(j);
correlation[i][j]=vi.CorrCoef(vj);
}
}
}
//--- Всё успешно
return true;
}
//+------------------------------------------------------------------+
//| Распечатывает в журнале симметричную матрицу корреляций |
//+------------------------------------------------------------------+
void SymbolsCorrelationMatrixSymmetricPrint(const string &symb_array[],matrix &correlation)
{
//--- Создаём и распечатываем заголовок
Print("Correlation matrix:");
string header=" ";
for(int j=0;j<(int)symb_array.Size();j++)
header+=symb_array[j]+" ";
Print(header);
//--- Распечатываем данные корреляции символов
for(int i=0;i<(int)symb_array.Size();i++)
{
string row=symb_array[i]+" ";
for(int j=0;j<(int)symb_array.Size();j++)
row+=DoubleToString(correlation[i][j],2)+" ";
Print(row);
}
}
//+------------------------------------------------------------------+
//| Получает и рассчитывает все необходимые данные |
//+------------------------------------------------------------------+
bool GetAndCalculateData(uint bars_total)
{
//--- Получаем значения для данных по символам
const int symb_total=(int)ExtSymbolsArray.Size(); // количество символов
//--- Изменяем размер матрицы: строки - символы, столбцы - бары
if(!ExtPricesData.Resize(symb_total,bars_total))
{
Print("Error. Failed to resize the symbol data matrix");
return false;
}
//--- Заполняем матрицу ценами закрытия символов и присваиваем значение флагу готовности данных
ExtDataReady=SymbolsDataMatrixFill(InpTimeframe,ExtSymbolsArray,ExtPricesData,bars_total);
if(!ExtDataReady)
return false;
//--- Рассчитываем симметричную матрицу корреляций
if(!SymbolsCorrelationMatrixSymmetric(ExtPricesData,ExtCorrelationMatrix))
{
Print("Error calculating correlation matrix");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Создаёт на панели таблицу символов с данными их корреляций |
//+------------------------------------------------------------------+
bool CreateTable(CTableControl *table_ctrl)
{
//--- создаём объект таблицы 0 (компонент Model + View) внутри элемента управления таблицами
//--- из вышесозданной матрицы ExtCorrelationMatrixSymmetric и
//--- string-массива символов ExtSymbolsArray как заголовков столбцов
ExtTableView=table_ctrl.TableCreate(ExtCorrelationMatrix,ExtSymbolsArray,ExtSymbolsArray);
if(ExtTableView==NULL)
return false;
//--- Установим для столбцов вывод текста по центру ячейки
int total=(int)table_ctrl.RowsTotal(0);
for(int i=0;i<total;i++)
{
table_ctrl.ColumnSetTextAnchor(0,i,ANCHOR_CENTER,true,false);
}
//--- Установим режим подсветки строк таблицы для отдельных ячеек
table_ctrl.SetRowsHighlightMode(0,ROWS_HIGHLIGHT_MODE_CELLS);
//--- и сделаем таблицу несортируемой
table_ctrl.SetSortable(0,false);
//--- Нарисуем и раскрасим таблицу в цвета корреляции символов
table_ctrl.Draw(false);
UpdateTableValuesAndColors(table_ctrl.GetTableView(0),ExtCorrelationMatrix);
//--- Получим модель таблицы с индексом 0 и распечатаем в журнале
CTable *table_model=table_ctrl.GetTableModel(0);
table_model.Print(7);
//--- Всё успешно
return true;
}
//+------------------------------------------------------------------+
//| Обновляет значения и цвета ячеек таблицы по матрице корреляций |
//+------------------------------------------------------------------+
void UpdateTableValuesAndColors(CTableView *table_view, matrix &corr_matrix)
{
//--- Проверим валидность указателя на таблицу
if(table_view==NULL)
return;
//--- Количество строк и столбцов таблицы
int total_row=table_view.RowsTotal();
int total_col=table_view.CellsInRow(0);
//--- В цикле по строкам таблицы
for(int r=0; r<total_row; r++)
{
//--- получаем очередной объект визуального представления строки
CTableRowView *row_obj=table_view.GetRowView(r);
if(row_obj==NULL)
continue;
//--- Получаем элемент управления цветом
CColorElement *ce=row_obj.GetBackColorControl();
if(ce==NULL)
continue;
//--- В цикле по количеству ячеек в строке
for(int c=0; c<total_col; c++)
{
//--- получаем очередной объект визуального представления ячейки
CTableCellView *cell=table_view.GetCellView(r,c);
if(cell==NULL)
continue;
//--- Берём значение корреляции из матрицы
double val=corr_matrix[r][c];
//--- Обновляем текст ячейки,
cell.SetText(DoubleToString(val,2));
//--- обновляем цвет ячейки
color new_color=ce.InterpolateColorByCoeff(clrRed,clrYellow,clrGreen,val);
cell.SetBackColor(new_color);
//--- Перерисовываем ячейку (график обновляем на последней ячейке таблицы)
bool flag=(r==total_row-1 && c==total_col-1 ? true : false);
cell.Draw(flag);
}
}
}
//+------------------------------------------------------------------+
//| Возвращает флаг существования графика с указанным идентификатором|
//+------------------------------------------------------------------+
bool IsExistChart(const long id)
{
//--- Переменные для идентификаторов графиков
long curr_chart=0, prev_chart=0;
int i=0;
//--- Проходим по всем графикам
while(!IsStopped() && i<CHARTS_MAX)
{
//--- На основании предыдущего получим новый график
curr_chart=ChartNext(prev_chart); // prev_chart==0 - получить первый график
//--- Если достигли конца списка графиков - выходим из цикла
if(curr_chart<0)
break;
//--- Если идентификатор графика совпадает с искомым - такой график есть
if(curr_chart==id)
return true;
//--- Запомним идентификатор текущего графика для следующего ChartNext()
prev_chart=curr_chart;
//--- увеличиваем счетчик
i++;
}
//--- Нет искомого графика
return false;
}
//+------------------------------------------------------------------+
//| Открывает графики символов |
//+------------------------------------------------------------------+
long OpenCharts(const string row_symb,const string col_symb)
{
//--- устанавливаем символ и таймфрейм для нового графика
string symbol=row_symb;
if(symbol==NULL || symbol=="")
symbol=Symbol();
//--- открываем новый график с заданными символом и периодом
long id=ChartOpen(symbol,PERIOD_CURRENT);
if(id==0)
{
Print("ChartOpen() failed. Error ", GetLastError());
return 0;
}
//--- Открепляем график и делаем его пустым
ChartSetInteger(id,CHART_IS_DOCKED,false);
ChartSetInteger(id,CHART_SHOW,false);
//--- Получаем координаты сторон откреплённого графика
int top=(int)ChartGetInteger(id,CHART_FLOAT_TOP);
int bottom=(int)ChartGetInteger(id,CHART_FLOAT_BOTTOM);
int left=(int)ChartGetInteger(id,CHART_FLOAT_LEFT);
int right=(int)ChartGetInteger(id,CHART_FLOAT_RIGHT);
//--- Устанавливаем новые ширину и высоту графика
ChartSetInteger(id,CHART_FLOAT_RIGHT,left+CHART_FLOAT_WIDTH);
ChartSetInteger(id,CHART_FLOAT_BOTTOM,top+CHART_FLOAT_HEIGHT);
//--- Получаем размеры графика в пикселях
int cw=(int)ChartGetInteger(id,CHART_WIDTH_IN_PIXELS);
int ch=(int)ChartGetInteger(id,CHART_HEIGHT_IN_PIXELS);
//--- Задаём высоту для верхнего и нижнего объектов-графиков
int h0=(int)round(ch/2);
int h1=ch-h0;
//--- Создаём на откреплённом графике два объекта-графика с символами строки и столбца
if(!CreateChartObject(id,"ChartRowSymbol",row_symb,PERIOD_CURRENT,0,0,cw,h0))
return 0;
if(!CreateChartObject(id,"ChartColSymbol",col_symb,PERIOD_CURRENT,0,h0-1,cw,h1+1))
return 0;
//--- Обновляем открытый график и возвращаем его идентификатор
ChartRedraw(id);
return id;
}
//+------------------------------------------------------------------+
//| Создаёт объект-график указанного символа |
//+------------------------------------------------------------------+
bool CreateChartObject(const long chart_id,const string name,const string symbol,const ENUM_TIMEFRAMES timeframe,const int x,const int y,const int w,const int h)
{
//--- Создаём объект-график с указанными координатами и размерами
//--- и устанавливаем его свойства - символ, период, координаты и размеры
if(ObjectCreate(chart_id,name,OBJ_CHART,0,x,y,w,h))
{
ObjectSetString(chart_id,name,OBJPROP_SYMBOL,symbol);
ObjectSetInteger(chart_id,name,OBJPROP_PERIOD,timeframe);
ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y);
ObjectSetInteger(chart_id,name,OBJPROP_XSIZE,w);
ObjectSetInteger(chart_id,name,OBJPROP_YSIZE,h);
return true;
}
//--- Ошибка создания объекта-графика
return false;
}
//+------------------------------------------------------------------+