1901 行
187 KiB
MQL5
1901 行
187 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| FrameViewer.mqh |
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
|
|
#include "Controls.mqh" // Классы контроллов
|
|
#include "Table.mqh" // Класс таблиц
|
|
#include <Arrays\ArrayDouble.mqh> // Массив вещественных данных
|
|
|
|
#define CELL_W 128 // Ширина ячеек таблицы
|
|
#define CELL_H 19 // Высота ячейки таблицы
|
|
#define BUTT_RES_W CELL_W+30 // Ширина кнопки выбора результата оптимизации
|
|
#define DATA_COUNT 8 // Количество данных
|
|
#define FRAME_ID 1 // Идентификатор фреймов
|
|
#define TABLE_OPT_STAT_ID 1 // Идентификатор таблицы статистики на вкладке оптимизации
|
|
#define TABLE_OPT_INP_ID 2 // Идентификатор таблицы входных параметров на вкладке оптимизации
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Класс прогресс-бара, рисующий двумя цветами |
|
|
//+------------------------------------------------------------------+
|
|
class CColorProgressBar :public CObject
|
|
{
|
|
private:
|
|
CCanvas *m_background; // Указатель на объект класса CCanvas для рисования на фоне
|
|
CCanvas *m_foreground; // Указатель на объект класса CCanvas для рисования на переднем плане
|
|
CRect m_bound; // Координаты и размеры рабочей области
|
|
color m_good_color, m_bad_color; // Цвета прибыльной и убыточной серий
|
|
color m_back_color, m_fore_color; // Цвета фона и рамки
|
|
bool m_passes[]; // Количество обработанных проходов
|
|
int m_last_index; // Номер последнего прохода
|
|
public:
|
|
//--- Конструктор/деструктор
|
|
CColorProgressBar(void);
|
|
~CColorProgressBar(void){};
|
|
|
|
//--- Устанавливает указатель на канвас
|
|
void SetCanvas(CCanvas *background, CCanvas *foreground)
|
|
{
|
|
if(background==NULL)
|
|
{
|
|
::Print(__FUNCTION__, ": Error. Background is NULL");
|
|
return;
|
|
}
|
|
if(foreground==NULL)
|
|
{
|
|
::Print(__FUNCTION__, ": Error. Foreground is NULL");
|
|
return;
|
|
}
|
|
|
|
this.m_background=background;
|
|
this.m_foreground=foreground;
|
|
}
|
|
//--- Устанавливает координаты и размеры рабочей области на канвасе
|
|
void SetBound(const int x1, const int y1, const int x2, const int y2)
|
|
{
|
|
this.m_bound.SetBound(x1, y1, x2, y2);
|
|
}
|
|
//--- Возврат координат границ прямоугольной области
|
|
int X1(void) const { return this.m_bound.left; }
|
|
int Y1(void) const { return this.m_bound.top; }
|
|
int X2(void) const { return this.m_bound.right; }
|
|
int Y2(void) const { return this.m_bound.bottom; }
|
|
|
|
//--- Установка цвета фона и рамки
|
|
void SetBackColor(const color clr) { this.m_back_color=clr; }
|
|
void SetForeColor(const color clr) { this.m_fore_color=clr; }
|
|
//--- Возврат цвета фона и рамки
|
|
color BackColor(void) const { return this.m_back_color; }
|
|
color ForeColor(void) const { return this.m_fore_color; }
|
|
//--- Сбрасывает счетчик в ноль
|
|
void Reset(void) { this.m_last_index=0; }
|
|
//--- Добавляет результат для отрисовки полоски в прогресс-баре
|
|
void AddResult(bool good, const bool chart_redraw);
|
|
//--- Обновляет прогресс-бар на графике
|
|
void Update(const bool chart_redraw);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Конструктор |
|
|
//+------------------------------------------------------------------+
|
|
CColorProgressBar::CColorProgressBar() : m_last_index(0), m_good_color(clrSeaGreen), m_bad_color(clrLightPink)
|
|
{
|
|
//--- Зададим размер массива проходов с запасом
|
|
::ArrayResize(this.m_passes, 5000, 1000);
|
|
::ArrayInitialize(this.m_passes, 0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Добавление результата |
|
|
//+------------------------------------------------------------------+
|
|
void CColorProgressBar::AddResult(bool good, const bool chart_redraw)
|
|
{
|
|
this.m_passes[this.m_last_index]=good;
|
|
//--- Добавим еще одну вертикальную черту нужного цвета в прогресс-бар
|
|
this.m_foreground.LineVertical(this.X1()+1+this.m_last_index, this.Y1()+1, this.Y2()-1, ::ColorToARGB(good ? this.m_good_color : this.m_bad_color));
|
|
//--- Обновление на графике
|
|
this.m_foreground.Update(chart_redraw);
|
|
//--- Обновление индекса
|
|
this.m_last_index++;
|
|
if(this.m_last_index>=this.m_bound.Width()-1)
|
|
this.m_last_index=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Обновление прогресс-бара на графике |
|
|
//+------------------------------------------------------------------+
|
|
void CColorProgressBar::Update(const bool chart_redraw)
|
|
{
|
|
//--- Зальем фон цветом фона
|
|
this.m_background.FillRectangle(this.X1(), this.Y1(), this.X2(), this.Y2(), ::ColorToARGB(this.m_back_color));
|
|
//--- Нарисуем рамку
|
|
this.m_background.Rectangle(this.X1(), this.Y1(), this.X2(), this.Y2(), ::ColorToARGB(this.m_fore_color));
|
|
//--- Обновим чарт
|
|
this.m_background.Update(chart_redraw);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
//| Класс для отрисовки графиков статистики и таблиц |
|
|
//| результатов оптимизации и параметров настройки советника |
|
|
//+------------------------------------------------------------------+
|
|
class CStatChart: public CObject
|
|
{
|
|
private:
|
|
color m_back_color; // Цвет фона
|
|
color m_fore_color; // Цвет рамки
|
|
int m_line_width; // Толщина линии в пискелях
|
|
int m_lines; // Количество линий на графике
|
|
CArrayDouble m_seria[]; // Массивы для хранения значений графика
|
|
bool m_profitseria[]; // Прибыльная серия или нет
|
|
int m_lastseria_index; // Индекс свежей линии на графике
|
|
color m_profit_color; // Цвет прибыльной серии
|
|
color m_loss_color; // Цвет убыточной серии
|
|
color m_selected_color; // Цвет выбранной лучшей серии
|
|
|
|
protected:
|
|
CCanvas *m_background; // Указатель на объект класса CCanvas для рисования на фоне
|
|
CCanvas *m_foreground; // Указатель на объект класса CCanvas для рисования на переднем плане
|
|
CRect m_bound_chart; // Рабочая область графика
|
|
CRect m_bound_head; // Рабочая область заголовка чарта
|
|
CColorProgressBar m_progress_bar; // Прогресс-бар
|
|
CButton m_button_replay; // Кнопка воспроизведения
|
|
CButtonSwitch m_button_res; // Кнопка выбора одного из трёх лучших результатов
|
|
int m_tab_id; // Идентификатор вкладки
|
|
|
|
public:
|
|
//--- Конструктор/деструктор
|
|
CStatChart() : m_lastseria_index(0), m_profit_color(clrForestGreen), m_loss_color(clrOrangeRed), m_selected_color(clrDodgerBlue), m_tab_id(0) {};
|
|
~CStatChart() { this.m_background=NULL; this.m_foreground=NULL; }
|
|
|
|
//--- Устанавливает указатель на канвас
|
|
void SetCanvas(CCanvas *background, CCanvas *foreground)
|
|
{
|
|
if(background==NULL)
|
|
{
|
|
::Print(__FUNCTION__, ": Error. Background is NULL");
|
|
return;
|
|
}
|
|
if(foreground==NULL)
|
|
{
|
|
::Print(__FUNCTION__, ": Error. Foreground is NULL");
|
|
return;
|
|
}
|
|
this.m_background=background;
|
|
this.m_foreground=foreground;
|
|
this.m_progress_bar.SetCanvas(background, foreground);
|
|
}
|
|
//--- Устанавливает координаты и размеры рабочей области чарта и прогресс-бара на канвасе
|
|
void SetChartBounds(const int x1, const int y1, const int x2, const int y2)
|
|
{
|
|
this.m_bound_chart.SetBound(x1, y1, x2, y2);
|
|
this.SetBoundHeader(x1, y1-CELL_H, x2, y1);
|
|
this.m_progress_bar.SetBound(x1, y2-CELL_H, x2, y2);
|
|
}
|
|
//--- Устанавливает координаты и размеры заголовка чарта на канвасе
|
|
void SetBoundHeader(const int x1, const int y1, const int x2, const int y2)
|
|
{
|
|
this.m_bound_head.SetBound(x1, y1, x2, y2);
|
|
}
|
|
//--- Возвращает указатель на (1) себя, (2) прогресс-бар
|
|
CStatChart *Get(void) { return &this; }
|
|
CColorProgressBar*GetProgressBar(void) { return(&this.m_progress_bar); }
|
|
|
|
//--- Установка/возврат идентификатора вкладки
|
|
void SetTabID(const int id) { this.m_tab_id=id; }
|
|
int TabID(void) const { return this.m_tab_id; }
|
|
|
|
//--- Возврат координат границ прямоугольной области чарта
|
|
int X1(void) const { return this.m_bound_chart.left; }
|
|
int Y1(void) const { return this.m_bound_chart.top; }
|
|
int X2(void) const { return this.m_bound_chart.right; }
|
|
int Y2(void) const { return this.m_bound_chart.bottom; }
|
|
//--- Возврат координат границ прямоугольной области заголовка
|
|
int HeaderX1(void) const { return this.m_bound_head.left; }
|
|
int HeaderY1(void) const { return this.m_bound_head.top; }
|
|
int HeaderX2(void) const { return this.m_bound_head.right; }
|
|
int HeaderY2(void) const { return this.m_bound_head.bottom; }
|
|
//--- Возврат координат границ прямоугольной области прогресс-бара
|
|
int ProgressBarX1(void) const { return this.m_progress_bar.X1(); }
|
|
int ProgressBarY1(void) const { return this.m_progress_bar.Y1(); }
|
|
int ProgressBarX2(void) const { return this.m_progress_bar.X2(); }
|
|
int ProgressBarY2(void) const { return this.m_progress_bar.Y2(); }
|
|
|
|
//--- Возвращает указатель на кнопку (1) воспроизведения, (2) выбора результата (3) худшего, (4) среднего, (5) лучшего результата
|
|
CButton *ButtonReplay(void) { return(&this.m_button_replay); }
|
|
CButtonSwitch *ButtonResult(void) { return(&this.m_button_res); }
|
|
CButtonTriggered *ButtonResultMin(void) { return(this.m_button_res.GetButton(0)); }
|
|
CButtonTriggered *ButtonResultMid(void) { return(this.m_button_res.GetButton(1)); }
|
|
CButtonTriggered *ButtonResultMax(void) { return(this.m_button_res.GetButton(2)); }
|
|
|
|
//--- (1) Скрывает, (2) показывает, (3) переносит на передний план кнопку вывбора результатов
|
|
bool ButtonsResultHide(void) { return(this.m_button_res.Hide()); }
|
|
bool ButtonsResultShow(void) { return(this.m_button_res.Show()); }
|
|
bool ButtonsResultBringToTop(void) { return(this.m_button_res.BringToTop()); }
|
|
|
|
//--- Создаёт кнопку воспроизведения
|
|
bool CreateButtonReplay(void)
|
|
{
|
|
if(this.m_background==NULL)
|
|
{
|
|
::PrintFormat("%s: Background is not assigned (use SetCanvas() function first)");
|
|
return false;
|
|
}
|
|
string text="Optimization Completed: Click to Replay";
|
|
int w=this.m_background.TextWidth(text);
|
|
|
|
//--- Левая верхняя координата кнопки
|
|
CPoint cp=this.m_bound_head.CenterPoint();
|
|
int x=cp.x-w/2;
|
|
int y=this.Y1()+this.m_bound_head.top-2;
|
|
|
|
//--- Создаём кнопу и устанавливаем для неё новые цвета, скрываем созданную кнопку
|
|
if(!this.m_button_replay.Create(::StringFormat("Tab%d_ButtonReplay", this.m_tab_id), text, x, y, w, CELL_H-1))
|
|
return false;
|
|
|
|
this.m_button_replay.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, C'144,238,144', C'144,228,144', C'144,218,144', clrSilver);
|
|
this.m_button_replay.SetDefaultColors(COLOR_BORDER, STATE_OFF, C'144,238,144', C'144,228,144', C'144,218,144', clrSilver);
|
|
this.m_button_replay.SetDefaultColors(COLOR_FOREGROUND, STATE_OFF, clrBlack, clrBlack, clrBlack, clrGray);
|
|
this.m_button_replay.ResetUsedColors(STATE_OFF);
|
|
this.m_button_replay.Draw(false);
|
|
this.m_button_replay.Hide();
|
|
return true;
|
|
}
|
|
|
|
//--- Создаёт кнопку выбора результатов
|
|
bool CreateButtonResults(void)
|
|
{
|
|
if(this.m_background==NULL)
|
|
{
|
|
::PrintFormat("%s: Background is not assigned (use SetCanvas() function first)");
|
|
return false;
|
|
}
|
|
//--- Левая верхняя координата кнопки
|
|
int x=this.m_bound_head.left+1;
|
|
int y=this.m_progress_bar.Y1()+CELL_H+2;
|
|
int w=BUTT_RES_W;
|
|
|
|
//--- Создаём кнопу и устанавливаем для неё новые цвета, скрываем созданную кнопку
|
|
if(!this.m_button_res.Create(::StringFormat("Tab%u_ButtonRes",this.m_tab_id), "", x, y, w, CELL_H-1))
|
|
return false;
|
|
|
|
string text[3]={"Worst result of the top 3", "Average result of the top 3", "Best result of the top 3"};
|
|
if(!this.m_button_res.AddNewButton(text, w))
|
|
return false;
|
|
this.m_button_res.GetButton(0).SetDefaultColors(COLOR_BORDER, STATE_OFF, C'228,228,228', C'228,228,228', C'228,228,228', clrSilver);
|
|
this.m_button_res.GetButton(0).ResetUsedColors(STATE_OFF);
|
|
this.m_button_res.GetButton(1).SetDefaultColors(COLOR_BORDER, STATE_OFF, C'228,228,228', C'228,228,228', C'228,228,228', clrSilver);
|
|
this.m_button_res.GetButton(1).ResetUsedColors(STATE_OFF);
|
|
this.m_button_res.GetButton(2).SetDefaultColors(COLOR_BORDER, STATE_OFF, C'228,228,228', C'228,228,228', C'228,228,228', clrSilver);
|
|
this.m_button_res.GetButton(2).ResetUsedColors(STATE_OFF);
|
|
this.m_button_res.Draw(false);
|
|
this.m_button_res.Hide();
|
|
return true;
|
|
}
|
|
|
|
//--- Устанавливает цвет фона
|
|
void SetBackColor(const color clr)
|
|
{
|
|
this.m_back_color=clr;
|
|
this.m_progress_bar.SetBackColor(clr);
|
|
}
|
|
//--- Устанавливает цвет рамки
|
|
void SetForeColor(const color clr)
|
|
{
|
|
this.m_fore_color=clr;
|
|
this.m_progress_bar.SetForeColor(clr);
|
|
}
|
|
|
|
//--- Задаёт количество линий на графике
|
|
void SetLines(const int num)
|
|
{
|
|
this.m_lines=num;
|
|
::ArrayResize(this.m_seria, num);
|
|
::ArrayResize(this.m_profitseria, num);
|
|
}
|
|
|
|
//--- Установка цвета (1) прибыльной, (2) убыточной, (3) выбранной серии
|
|
void SetProfitColorLine(const color clr) { this.m_profit_color=clr; }
|
|
void SetLossColorLine(const color clr) { this.m_loss_color=clr; }
|
|
void SetSelectedLineColor(const color clr) { this.m_selected_color=clr; }
|
|
|
|
//--- Обновление объекта на экране
|
|
void Update(color clr, const int line_width, const bool chart_redraw);
|
|
//--- Добавление данных из массива
|
|
void AddSeria(const double &array[], bool profit);
|
|
//--- Рисует график
|
|
void Draw(const int seria_index, color clr, const int line_width, const bool chart_redraw);
|
|
//--- Рисует линию в привычных координатах (слева-направо, снизу-вверх)
|
|
void Line(int x1, int y1, int x2, int y2, uint col, int size);
|
|
//--- Получение макс. и мин. значения в серии
|
|
double MaxValue(const int seria_index);
|
|
double MinValue(const int seria_index);
|
|
|
|
//--- Обработчик событий
|
|
void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
|
|
{
|
|
//--- Если кнопка воспроизведения не скрыта - вызываем её обработчик событий
|
|
if(!this.m_button_replay.IsHidden())
|
|
this.m_button_replay.OnChartEvent(id, lparam, dparam, sparam);
|
|
//--- Если кнопка выбора результата не скрыта - вызываем её обработчик событий
|
|
if(!this.m_button_res.IsHidden())
|
|
this.m_button_res.OnChartEvent(id, lparam, dparam, sparam);
|
|
}
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Обновление чарта |
|
|
//+------------------------------------------------------------------+
|
|
void CStatChart::Update(color clr, const int line_width, const bool chart_redraw)
|
|
{
|
|
//--- Если канвас для фона или переднего плана не установлен - уходим
|
|
if(this.m_background==NULL || this.m_foreground==NULL)
|
|
return;
|
|
//--- StatChart зальем фон
|
|
this.m_background.FillRectangle(this.X1(), this.Y1(), this.X2(), this.Y2(), ::ColorToARGB(this.m_back_color));
|
|
//--- StatChart нарисуем рамку
|
|
this.m_background.Rectangle(this.X1(), this.Y1(), this.X2(), this.Y2(), ::ColorToARGB(this.m_fore_color));
|
|
|
|
//--- ProgressBar зальем фон и нарисуем рамку
|
|
this.m_progress_bar.Update(false);
|
|
|
|
//--- Отрисуем каждую серию на 80% доступной площади чарта по вертикали и горизонтали
|
|
for(int i=0; i<this.m_lines; i++)
|
|
{
|
|
//--- Если цвет задан отсутствующим - используем цвета прибыльной и убыточной серий
|
|
if(clr==clrNONE)
|
|
{
|
|
clr=this.m_loss_color;
|
|
if(this.m_profitseria[i])
|
|
clr=this.m_profit_color;
|
|
}
|
|
//--- иначе - используем цвет, заданный для выбранной линии
|
|
else
|
|
clr=this.m_selected_color;
|
|
|
|
//--- Рисуем график результатов оптимизации
|
|
this.Draw(i, clr, line_width, false);
|
|
}
|
|
//--- Обновим оба канваса
|
|
this.m_background.Update(false);
|
|
this.m_foreground.Update(chart_redraw);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Добавляет новую серию данных для отрисовки на графике |
|
|
//+------------------------------------------------------------------+
|
|
void CStatChart::AddSeria(const double &array[], bool profit)
|
|
{
|
|
//--- Добавляем массив в серию номер m_lastseria_index
|
|
this.m_seria[this.m_lastseria_index].Resize(0);
|
|
this.m_seria[this.m_lastseria_index].AddArray(array);
|
|
this.m_profitseria[this.m_lastseria_index]=profit;
|
|
//--- Отслеживаем индекс последней линии (не используется в данный момент)
|
|
this.m_lastseria_index++;
|
|
if(this.m_lastseria_index>=this.m_lines)
|
|
this.m_lastseria_index=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получение максимального значения указанной серии |
|
|
//+------------------------------------------------------------------+
|
|
double CStatChart::MaxValue(const int seria_index)
|
|
{
|
|
double res=this.m_seria[seria_index].At(0);
|
|
int total=this.m_seria[seria_index].Total();
|
|
//--- Переберем массив и сравним каждые две соседние серии
|
|
for(int i=1; i<total; i++)
|
|
{
|
|
if(this.m_seria[seria_index].At(i)>res)
|
|
res=this.m_seria[seria_index].At(i);
|
|
}
|
|
//--- результат
|
|
return res;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получение минимального значения указанной серии |
|
|
//+------------------------------------------------------------------+
|
|
double CStatChart::MinValue(const int seria_index)
|
|
{
|
|
double res=this.m_seria[seria_index].At(0);;
|
|
int total=this.m_seria[seria_index].Total();
|
|
//--- Переберем массив и сравним каждые две соседние серии
|
|
for(int i=1; i<total; i++)
|
|
{
|
|
if(this.m_seria[seria_index].At(i)<res)
|
|
res=this.m_seria[seria_index].At(i);
|
|
}
|
|
//--- результат
|
|
return res;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Перегрузка базовой функции рисования |
|
|
//+------------------------------------------------------------------+
|
|
void CStatChart::Line(int x1, int y1, int x2, int y2, uint col, int size)
|
|
{
|
|
//--- Если канвас не задан - уходим
|
|
if(this.m_foreground==NULL)
|
|
return;
|
|
//--- Так как ось Y перевернута, то нужно перевернуть y1 и y2
|
|
int y1_adj=this.m_bound_chart.Height()-CELL_H-y1;
|
|
int y2_adj=this.m_bound_chart.Height()-CELL_H-y2;
|
|
|
|
//--- Рисуем сглаженную линию
|
|
//--- Если толщина линии меньше 3, то рисуем линию с использованием алгоритма сглаживания Ву
|
|
//--- (при толщине 1 и 2 в методе LineThick() вызывается метод LineWu()),
|
|
//--- иначе - рисуем сглаженную линию заданной толщины при помощи LineThick
|
|
this.m_foreground.LineThick(x1, y1_adj, x2, y2_adj,::ColorToARGB(col), (size<1 ? 1 : size), STYLE_SOLID, LINE_END_ROUND);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Отрисовка линии баланса на графике |
|
|
//+------------------------------------------------------------------+
|
|
void CStatChart::Draw(const int seria_index, color clr, const int line_width, const bool chart_redraw)
|
|
{
|
|
//--- Если канвас не задан - уходим
|
|
if(this.m_foreground==NULL)
|
|
return;
|
|
|
|
//--- Готовим коэффициенты для перевода значений в пиксели
|
|
double min=this.MaxValue(seria_index);
|
|
double max=this.MinValue(seria_index);
|
|
double size=this.m_seria[seria_index].Total();
|
|
|
|
//--- Отступы от края графика
|
|
double x_indent=this.m_bound_chart.Width()*0.05;
|
|
double y_indent=this.m_bound_chart.Height()*0.05;
|
|
|
|
//--- Вычислим коэффициенты
|
|
double k_y=(max-min)/(this.m_bound_chart.Height()-2*CELL_H-2*y_indent);
|
|
double k_x=(size)/(this.m_bound_chart.Width()-2*x_indent);
|
|
|
|
//--- Постоянные
|
|
double start_x=this.m_bound_chart.left+x_indent;
|
|
double start_y=this.m_bound_chart.bottom-2*CELL_H*2-y_indent;
|
|
|
|
//--- Теперь рисуем ломанную линию проходя по всем точкам серии
|
|
for(int i=1; i<size; i++)
|
|
{
|
|
//--- переводим значения в пиксели
|
|
int x1=(int)((i-0)/k_x+start_x); // номер значения откладываем на горизонтали
|
|
int y1=(int)(start_y-(m_seria[seria_index].At(i)-min)/k_y); // по вертикали
|
|
int x2=(int)((i-1-0)/k_x+start_x);// номер значения откладываем на горизонтали
|
|
int y2=(int)(start_y-(m_seria[seria_index].At(i-1)-min)/k_y); // по вертикали
|
|
//--- Выводим линию от предыдущей точки к текущей
|
|
this.Line(x1, y1, x2, y2, clr, line_width);
|
|
}
|
|
//--- Обновление канваса с перерисовкой графика (если флаг установлен)
|
|
this.m_foreground.Update(chart_redraw);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
//| Перечисления |
|
|
//+------------------------------------------------------------------+
|
|
enum ENUM_FRAME_PROP // Свойства фрейма
|
|
{
|
|
FRAME_PROP_PASS_NUM, // Номер прохода
|
|
FRAME_PROP_SHARPE_RATIO, // Результат Sharpe Ratio
|
|
FRAME_PROP_NET_PROFIT, // Результат Net Profit
|
|
FRAME_PROP_PROFIT_FACTOR, // Результат Profit Factor
|
|
FRAME_PROP_RECOVERY_FACTOR, // Результат Recovery Factor
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Класс данных фрейма |
|
|
//+------------------------------------------------------------------+
|
|
class CFrameData : public CObject
|
|
{
|
|
protected:
|
|
ulong m_pass; // Номер прохода
|
|
double m_sharpe_ratio; // Коэффициент Шарпа
|
|
double m_net_profit; // Общая прибыль
|
|
double m_profit_factor; // Доходность
|
|
double m_recovery_factor; // Фактор восстановления
|
|
public:
|
|
//--- Установка свойств фрейма (результатов прохода)
|
|
void SetPass(const ulong pass) { this.m_pass=pass; }
|
|
void SetSharpeRatio(const double value) { this.m_sharpe_ratio=value; }
|
|
void SetNetProfit(const double value) { this.m_net_profit=value; }
|
|
void SetProfitFactor(const double value) { this.m_profit_factor=value; }
|
|
void SetRecoveryFactor(const double value) { this.m_recovery_factor=value; }
|
|
//--- Возврат свойств фрейма (результатов прохода)
|
|
ulong Pass(void) const { return this.m_pass; }
|
|
double SharpeRatio(void) const { return this.m_sharpe_ratio; }
|
|
double NetProfit(void) const { return this.m_net_profit; }
|
|
double ProfitFactor(void) const { return this.m_profit_factor; }
|
|
double RecoveryFactor(void) const { return this.m_recovery_factor; }
|
|
|
|
//--- Описание свойств
|
|
string PassDescription(void) const { return ::StringFormat("Pass: %I64u", this.m_pass); }
|
|
string SharpeRatioDescription(void) const { return ::StringFormat("Sharpe Ratio: %.2f", this.m_sharpe_ratio); }
|
|
string NetProfitDescription(void) const { return ::StringFormat("Net Profit: %.2f", this.m_net_profit); }
|
|
string ProfitFactorDescription(void) const { return ::StringFormat("Profit Factor: %.2f", this.m_profit_factor); }
|
|
string RecoveryFactorDescription(void) const { return ::StringFormat("Recovery Factor: %.2f", this.m_recovery_factor); }
|
|
|
|
//--- Вывод в журнал свойств фрейма
|
|
void Print(void)
|
|
{
|
|
::PrintFormat("Frame %s:", this.PassDescription());
|
|
::PrintFormat(" - %s", this.SharpeRatioDescription());
|
|
::PrintFormat(" - %s", this.NetProfitDescription());
|
|
::PrintFormat(" - %s", this.ProfitFactorDescription());
|
|
::PrintFormat(" - %s", this.RecoveryFactorDescription());
|
|
}
|
|
|
|
//--- Метод сравнения двух объектов
|
|
virtual int Compare(const CObject *node,const int mode=0) const
|
|
{
|
|
//--- Вещественные значения сравниваем как двухзначные
|
|
const CFrameData *obj=node;
|
|
switch(mode)
|
|
{
|
|
case FRAME_PROP_SHARPE_RATIO : return(::NormalizeDouble(this.SharpeRatio(),2) > ::NormalizeDouble(obj.SharpeRatio(),2) ? 1 :
|
|
::NormalizeDouble(this.SharpeRatio(),2) < ::NormalizeDouble(obj.SharpeRatio(),2) ? -1 : 0);
|
|
case FRAME_PROP_NET_PROFIT : return(::NormalizeDouble(this.NetProfit(),2) > ::NormalizeDouble(obj.NetProfit(),2) ? 1 :
|
|
::NormalizeDouble(this.NetProfit(),2) < ::NormalizeDouble(obj.NetProfit(),2) ? -1 : 0);
|
|
case FRAME_PROP_PROFIT_FACTOR : return(::NormalizeDouble(this.ProfitFactor(),2) > ::NormalizeDouble(obj.ProfitFactor(),2) ? 1 :
|
|
::NormalizeDouble(this.ProfitFactor(),2) < ::NormalizeDouble(obj.ProfitFactor(),2) ? -1 : 0);
|
|
case FRAME_PROP_RECOVERY_FACTOR : return(::NormalizeDouble(this.RecoveryFactor(),2)> ::NormalizeDouble(obj.RecoveryFactor(),2) ? 1 :
|
|
::NormalizeDouble(this.RecoveryFactor(),2)< ::NormalizeDouble(obj.RecoveryFactor(),2) ? -1 : 0);
|
|
//---FRAME_PROP_PASS_NUM
|
|
default : return(this.Pass()>obj.Pass() ? 1 : this.Pass()<obj.Pass() ? -1 : 0);
|
|
}
|
|
}
|
|
|
|
//--- Конструкторы/деструктор
|
|
CFrameData (const ulong pass, const double sharpe_ratio, const double net_profit, const double profit_factor, const double recovery_factor) :
|
|
m_pass(pass), m_sharpe_ratio(sharpe_ratio), m_net_profit(net_profit), m_profit_factor(profit_factor), m_recovery_factor(recovery_factor) {}
|
|
CFrameData (void) :
|
|
m_pass(0), m_sharpe_ratio(0), m_net_profit(0), m_profit_factor(0), m_recovery_factor(0) {}
|
|
~CFrameData (void) {}
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
//| Класс просмотровщика фреймов |
|
|
//+------------------------------------------------------------------+
|
|
class CFrameViewer : public CObject
|
|
{
|
|
private:
|
|
int m_w; // Ширина графика
|
|
int m_h; // Высота графика
|
|
color m_selected_color; // Цвет выбранной серии из трёх лучших
|
|
uint m_line_width; // Толщина линии выбранной серии из трёх лучших
|
|
bool m_completed; // Флаг завершения оптимизации
|
|
|
|
CFrameData m_frame_tmp; // Объект фрейм для поиска по свойству
|
|
CArrayObj m_list_frames; // Список фреймов
|
|
CTabControl m_tab_control; // Элемент управления Tab Control
|
|
|
|
//--- Объявляем объекты вкладок на элементе управления Tab Control
|
|
//--- Вкладка 0 (Optimization) элемента управления Tab Control
|
|
CTableDataControl m_table_inp_0; // Таблица параметров оптимизации на вкладке 0
|
|
CTableDataControl m_table_stat_0; // Таблица результатов оптимизации на вкладке 0
|
|
CStatChart m_chart_stat_0; // График оптимизации на вкладке 0
|
|
CColorProgressBar*m_progress_bar; // Прогресс-бар на графике оптимизации на вкладке 0
|
|
|
|
//--- Вкладка 1 (Sharpe Ratio) элемента управления Tab Control
|
|
CTableDataControl m_table_inp_1; // Таблица параметров оптимизации на вкладке 1
|
|
CTableDataControl m_table_stat_1; // Таблица результатов оптимизации на вкладке 1
|
|
CStatChart m_chart_stat_1; // График результатов оптимизации на вкладке 1
|
|
|
|
//--- Вкладка 2 (Net Profit) элемента управления Tab Control
|
|
CTableDataControl m_table_inp_2; // Таблица параметров оптимизации на вкладке 2
|
|
CTableDataControl m_table_stat_2; // Таблица результатов оптимизации на вкладке 2
|
|
CStatChart m_chart_stat_2; // График результатов оптимизации на вкладке 2
|
|
|
|
//--- Вкладка 3 (Profit Factor) элемента управления Tab Control
|
|
CTableDataControl m_table_inp_3; // Таблица параметров оптимизации на вкладке 3
|
|
CTableDataControl m_table_stat_3; // Таблица результатов оптимизации на вкладке 3
|
|
CStatChart m_chart_stat_3; // График результатов оптимизации на вкладке 3
|
|
|
|
//--- Вкладка 4 (Recovery Factor) элемента управления Tab Control
|
|
CTableDataControl m_table_inp_4; // Таблица параметров оптимизации на вкладке 4
|
|
CTableDataControl m_table_stat_4; // Таблица результатов оптимизации на вкладке 4
|
|
CStatChart m_chart_stat_4; // График результатов оптимизации на вкладке 4
|
|
|
|
protected:
|
|
//--- Возвращает указатель на таблицу параметров оптимизации по индексу вкладки
|
|
CTableDataControl*GetTableInputs(const uint tab_id)
|
|
{
|
|
switch(tab_id)
|
|
{
|
|
case 0 : return this.m_table_inp_0.Get();
|
|
case 1 : return this.m_table_inp_1.Get();
|
|
case 2 : return this.m_table_inp_2.Get();
|
|
case 3 : return this.m_table_inp_3.Get();
|
|
case 4 : return this.m_table_inp_4.Get();
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
//--- Возвращает указатель на таблицу результатов оптимизации по индексу вкладки
|
|
CTableDataControl*GetTableStats(const uint tab_id)
|
|
{
|
|
switch(tab_id)
|
|
{
|
|
case 0 : return this.m_table_stat_0.Get();
|
|
case 1 : return this.m_table_stat_1.Get();
|
|
case 2 : return this.m_table_stat_2.Get();
|
|
case 3 : return this.m_table_stat_3.Get();
|
|
case 4 : return this.m_table_stat_4.Get();
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
//--- Возвращает указатель на график результатов оптимизации по индексу вкладки
|
|
CStatChart *GetChartStats(const uint tab_id)
|
|
{
|
|
switch(tab_id)
|
|
{
|
|
case 0 : return this.m_chart_stat_0.Get();
|
|
case 1 : return this.m_chart_stat_1.Get();
|
|
case 2 : return this.m_chart_stat_2.Get();
|
|
case 3 : return this.m_chart_stat_3.Get();
|
|
case 4 : return this.m_chart_stat_4.Get();
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
//--- Добавляет объект фрейм в список
|
|
bool AddFrame(CFrameData *frame)
|
|
{
|
|
if(frame==NULL)
|
|
{
|
|
::PrintFormat("%s: Error: Empty object passed",__FUNCTION__);
|
|
return false;
|
|
}
|
|
this.m_frame_tmp.SetPass(frame.Pass());
|
|
this.m_list_frames.Sort(FRAME_PROP_PASS_NUM);
|
|
int index=this.m_list_frames.Search(frame);
|
|
if(index>WRONG_VALUE)
|
|
return false;
|
|
return this.m_list_frames.Add(frame);
|
|
}
|
|
|
|
//--- Рисует таблицу статистики оптимизации на указанной вкладке
|
|
void TableStatDraw(const uint tab_id, const int x, const int y, const int w, const int h, const bool chart_redraw);
|
|
//--- Рисует таблицу входных параметров оптимизации на указанной вкладке
|
|
void TableInpDraw(const uint tab_id, const int x, const int y, const int w, const int h, const uint rows, const bool chart_redraw);
|
|
//--- Рисует график оптимизации на указанной вкладке
|
|
void ChartOptDraw(const uint tab_id, const bool opt_completed, const bool chart_redraw);
|
|
//--- Рисует таблицы данных и график оптимизации
|
|
void DrawDataChart(const uint tab_id);
|
|
//--- Рисует графики трёх лучших проходов по критерию оптимизации
|
|
void DrawBestFrameData(const uint tab_id, const int res_index);
|
|
//--- Управляет отображением управляющих объектов на графиках оптимизации
|
|
void ControlObjectsView(const uint tab_id);
|
|
|
|
//--- Повторное проигрывание фреймов после окончания оптимизации
|
|
void ReplayFrames(const int delay_ms);
|
|
//--- Получение данных текущего фрейма и вывод их на указанной вкладке в таблицу и на график результатов оптимизации
|
|
bool DrawFrameData(const uint tab_id, const string text, color clr, const uint line_width, ulong &pass, string ¶ms[], uint &par_count, double &data[]);
|
|
//--- Выводит данные указанного фрейма на график оптимизации
|
|
bool DrawFrameDataByPass(const uint tab_id, const ulong pass_num, const string text, color clr, const uint line_width, double &data[]);
|
|
//--- Заполняет массив индексами фреймов трёх лучших проходов для указанного критерия оптимизации (по индексу вкладки)
|
|
bool FillArrayBestFrames(const uint tab_id, ulong &array_passes[]);
|
|
//--- Выводит на графики результатов оптимизации на каждой вкладке по три лучших прохода
|
|
void DrawBestFrameDataAll(void);
|
|
//--- Ищет и возвращает указатель на объект фрейма, со значением свойства меньше образца
|
|
CFrameData *FrameSearchLess(CFrameData *frame, const int mode);
|
|
|
|
public:
|
|
//--- Установка толщины выбранной линии
|
|
void SetSelectedLineWidth(const uint width) { this.m_line_width=width; }
|
|
|
|
//--- Установка цвета прибыльной серии
|
|
void SetProfitColorLine(const color clr)
|
|
{
|
|
int total=this.m_tab_control.TabsTotal();
|
|
for(int i=1; i<total; i++)
|
|
{
|
|
CStatChart *chart=this.GetChartStats(i);
|
|
if(chart!=NULL)
|
|
chart.SetProfitColorLine(clr);
|
|
}
|
|
}
|
|
//--- Установка цвета убыточной серии
|
|
void SetLossColorLine(const color clr)
|
|
{
|
|
int total=this.m_tab_control.TabsTotal();
|
|
for(int i=1; i<total; i++)
|
|
{
|
|
CStatChart *chart=this.GetChartStats(i);
|
|
if(chart!=NULL)
|
|
chart.SetLossColorLine(clr);
|
|
}
|
|
}
|
|
//--- Установка цвета выбранной серии
|
|
void SetSelectedLineColor(const color clr)
|
|
{
|
|
int total=this.m_tab_control.TabsTotal();
|
|
for(int i=1; i<total; i++)
|
|
{
|
|
CStatChart *chart=this.GetChartStats(i);
|
|
if(chart!=NULL)
|
|
chart.SetSelectedLineColor(clr);
|
|
}
|
|
}
|
|
|
|
//--- Обработчики событий тестера стратегий
|
|
void OnTester(const double OnTesterValue);
|
|
int OnTesterInit(const int lines, const int selected_line_width, const color selected_line_color);
|
|
void OnTesterPass(void);
|
|
void OnTesterDeinit(void);
|
|
|
|
//--- Обработчики событий графика
|
|
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam,const int delay_ms);
|
|
|
|
protected:
|
|
//--- Обработчик (1) смены вкладки элемента Tab Control, (2) выбора кнопки переключателя Button Switch
|
|
void OnTabSwitchEvent(const int tab_id);
|
|
void OnButtonSwitchEvent(const int tab_id, const uint butt_id);
|
|
|
|
public:
|
|
//--- Конструктор/деструктор
|
|
CFrameViewer(void);
|
|
~CFrameViewer(void){ this.m_list_frames.Clear(); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Конструктор |
|
|
//+------------------------------------------------------------------+
|
|
CFrameViewer::CFrameViewer(void) : m_completed(false), m_progress_bar(NULL), m_selected_color(clrDodgerBlue), m_line_width(1)
|
|
{
|
|
//--- Размеры окна графика
|
|
this.m_w=(int)::ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
|
|
this.m_h=(int)::ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
|
|
//--- Получаем указатель на прогресс-бар из объекта чарта статистики
|
|
this.m_progress_bar=this.m_chart_stat_0.GetProgressBar();
|
|
this.m_list_frames.Clear();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Должна вызываться в обработчике эксперта OnTesterInit() |
|
|
//+------------------------------------------------------------------+
|
|
int CFrameViewer::OnTesterInit(const int lines, const int selected_line_width, const color selected_line_color)
|
|
{
|
|
//--- Идентификатор графика с экспертом, работающем во Frame-режиме
|
|
long chart_id=::ChartID();
|
|
|
|
//--- Готовим плавающий график для рисования таблиц статистики и линий баланса
|
|
::ResetLastError();
|
|
if(!::ChartSetInteger(chart_id, CHART_SHOW, false))
|
|
{
|
|
::PrintFormat("%s: ChartSetInteger() failed. Error %d",__FUNCTION__, GetLastError());
|
|
return INIT_FAILED;
|
|
}
|
|
if(!::ChartSetInteger(chart_id, CHART_IS_DOCKED, false))
|
|
{
|
|
::PrintFormat("%s: ChartSetInteger() failed. Error %d",__FUNCTION__, GetLastError());
|
|
return INIT_FAILED;
|
|
}
|
|
//--- Очищаем график полностью от всех графических объектов
|
|
::ObjectsDeleteAll(chart_id);
|
|
|
|
//--- По размерам графика создаём элемент управления Tab Control с пятью вкладками
|
|
int w=(int)::ChartGetInteger(chart_id, CHART_WIDTH_IN_PIXELS);
|
|
int h=(int)::ChartGetInteger(chart_id, CHART_HEIGHT_IN_PIXELS);
|
|
if(this.m_tab_control.Create("TabControl", "", 0, 0, w, h))
|
|
{
|
|
//--- Если элемент управления создан успешно - добавляем к нему пять вкладок
|
|
bool res=true;
|
|
for(int i=0; i<5; i++)
|
|
{
|
|
string tab_text=(i==1 ? "Sharpe Ratio" : i==2 ? "Net Profit" : i==3 ? "Profit Factor" : i==4 ? "Recovery Factor" : "Optimization");
|
|
res &=this.m_tab_control.AddTab(i, tab_text);
|
|
}
|
|
if(!res)
|
|
{
|
|
::PrintFormat("%s: Errors occurred while adding tabs to the Tab Control",__FUNCTION__);
|
|
return INIT_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Print("Tab Control creation failed");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
//--- Объекты CCanvas рабочей области вкладки 0 (Optimization) для рисования фоновых изображений и текста
|
|
CCanvas *tab0_background=this.m_tab_control.GetTabBackground(0);
|
|
CCanvas *tab0_foreground=this.m_tab_control.GetTabForeground(0);
|
|
//--- Объекты CCanvas рабочей области вкладки 1 (Sharpe Ratio) для рисования фоновых изображений и текста
|
|
CCanvas *tab1_background=this.m_tab_control.GetTabBackground(1);
|
|
CCanvas *tab1_foreground=this.m_tab_control.GetTabForeground(1);
|
|
//--- Объекты CCanvas рабочей области вкладки 2 (Net Profit) для рисования фоновых изображений и текста
|
|
CCanvas *tab2_background=this.m_tab_control.GetTabBackground(2);
|
|
CCanvas *tab2_foreground=this.m_tab_control.GetTabForeground(2);
|
|
//--- Объекты CCanvas рабочей области вкладки 3 (Profit Factor) для рисования фоновых изображений и текста
|
|
CCanvas *tab3_background=this.m_tab_control.GetTabBackground(3);
|
|
CCanvas *tab3_foreground=this.m_tab_control.GetTabForeground(3);
|
|
//--- Объекты CCanvas рабочей области вкладки 4 (Recovery Factor) для рисования фоновых изображений и текста
|
|
CCanvas *tab4_background=this.m_tab_control.GetTabBackground(4);
|
|
CCanvas *tab4_foreground=this.m_tab_control.GetTabForeground(4);
|
|
|
|
//--- Устанавливаем объектам графиков статистики оптимизации идентификаторы вкладок
|
|
this.m_chart_stat_0.SetTabID(0);
|
|
this.m_chart_stat_1.SetTabID(1);
|
|
this.m_chart_stat_2.SetTabID(2);
|
|
this.m_chart_stat_3.SetTabID(3);
|
|
this.m_chart_stat_4.SetTabID(4);
|
|
|
|
//--- Указываем для объектов чартов статистики, что рисуем на вкладке с соответствующим индексом
|
|
this.m_chart_stat_0.SetCanvas(tab0_background, tab0_foreground);
|
|
this.m_chart_stat_1.SetCanvas(tab1_background, tab1_foreground);
|
|
this.m_chart_stat_2.SetCanvas(tab2_background, tab2_foreground);
|
|
this.m_chart_stat_3.SetCanvas(tab3_background, tab3_foreground);
|
|
this.m_chart_stat_4.SetCanvas(tab4_background, tab4_foreground);
|
|
|
|
//--- Устанавливаем количество серий на графиках статистики оптимизации
|
|
this.m_chart_stat_0.SetLines(lines);
|
|
this.m_chart_stat_1.SetLines(lines);
|
|
this.m_chart_stat_2.SetLines(lines);
|
|
this.m_chart_stat_3.SetLines(lines);
|
|
this.m_chart_stat_4.SetLines(lines);
|
|
|
|
//--- Задаём цвета фона и переднего плана графиков статистики оптимизации
|
|
this.m_chart_stat_0.SetBackColor(clrIvory);
|
|
this.m_chart_stat_0.SetForeColor(C'200,200,200');
|
|
this.m_chart_stat_1.SetBackColor(clrIvory);
|
|
this.m_chart_stat_1.SetForeColor(C'200,200,200');
|
|
this.m_chart_stat_2.SetBackColor(clrIvory);
|
|
this.m_chart_stat_2.SetForeColor(C'200,200,200');
|
|
this.m_chart_stat_3.SetBackColor(clrIvory);
|
|
this.m_chart_stat_3.SetForeColor(C'200,200,200');
|
|
this.m_chart_stat_4.SetBackColor(clrIvory);
|
|
this.m_chart_stat_4.SetForeColor(C'200,200,200');
|
|
|
|
//--- Установим толщину и цвет выбранной линии лучшего прохода
|
|
this.SetSelectedLineWidth(selected_line_width);
|
|
this.SetSelectedLineColor(selected_line_color);
|
|
|
|
//--- Нарисуем на вкладке 0 (Optimization) две таблицы с результатами оптимизации и входными параметрами,
|
|
//--- и окно с полосой прогресса для вывода графиков и процесса оптимизации
|
|
this.TableStatDraw(0, 4, 4, CELL_W*2, CELL_H, false);
|
|
this.TableInpDraw(0, 4, this.m_table_stat_0.Y2()+4, CELL_W*2, CELL_H, 0, false);
|
|
this.ChartOptDraw(0, this.m_completed, true);
|
|
//--- Создадим на вкладке 0 кнопку воспроизведения оптимизации
|
|
if(!this.m_chart_stat_0.CreateButtonReplay())
|
|
{
|
|
Print("Button Replay creation failed");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
//--- Нарисуем на вкладке 1 (Sharpe Ratio) две таблицы с результатами оптимизации и входными параметрами,
|
|
//--- и окно для вывода графиков результатов оптимизации
|
|
this.TableStatDraw(1, 4, 4, CELL_W*2, CELL_H, false);
|
|
this.TableInpDraw(1, 4, this.m_table_stat_1.Y2()+4, CELL_W*2, CELL_H, 0, false);
|
|
this.ChartOptDraw(1, this.m_completed, true);
|
|
//--- Создадим на вкладке 1 кнопку выбора результата
|
|
if(!this.m_chart_stat_1.CreateButtonResults())
|
|
{
|
|
Print("Tab1: There were errors when creating the result buttons");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
//--- Нарисуем на вкладке 2 (Net Profit) две таблицы с результатами оптимизации и входными параметрами,
|
|
//--- и окно для вывода графиков результатов оптимизации
|
|
this.TableStatDraw(2, 4, 4, CELL_W*2, CELL_H, false);
|
|
this.TableInpDraw(2, 4, this.m_table_stat_1.Y2()+4, CELL_W*2, CELL_H, 0, false);
|
|
this.ChartOptDraw(2, this.m_completed, true);
|
|
//--- Создадим на вкладке 2 кнопку выбора результата
|
|
if(!this.m_chart_stat_2.CreateButtonResults())
|
|
{
|
|
Print("Tab2: There were errors when creating the result buttons");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
//--- Нарисуем на вкладке 3 (Profit Factor) две таблицы с результатами оптимизации и входными параметрами,
|
|
//--- и окно для вывода графиков результатов оптимизации
|
|
this.TableStatDraw(3, 4, 4, CELL_W*2, CELL_H, false);
|
|
this.TableInpDraw(3, 4, this.m_table_stat_1.Y2()+4, CELL_W*2, CELL_H, 0, false);
|
|
this.ChartOptDraw(3, this.m_completed, true);
|
|
//--- Создадим на вкладке 3 кнопку выбора результата
|
|
if(!this.m_chart_stat_3.CreateButtonResults())
|
|
{
|
|
Print("Tab3: There were errors when creating the result buttons");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
//--- Нарисуем на вкладке 4 (Recovery Factor) две таблицы с результатами оптимизации и входными параметрами,
|
|
//--- и окно для вывода графиков результатов оптимизации
|
|
this.TableStatDraw(4, 4, 4, CELL_W*2, CELL_H, false);
|
|
this.TableInpDraw(4, 4, this.m_table_stat_1.Y2()+4, CELL_W*2, CELL_H, 0, false);
|
|
this.ChartOptDraw(4, this.m_completed, true);
|
|
//--- Создадим на вкладке 4 кнопку выбора результата
|
|
if(!this.m_chart_stat_4.CreateButtonResults())
|
|
{
|
|
Print("Tab4: There were errors when creating the result buttons");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Должна вызываться в обработчике эксперта OnTesterDeinit() |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::OnTesterDeinit(void)
|
|
{
|
|
//--- Получаем указатели на канвас для рисования фона и переднего плана
|
|
CCanvas *background=this.m_tab_control.GetTabBackground(0);
|
|
CCanvas *foreground=this.m_tab_control.GetTabForeground(0);
|
|
|
|
if(background==NULL || foreground==NULL)
|
|
return;
|
|
|
|
//--- Устанавливаем флаг завершения оптимизации
|
|
this.m_completed=true;
|
|
|
|
//--- Координаты заголовка графика
|
|
int x1=this.m_chart_stat_0.HeaderX1();
|
|
int y1=this.m_chart_stat_0.HeaderY1();
|
|
int x2=this.m_chart_stat_0.HeaderX2();
|
|
int y2=this.m_chart_stat_0.HeaderY2();
|
|
|
|
int x=(x1+x2)/2;
|
|
int y=(y1+y2)/2;
|
|
|
|
//--- Перекрасим фон и сотрём текст заголовка
|
|
background.FillRectangle(x1, y1, x2, y2, ::ColorToARGB(clrLightGreen));
|
|
foreground.FillRectangle(x1, y1, x2, y2, 0x00FFFFFF);
|
|
|
|
//--- Изменим текст и цвет шапки заголовка
|
|
string text="Optimization Complete: Click to Replay";
|
|
foreground.FontSet("Calibri", -100, FW_BLACK);
|
|
foreground.TextOut(x, y, text, ::ColorToARGB(clrMidnightBlue), TA_CENTER|TA_VCENTER);
|
|
background.Update(false);
|
|
foreground.Update(true);
|
|
|
|
//--- Получаем индекс активной вкладки и вызываем метод управления отображением управляющих объектов на графиках оптимизации
|
|
int tab_selected=this.m_tab_control.GetSelectedTabID();
|
|
this.ControlObjectsView(tab_selected);
|
|
|
|
//--- На каждой вкладке (1 - 4) нарисуем графики трёх лучших проходов оптимизации
|
|
this.DrawBestFrameDataAll();
|
|
::ChartRedraw();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Готовит массив значений баланса и отправляет его во фрейме |
|
|
//| Должна вызываться в эксперте в обработчике OnTester() |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::OnTester(const double OnTesterValue)
|
|
{
|
|
//--- Переменные для работы с результатами прохода
|
|
double balance[];
|
|
int data_count=0;
|
|
double balance_current=::TesterStatistics(STAT_INITIAL_DEPOSIT);
|
|
|
|
//--- Временные переменные для работы со сделками
|
|
ulong ticket=0;
|
|
double profit;
|
|
string symbol;
|
|
long entry;
|
|
|
|
//--- Запросим всю торговую историю
|
|
::ResetLastError();
|
|
if(!::HistorySelect(0, ::TimeCurrent()))
|
|
{
|
|
PrintFormat("%s: HistorySelect() failed. Error ",__FUNCTION__, ::GetLastError());
|
|
return;
|
|
}
|
|
|
|
//--- Собираем данные о сделках
|
|
uint deals_total=::HistoryDealsTotal();
|
|
for(uint i=0; i<deals_total; i++)
|
|
{
|
|
ticket=::HistoryDealGetTicket(i);
|
|
if(ticket==0)
|
|
continue;
|
|
symbol=::HistoryDealGetString(ticket, DEAL_SYMBOL);
|
|
entry =::HistoryDealGetInteger(ticket, DEAL_ENTRY);
|
|
profit=::HistoryDealGetDouble(ticket, DEAL_PROFIT);
|
|
if(entry!=DEAL_ENTRY_OUT && entry!=DEAL_ENTRY_INOUT)
|
|
continue;
|
|
|
|
balance_current+=profit;
|
|
data_count++;
|
|
::ArrayResize(balance, data_count);
|
|
balance[data_count-1]=balance_current;
|
|
}
|
|
//--- Массив data[] для отправки данных во фрейм
|
|
double data[];
|
|
::ArrayResize(data, ::ArraySize(balance)+DATA_COUNT);
|
|
::ArrayCopy(data, balance, DATA_COUNT, 0);
|
|
|
|
//--- Заполним первые DATA_COUNT значений массива результатами тестирования
|
|
data[0]=::TesterStatistics(STAT_SHARPE_RATIO); // коэффициент Шарпа
|
|
data[1]=::TesterStatistics(STAT_PROFIT); // чистая прибыль
|
|
data[2]=::TesterStatistics(STAT_PROFIT_FACTOR); // фактор прибыльности
|
|
data[3]=::TesterStatistics(STAT_RECOVERY_FACTOR); // фактор восстановления
|
|
data[4]=::TesterStatistics(STAT_TRADES); // количество трейдов
|
|
data[5]=::TesterStatistics(STAT_DEALS); // количество сделок
|
|
data[6]=::TesterStatistics(STAT_EQUITY_DDREL_PERCENT); // максимальная просадка средств в процентах
|
|
data[7]=OnTesterValue; // значение пользовательского критерия оптимизации
|
|
if(data[2]==DBL_MAX)
|
|
data[2]=0;
|
|
//--- Создадим фрейм с данными и отправим его в терминал
|
|
if(!::FrameAdd(::MQLInfoString(MQL_PROGRAM_NAME), FRAME_ID, deals_total, data))
|
|
::PrintFormat("%s: Frame add error: ",__FUNCTION__, ::GetLastError());
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получает фрейм с данными при оптимизации и отображает график |
|
|
//| Должна вызываться в эксперте в обработчике OnTesterPass() |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::OnTesterPass(void)
|
|
{
|
|
//--- Переменные для работы со фреймами
|
|
string name;
|
|
ulong pass;
|
|
long id;
|
|
double value, data[];
|
|
string params[];
|
|
uint par_count;
|
|
|
|
//--- Вспомогательные переменные
|
|
static datetime start=::TimeLocal();
|
|
static int frame_counter=0;
|
|
|
|
//--- При получении нового фрейма получаем из него данные
|
|
while(!::IsStopped() && ::FrameNext(pass, name, id, value, data))
|
|
{
|
|
frame_counter++;
|
|
string text=::StringFormat("Frames completed (tester passes): %d in %s", frame_counter,::TimeToString(::TimeLocal()-start, TIME_MINUTES|TIME_SECONDS));
|
|
//--- Получим входные параметры эксперта, для которых сформирован фрейм, и отправим их в таблицы и на график
|
|
//--- При успешном получении фрейма запишем его данные в объект фрейм и разместим его в списке
|
|
if(this.DrawFrameData(0, text, clrNONE, 0, pass, params, par_count, data))
|
|
{
|
|
//--- Результаты прохода тестера
|
|
double sharpe_ratio=data[0];
|
|
double net_profit=data[1];
|
|
double profit_factor=data[2];
|
|
double recovery_factor=data[3];
|
|
|
|
//--- Создаём новый объект фрейм и сохраняем его в списке
|
|
CFrameData *frame=new CFrameData(pass, sharpe_ratio, net_profit, profit_factor, recovery_factor);
|
|
if(frame!=NULL)
|
|
{
|
|
if(!this.AddFrame(frame))
|
|
delete frame;
|
|
}
|
|
::ChartRedraw();
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Обработка событий на графике |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::OnChartEvent(const int id,const long &lparam,
|
|
const double &dparam,const string &sparam,
|
|
const int delay_ms)
|
|
{
|
|
//--- Вызываем обработчики событий объекта управления вкладками и графиков результатов оптимизации
|
|
this.m_tab_control.OnChartEvent(id, lparam, dparam, sparam);
|
|
this.m_chart_stat_0.OnChartEvent(id, lparam, dparam, sparam);
|
|
this.m_chart_stat_1.OnChartEvent(id, lparam, dparam, sparam);
|
|
this.m_chart_stat_2.OnChartEvent(id, lparam, dparam, sparam);
|
|
this.m_chart_stat_3.OnChartEvent(id, lparam, dparam, sparam);
|
|
this.m_chart_stat_4.OnChartEvent(id, lparam, dparam, sparam);
|
|
|
|
//--- Если пришло событие изменения графика
|
|
if(id==CHARTEVENT_CHART_CHANGE)
|
|
{
|
|
//--- получим размеры графика
|
|
int w=(int)::ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
|
|
int h=(int)::ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
|
|
if(w!=this.m_w || h!=this.m_h)
|
|
{
|
|
if(w==0 || h==0)
|
|
return;
|
|
|
|
//--- Изменим размер элемента управления Tab Control
|
|
this.m_tab_control.Resize(w, h);
|
|
|
|
//--- Получим идентификатор выбранной вкладки и нарисуем таблицы данных и график оптимизации на вкладке
|
|
int tab_selected=this.m_tab_control.GetSelectedTabID();
|
|
this.DrawDataChart(tab_selected);
|
|
|
|
//--- Получим указатель на кнопку-переключатель и выбранную кнопку показа результатов оптимизации
|
|
CButtonSwitch *button_switch=(tab_selected>0 ? this.GetChartStats(tab_selected).ButtonResult() : NULL);
|
|
uint res_index=(button_switch!=NULL ? button_switch.SelectedButton() : -1);
|
|
|
|
//--- В зависимости от выбранной вкладки
|
|
switch(tab_selected)
|
|
{
|
|
//--- вкладка 0 (Optimization)
|
|
case 0 :
|
|
//--- Рисуем график с линией последнего прохода и две пустые таблицы
|
|
this.DrawDataChart(0);
|
|
//--- Запускает воспроизведение проведённой оптимизации,
|
|
//--- что останавливает работу с остальным пока длится воспроизведение
|
|
//if(this.m_completed)
|
|
// this.ReplayFrames(1);
|
|
break;
|
|
|
|
//--- вкладки 1 - 4
|
|
default:
|
|
//--- Получаем индекс выбранной кнопки прохода оптимизации
|
|
res_index=button_switch.SelectedButton();
|
|
//--- Рисуем график с результатами трёх лучших проходов выбранной вкладки
|
|
this.DrawDataChart(tab_selected);
|
|
this.DrawBestFrameData(tab_selected, -1);
|
|
this.DrawBestFrameData(tab_selected, res_index);
|
|
|
|
//--- На вкладке 0 рисуем график с линией последнего прохода и две пустые таблицы
|
|
this.DrawDataChart(0);
|
|
//--- Запускает воспроизведение проведённой оптимизации,
|
|
//--- что останавливает работу с остальным пока длится воспроизведение
|
|
//--- Чтобы заново нарисовать графики всех проходов, можно нажать кнопку воспроизведения
|
|
//if(this.m_completed)
|
|
// this.ReplayFrames(1);
|
|
break;
|
|
}
|
|
|
|
//--- Запомним новые размеры для последующей проверки
|
|
this.m_w=w;
|
|
this.m_h=h;
|
|
}
|
|
}
|
|
|
|
//--- Если процесс оптимизации не завершён - уходим
|
|
if(!this.m_completed)
|
|
return;
|
|
|
|
//--- Если пришло пользовательское событие
|
|
if(id>CHARTEVENT_CUSTOM)
|
|
{
|
|
//--- Если пришло событие кнопки Replay и оптимизация завершена
|
|
if(sparam==this.m_chart_stat_0.ButtonReplay().Name() && this.m_completed)
|
|
{
|
|
//--- скроем кнопку Replay,
|
|
this.m_chart_stat_0.ButtonReplay().Hide();
|
|
|
|
//--- Инициализируем график результатов оптимизации,
|
|
this.ChartOptDraw(0, this.m_completed, true);
|
|
|
|
//--- запустим воспроизведение,
|
|
this.m_completed=false; // заблокируем, чтобы не запустить несколько раз подряд
|
|
this.ReplayFrames(delay_ms); // процедура воспроизведения
|
|
this.m_completed=true; // снимаем блокировку
|
|
|
|
//--- После завершения воспроизведения покажем кнопку Replay и перерисуем график
|
|
this.m_chart_stat_0.ButtonReplay().Show();
|
|
::ChartRedraw();
|
|
}
|
|
|
|
//--- Получаем указатели на кнопки вкладок
|
|
CTabButton *tab_btn0=this.m_tab_control.GetTabButton(0);
|
|
CTabButton *tab_btn1=this.m_tab_control.GetTabButton(1);
|
|
CTabButton *tab_btn2=this.m_tab_control.GetTabButton(2);
|
|
CTabButton *tab_btn3=this.m_tab_control.GetTabButton(3);
|
|
CTabButton *tab_btn4=this.m_tab_control.GetTabButton(4);
|
|
if(tab_btn0==NULL || tab_btn1==NULL || tab_btn2==NULL || tab_btn3==NULL || tab_btn4==NULL)
|
|
return;
|
|
|
|
//--- Получаем идентификатор выбранной вкладки
|
|
int tab_selected=this.m_tab_control.GetSelectedTabID();
|
|
|
|
//--- Если пришло событие переключения на вкладку 0
|
|
if(sparam==tab_btn0.Name())
|
|
{
|
|
//--- На вкладке 0 рисуем график с линией последнего прохода и две таблицы с пустыми результатами
|
|
this.DrawDataChart(0);
|
|
//--- Запускает воспроизведение проведённой оптимизации
|
|
//--- (может долго длиться - при желании, чтобы отобразить графики, можно нажать кнопку Replay)
|
|
//if(this.m_completed)
|
|
// this.ReplayFrames(1);
|
|
::ChartRedraw();
|
|
return;
|
|
}
|
|
|
|
//--- Получаем указатель на чарт выбранной вкладки
|
|
CStatChart *chart_stat=this.GetChartStats(tab_selected);
|
|
if(tab_selected==0 || chart_stat==NULL)
|
|
return;
|
|
//--- Получаем указатели на кнопки чарта выбранной вкладки (индекс вкладки 1 - 4)
|
|
CButtonTriggered *button_min=chart_stat.ButtonResultMin();
|
|
CButtonTriggered *button_mid=chart_stat.ButtonResultMid();
|
|
CButtonTriggered *button_max=chart_stat.ButtonResultMax();
|
|
if(button_min==NULL || button_mid==NULL || button_max==NULL)
|
|
return;
|
|
|
|
//--- Если пришло событие переключения на вкладку 1
|
|
if(sparam==tab_btn1.Name())
|
|
{
|
|
//--- вызываем обработчик переключения на вкладку
|
|
this.OnTabSwitchEvent(1);
|
|
}
|
|
//--- Если пришло событие переключения на вкладку 2
|
|
if(sparam==tab_btn2.Name())
|
|
{
|
|
//--- вызываем обработчик переключения на вкладку
|
|
this.OnTabSwitchEvent(2);
|
|
}
|
|
//--- Если пришло событие переключения на вкладку 3
|
|
if(sparam==tab_btn3.Name())
|
|
{
|
|
//--- вызываем обработчик переключения на вкладку
|
|
this.OnTabSwitchEvent(3);
|
|
}
|
|
//--- Если пришло событие переключения на вкладку 4
|
|
if(sparam==tab_btn4.Name())
|
|
{
|
|
//--- вызываем обработчик переключения на вкладку
|
|
this.OnTabSwitchEvent(4);
|
|
}
|
|
|
|
//--- Если пришло событие нажатие на кнопку минимального результата выбранной вкладки
|
|
if(sparam==button_min.Name())
|
|
{
|
|
//--- вызываем обработчик переключения кнопки-переключателя
|
|
this.OnButtonSwitchEvent(tab_selected, 0);
|
|
}
|
|
//--- Если пришло событие нажатие на кнопку среднего результата выбранной вкладки
|
|
if(sparam==button_mid.Name())
|
|
{
|
|
//--- вызываем обработчик переключения кнопки-переключателя
|
|
this.OnButtonSwitchEvent(tab_selected, 1);
|
|
}
|
|
//--- Если пришло событие нажатие на кнопку лучшего результата выбранной вкладки
|
|
if(sparam==button_max.Name())
|
|
{
|
|
//--- вызываем обработчик переключения кнопки-переключателя
|
|
this.OnButtonSwitchEvent(tab_selected, 2);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Обработчик переключения вкладки |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::OnTabSwitchEvent(const int tab_id)
|
|
{
|
|
//--- Получаем указатель на чарт выбранной вкладки
|
|
CStatChart *chart_stat=this.GetChartStats(tab_id);
|
|
if(chart_stat==NULL)
|
|
return;
|
|
|
|
//--- Получаем указатель на кнопку-переключатель чарта выбранной вкладки
|
|
CButtonSwitch *button_switch=chart_stat.ButtonResult();
|
|
if(button_switch==NULL)
|
|
return;
|
|
|
|
//--- Индекс нажатой кнопки
|
|
uint butt_index=button_switch.SelectedButton();
|
|
//--- Инициализируем график результатов на вкладке tab_id и
|
|
this.DrawDataChart(tab_id);
|
|
//--- вызываем метод, контролирующий отображение управляющих элементов на всех вкладках
|
|
this.ControlObjectsView(tab_id);
|
|
|
|
//--- Рисуем все три лучших прохода
|
|
this.DrawBestFrameData(tab_id, -1);
|
|
//--- Выделяем проход, выбранный кнопкой
|
|
this.DrawBestFrameData(tab_id, butt_index);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Обработчик переключения кнопки-переключателя |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::OnButtonSwitchEvent(const int tab_id, const uint butt_id)
|
|
{
|
|
//--- Инициализируем график результатов на вкладке tab_id
|
|
this.DrawDataChart(tab_id);
|
|
//--- Рисуем все три лучших прохода
|
|
this.DrawBestFrameData(tab_id, -1);
|
|
//--- Выделяем проход, выбранный кнопкой butt_id
|
|
this.DrawBestFrameData(tab_id, butt_id);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Рисует таблицы данных и график оптимизации |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::DrawDataChart(const uint tab_id)
|
|
{
|
|
//--- Рисуем таблицу статистики, таблицу входных параметров и график оптимизации
|
|
this.TableStatDraw(tab_id, 4, 4, CELL_W*2, CELL_H, false);
|
|
this.TableInpDraw(tab_id, 4, this.GetTableStats(tab_id).Y2()+4, CELL_W*2, CELL_H, this.GetTableInputs(tab_id).RowsTotal(), false);
|
|
this.ChartOptDraw(tab_id, this.m_completed, true);
|
|
//--- вызываем метод, контролирующий отображение управляющих элементов на всех вкладках
|
|
this.ControlObjectsView(tab_id);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Повторное проигрывание фреймов после окончания оптимизации |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::ReplayFrames(const int delay_ms)
|
|
{
|
|
//--- Переменные для работы со фреймами
|
|
string name;
|
|
ulong pass;
|
|
long id;
|
|
double value, data[];
|
|
string params[];
|
|
uint par_count;
|
|
|
|
//--- Счетчик фреймов
|
|
int frame_counter=0;
|
|
|
|
//--- Очистим счетчики прогресс-бара
|
|
this.m_progress_bar.Reset();
|
|
this.m_progress_bar.Update(false);
|
|
|
|
//--- Переводим указатель фреймов в начало и запускаем перебор фреймов
|
|
::FrameFirst();
|
|
while(!::IsStopped() && ::FrameNext(pass, name, id, value, data))
|
|
{
|
|
//--- Увеличиваем счётчик фреймов и подготавливаем текст заголовка графика оптимизации
|
|
frame_counter++;
|
|
string text=::StringFormat("Playing with pause %d ms: frame %d", delay_ms, frame_counter);
|
|
//--- Получаем входные параметры эксперта, для которых сформирован фрейм, данные фрейма и выводим их на график
|
|
if(this.DrawFrameData(0, text, clrNONE, 0, pass, params, par_count, data))
|
|
::ChartRedraw();
|
|
|
|
//--- Подождём delay_ms миллисекунд
|
|
::Sleep(delay_ms);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Выводит данные указанного фрейма на график оптимизации |
|
|
//+------------------------------------------------------------------+
|
|
bool CFrameViewer::DrawFrameDataByPass(const uint tab_id, const ulong pass_num, const string text, color clr, const uint line_width, double &data[])
|
|
{
|
|
//--- Переменные для работы со фреймами
|
|
string name;
|
|
ulong pass;
|
|
long id;
|
|
uint par_count;
|
|
double value;
|
|
string params[];
|
|
|
|
//--- Переводим указатель фреймов в начало и запускаем поиск фрейма pass_num
|
|
::FrameFirst();
|
|
while(::FrameNext(pass, name, id, value, data))
|
|
{
|
|
//--- Если номер прохода соответствует искомому -
|
|
//--- получаем данные фрейма и выводим их в таблицу
|
|
//--- и на график на вкладке tab_id
|
|
if(pass==pass_num)
|
|
{
|
|
if(DrawFrameData(tab_id, text, clr, line_width, pass, params, par_count, data))
|
|
return true;
|
|
}
|
|
}
|
|
//--- Проход не найден
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Рисует графики трёх лучших проходов по критерию оптимизации |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::DrawBestFrameData(const uint tab_id, const int res_index)
|
|
{
|
|
//--- Если переданы некорректные идентификаторы таблицы и нажатой кнопки - уходим
|
|
if(tab_id<1 || tab_id>4 || res_index>2)
|
|
{
|
|
::PrintFormat("%s: Error. Incorrect table (%u) or selected button (%d) identifiers passed",__FUNCTION__, tab_id, res_index);
|
|
return;
|
|
}
|
|
|
|
//--- Массивы для получения результатов проходов
|
|
ulong array_passes[3];
|
|
double data[];
|
|
|
|
//--- Создаём текст заголовка графика проходов
|
|
string res=
|
|
(
|
|
tab_id==1 ? "Results by Sharpe Ratio" :
|
|
tab_id==2 ? "Results by Net Profit" :
|
|
tab_id==3 ? "Results by Profit Factor" :
|
|
tab_id==4 ? "Results by Recovery Factor" :
|
|
""
|
|
);
|
|
string text="Optimization Completed: "+res;
|
|
|
|
//--- Заполняем массив array_passes индексами трёх лучших проходов
|
|
this.FillArrayBestFrames(tab_id, array_passes);
|
|
//--- Если индекс кнопки прохода задан отрицательным числом -
|
|
if(res_index<0)
|
|
{
|
|
//--- выводим на график все три прохода
|
|
//--- (цвет линий указывается как clrNONE для автоматического выбора цвета линий прибыльной или убыточной серий)
|
|
for(int i=0; i<(int)array_passes.Size(); i++)
|
|
this.DrawFrameDataByPass(tab_id, array_passes[i], text, clrNONE, 0, data);
|
|
}
|
|
//--- Иначе - выводим на график серию, указанную индексом нажатой кнопки (res_index),
|
|
//--- цветом, заданным в m_selected_color, и толщиной, указанной в m_line_width
|
|
else
|
|
this.DrawFrameDataByPass(tab_id, array_passes[res_index], text, this.m_selected_color, this.m_line_width, data);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Выводит на графики результатов оптимизации |
|
|
//| на каждой вкладке по три лучших прохода |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::DrawBestFrameDataAll(void)
|
|
{
|
|
//--- В цикле по всем вкладкам от вкладки 1, рисуем графики трёх лучших проходов для каждой вкладки
|
|
for(int i=1; i<this.m_tab_control.TabsTotal(); i++)
|
|
this.DrawBestFrameData(i,-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получение данных текущего фрейма и вывод их на указанной вкладке |
|
|
//| в таблицу и на график результатов оптимизации |
|
|
//+------------------------------------------------------------------+
|
|
bool CFrameViewer::DrawFrameData(const uint tab_id, const string text, color clr, const uint line_width, ulong &pass, string ¶ms[], uint &par_count, double &data[])
|
|
{
|
|
//--- Проверяем переданный идентификатор вкладки
|
|
if(tab_id>4)
|
|
{
|
|
::PrintFormat("%s: Error: Invalid tab ID passed (%u)",__FUNCTION__, tab_id);
|
|
return false;
|
|
}
|
|
//--- Получаем указатели на используемые объекты на указанной вкладке
|
|
CCanvas *foreground=this.m_tab_control.GetTabForeground(tab_id);
|
|
CTableDataControl *table_stat=this.GetTableStats(tab_id);
|
|
CTableDataControl *table_inp=this.GetTableInputs(tab_id);
|
|
CStatChart *chart_stat=this.GetChartStats(tab_id);
|
|
|
|
if(foreground==NULL || table_stat==NULL || table_inp==NULL || chart_stat==NULL)
|
|
return false;
|
|
|
|
//--- Получим входные параметры эксперта, для которых сформирован фрейм, данные фрейма и выведем их на график
|
|
::ResetLastError();
|
|
if(::FrameInputs(pass, params, par_count))
|
|
{
|
|
//--- Нарисуем таблицу входных параметров на графике
|
|
this.TableInpDraw(tab_id, 4, table_stat.Y2()+4, CELL_W*2, CELL_H, par_count, false);
|
|
|
|
//--- Перебираем параметры, params[i], строка выглядит как "parameter=value"
|
|
for(uint i=0; i<par_count; i++)
|
|
{
|
|
//--- Заполняем таблицу названиями и значениями входных параметров
|
|
string array[];
|
|
//--- Расщепим строку в params[i] на две подстроки и обновим ячейки в строке таблицы параметров тестирования
|
|
if(::StringSplit(params[i],'=',array)==2)
|
|
{
|
|
//--- Окрасим строки оптимизируемых параметров в бледно-желтый цвет,
|
|
//--- недоступные для оптимизации параметры - в бледно-розовый, остальные - в цвета по умолчанию
|
|
bool enable=false;
|
|
double value=0, start=0, step=0, stop=0;
|
|
color clr=clrMistyRose;
|
|
if(::ParameterGetRange(array[0], enable, value, start, step, stop))
|
|
clr=(enable ? clrLightYellow : clrNONE);
|
|
|
|
//--- Получим две ячейки таблицы по индексу параметра и выведем в них текст названия параметра и его значение
|
|
CTableCell *cell_0=table_inp.GetCell(i, 0);
|
|
CTableCell *cell_1=table_inp.GetCell(i, 1);
|
|
if(cell_0!=NULL && cell_1!=NULL)
|
|
{
|
|
//--- Обновим надписи в ячейках
|
|
cell_0.SetText(array[0]);
|
|
cell_1.SetText(array[1]);
|
|
cell_0.TextOut(foreground, 4, CELL_H/2, clr, 0, TA_VCENTER);
|
|
cell_1.TextOut(foreground, 4, CELL_H/2, clr, 0, TA_VCENTER);
|
|
}
|
|
}
|
|
}
|
|
|
|
//--- Обновим таблицу статистики оптимизации
|
|
//--- Строка заголовка таблицы
|
|
foreground.FillRectangle(table_stat.X1()+1, 4+1, table_stat.X1()+CELL_W*2-1, 4+CELL_H-1, 0x00FFFFFF);
|
|
foreground.FontSet("Calibri", -100, FW_BLACK);
|
|
foreground.TextOut(4+(CELL_W*2)/2, 4+CELL_H/2, ::StringFormat("Optimization results (pass %I64u)", pass), ::ColorToARGB(clrMidnightBlue), TA_CENTER|TA_VCENTER);
|
|
|
|
//--- В цикле по количеству строк таблицы
|
|
int total=table_stat.RowsTotal();
|
|
for(int i=0; i<total; i++)
|
|
{
|
|
//--- получим две ячейки текущей строки и
|
|
CTableCell *cell_0=table_stat.GetCell(i, 0);
|
|
CTableCell *cell_1=table_stat.GetCell(i, 1);
|
|
if(cell_0!=NULL && cell_1!=NULL)
|
|
{
|
|
//--- обновим значения результатов прохода во второй ячейке
|
|
string text="---";
|
|
switch(i)
|
|
{
|
|
case 0 : text=::StringFormat("%.2f", data[0]); break; // Sharpe Ratio
|
|
case 1 : text=::StringFormat("%.2f", data[1]); break; // Net Profit
|
|
case 2 : text=::StringFormat("%.2f", data[2]); break; // Profit Factor
|
|
case 3 : text=::StringFormat("%.2f", data[3]); break; // Recovery Factor
|
|
case 4 : text=::StringFormat("%.0f", data[4]); break; // Trades
|
|
case 5 : text=::StringFormat("%.0f", data[5]); break; // Deals
|
|
case 6 : text=::StringFormat("%.2f%%", data[6]);break; // Equity DD
|
|
case 7 : text=::StringFormat("%G", data[7]); break; // OnTester()
|
|
default: break;
|
|
}
|
|
//--- Подсветим цветом фон строки таблицы, соответствующей выбранной вкладке.
|
|
//--- Остальные строки будут иметь цвет по умолчанию
|
|
color clr=(tab_id>0 ? (i==tab_id-1 ? C'223,242,231' : clrNONE) : clrNONE);
|
|
|
|
//--- Обновим надписи в ячейках
|
|
cell_0.TextOut(foreground, 4, CELL_H/2, clr, 0, TA_VCENTER);
|
|
cell_1.SetText(text);
|
|
cell_1.TextOut(foreground, 4, CELL_H/2, clr, 0, TA_VCENTER);
|
|
}
|
|
}
|
|
|
|
//--- Массив для приема значений баланса текущего фрейма
|
|
double seria[];
|
|
::ArrayCopy(seria, data, 0, DATA_COUNT, ::ArraySize(data)-DATA_COUNT);
|
|
//--- Отправим массив для вывода на специальных график баланса
|
|
chart_stat.AddSeria(seria, data[1]>0);
|
|
//--- Обновим линии баланса на графике
|
|
chart_stat.Update(clr, line_width, false);
|
|
//--- Обновим прогресс бар (только для вкладки с идентификатором 0)
|
|
if(tab_id==0)
|
|
this.m_progress_bar.AddResult(data[1]>0, false);
|
|
|
|
//--- Обновим надпись на шапке графика
|
|
int x1=chart_stat.HeaderX1();
|
|
int y1=chart_stat.HeaderY1();
|
|
int x2=chart_stat.HeaderX2();
|
|
int y2=chart_stat.HeaderY2();
|
|
|
|
int x=(x1+x2)/2;
|
|
int y=(y1+y2)/2;
|
|
|
|
foreground.FillRectangle(x1, y1, x2, y2, 0x00FFFFFF);
|
|
foreground.FontSet("Calibri", -100, FW_BLACK);
|
|
foreground.TextOut(x, y, text, ::ColorToARGB(clrMidnightBlue), TA_CENTER|TA_VCENTER);
|
|
foreground.Update(false);
|
|
|
|
//--- Всё успешно
|
|
return true;
|
|
}
|
|
//--- Что-то пошло не так...
|
|
else
|
|
PrintFormat("%s: FrameInputs() failed. Error %d",__FUNCTION__, ::GetLastError());
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Заполняет массив индексами фреймов трёх лучших проходов |
|
|
//| для указанного критерия оптимизации (по индексу вкладки) |
|
|
//+------------------------------------------------------------------+
|
|
bool CFrameViewer::FillArrayBestFrames(const uint tab_id, ulong &array_passes[])
|
|
{
|
|
//--- Очищаем переданный в метод массив индексов проходов оптимизации
|
|
::ZeroMemory(array_passes);
|
|
|
|
//FRAME_PROP_PASS_NUM, // Номер прохода
|
|
//FRAME_PROP_SHARPE_RATIO, // Результат Sharpe Ratio
|
|
//FRAME_PROP_NET_PROFIT, // Результат Net Profit
|
|
//FRAME_PROP_PROFIT_FACTOR, // Результат Profit Factor
|
|
//FRAME_PROP_RECOVERY_FACTOR, // Результат Recovery Factor
|
|
|
|
//--- По идентификатору вкладки будем определять свойство, по которому искать лучшие проходы оптимизации
|
|
//--- Проверяем идентификатор вкладки, чтобы был в пределах от 1 до 4
|
|
if(tab_id<FRAME_PROP_SHARPE_RATIO || tab_id>FRAME_PROP_RECOVERY_FACTOR)
|
|
{
|
|
::PrintFormat("%s: Error: Invalid tab ID passed (%u)",__FUNCTION__, tab_id);
|
|
return false;
|
|
}
|
|
|
|
//--- Преобразуем идентификатор таблицы в свойство фрейма
|
|
ENUM_FRAME_PROP prop=(ENUM_FRAME_PROP)tab_id;
|
|
|
|
//--- Сортируем список фреймов в порядке возрастания по свойству,
|
|
//--- соответствующему значению tab_id в виде перечисления ENUM_FRAME_PROP
|
|
this.m_list_frames.Sort(prop);
|
|
|
|
//--- После сортировки, фрейм с лучшим результатом будет находиться в конце списка
|
|
//--- Получаем по индексу фрейм из списка с максимальным значением результата и
|
|
int index=this.m_list_frames.Total()-1;
|
|
CFrameData *frame_next=this.m_list_frames.At(index);
|
|
if(frame_next==NULL)
|
|
return false;
|
|
|
|
//--- записываем номер прохода в последнюю ячейку массива array_passes
|
|
array_passes[2]=frame_next.Pass();
|
|
|
|
//--- Теперь найдём объекты, у которых результат оптимизации по убыванию меньше найденного максимального
|
|
//--- В цикле от 1 до 0 (оставшиеся ячейки массива array_passes)
|
|
for(int i=1; i>=0; i--)
|
|
{
|
|
//--- ищем предыдущий объект со значением свойства меньше, чем у объекта frame_next
|
|
frame_next=this.FrameSearchLess(frame_next, prop);
|
|
//--- В очередную ячейку массива array_passes вписываем номер прохода найденного объекта
|
|
//--- Если объект не найден - значит, нет объектов со значением, меньше, чем у объекта frame_next,
|
|
//--- и в очередную ячейку массива array_passes в этом случае записываем его предыдущее значение
|
|
array_passes[i]=(frame_next!=NULL ? frame_next.Pass() : array_passes[i+1]);
|
|
}
|
|
//--- Всё успешно
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Ищет и возвращает указатель на объект фрейма, |
|
|
//| со значением свойства меньше образца |
|
|
//+------------------------------------------------------------------+
|
|
CFrameData *CFrameViewer::FrameSearchLess(CFrameData *frame, const int mode)
|
|
{
|
|
//--- В зависимости от типа свойства фрейма
|
|
switch(mode)
|
|
{
|
|
//--- во временный объект записываем соответствующее свойство переданного в метод объекта
|
|
case FRAME_PROP_SHARPE_RATIO : this.m_frame_tmp.SetSharpeRatio(frame.SharpeRatio()); break;
|
|
case FRAME_PROP_NET_PROFIT : this.m_frame_tmp.SetNetProfit(frame.NetProfit()); break;
|
|
case FRAME_PROP_PROFIT_FACTOR : this.m_frame_tmp.SetProfitFactor(frame.ProfitFactor()); break;
|
|
case FRAME_PROP_RECOVERY_FACTOR : this.m_frame_tmp.SetRecoveryFactor(frame.RecoveryFactor()); break;
|
|
default : this.m_frame_tmp.SetPass(frame.Pass()); break;
|
|
}
|
|
//--- Сортируем массив фреймов по указанному свойству и
|
|
this.m_list_frames.Sort(mode);
|
|
//--- получаем индекс ближайшего объекта с меньшим значением свойства, либо -1
|
|
int index=this.m_list_frames.SearchLess(&this.m_frame_tmp);
|
|
//--- Получаем из списка объект по индексу и возвращаем указатель на него, либо NULL
|
|
CFrameData *obj=this.m_list_frames.At(index);
|
|
return obj;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Рисует таблицу статистики оптимизации на указанной вкладке |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::TableStatDraw(const uint tab_id, const int x, const int y, const int w, const int h, const bool chart_redraw)
|
|
{
|
|
//--- Проверяем переданный идентификатор вкладки
|
|
if(tab_id>4)
|
|
{
|
|
::PrintFormat("%s: Error: Invalid tab ID passed (%u)",__FUNCTION__, tab_id);
|
|
return;
|
|
}
|
|
//--- Получаем указатели на используемые объекты на указанной вкладке
|
|
CCanvas *background=this.m_tab_control.GetTabBackground(tab_id);
|
|
CCanvas *foreground=this.m_tab_control.GetTabForeground(tab_id);
|
|
CTableDataControl *table_stat=this.GetTableStats(tab_id);
|
|
|
|
if(background==NULL || foreground==NULL || table_stat==NULL)
|
|
return;
|
|
|
|
//--- Рисуем заголовок таблицы результатов оптимизации
|
|
background.FillRectangle(x, y, x+CELL_W*2, y+CELL_H, ::ColorToARGB(C'195,209,223')); // C'180,190,230'
|
|
foreground.FillRectangle(x+1, y+1, x+CELL_W*2-1, y+CELL_H-1, 0x00FFFFFF);
|
|
foreground.FontSet("Calibri", -100, FW_BLACK);
|
|
foreground.TextOut(x+(CELL_W*2)/2, y+CELL_H/2, "Optimization results", ::ColorToARGB(clrMidnightBlue), TA_CENTER|TA_VCENTER);
|
|
|
|
//--- Задаём таблице её идентификатор и рисуем сетку таблицы
|
|
table_stat.SetID(TABLE_OPT_STAT_ID+10*tab_id);
|
|
table_stat.DrawGrid(background, x, y+CELL_H, 0, DATA_COUNT, 2, CELL_H, CELL_W, C'200,200,200', false);
|
|
|
|
//--- Нарисуем пустую таблицу результатов оптимизации - только заголовки, без значений
|
|
//--- В цикле по строкам таблицы
|
|
int total=table_stat.RowsTotal();
|
|
for(int row=0; row<total; row++)
|
|
{
|
|
//--- перебираем столбцы строк
|
|
for(int col=0; col<2; col++)
|
|
{
|
|
//--- Получаем ячейку таблицы в текущей строке и столбце
|
|
CTableCell *cell=table_stat.GetCell(row, col);
|
|
//--- Определяем текст в ячейке
|
|
//--- Для левой ячейки это будут заголовки результатов оптимизируемых параметров
|
|
if(col%2==0)
|
|
{
|
|
string text="OnTester()";
|
|
switch(row)
|
|
{
|
|
case 0 : text="Sharpe Ratio"; break;
|
|
case 1 : text="Net Profit"; break;
|
|
case 2 : text="Profit Factor"; break;
|
|
case 3 : text="Recovery Factor"; break;
|
|
case 4 : text="Trades"; break;
|
|
case 5 : text="Deals"; break;
|
|
case 6 : text="Equity DD"; break;
|
|
default: break;
|
|
}
|
|
cell.SetText(text);
|
|
}
|
|
//--- Для правой ячейки текст будет прочёркиванием для инициализируемой таблицы
|
|
else
|
|
cell.SetText(tab_id==0 ? " --- " : "");
|
|
|
|
//--- Выведем в ячейку соответствующий текст
|
|
cell.TextOut(foreground, 4, CELL_H/2, clrNONE, 0, TA_VCENTER);
|
|
}
|
|
}
|
|
|
|
//--- Обновим канвас фона и переднего плана
|
|
background.Update(false);
|
|
foreground.Update(chart_redraw);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//|Рисует таблицу входных параметров оптимизации на указанной вкладке|
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::TableInpDraw(const uint tab_id, const int x, const int y, const int w, const int h, const uint rows, const bool chart_redraw)
|
|
{
|
|
//--- Проверяем переданный идентификатор вкладки
|
|
if(tab_id>4)
|
|
{
|
|
::PrintFormat("%s: Error: Invalid tab ID passed (%u)",__FUNCTION__, tab_id);
|
|
return;
|
|
}
|
|
//--- Получаем указатели на используемые объекты на указанной вкладке
|
|
CCanvas *background=this.m_tab_control.GetTabBackground(tab_id);
|
|
CCanvas *foreground=this.m_tab_control.GetTabForeground(tab_id);
|
|
CTableDataControl *table_inp=this.GetTableInputs(tab_id);
|
|
|
|
if(background==NULL || foreground==NULL || table_inp==NULL)
|
|
return;
|
|
|
|
//--- Рисуем заголовок таблицы параметров оптимизации
|
|
background.FillRectangle(x, y, x+CELL_W*2, y+CELL_H, ::ColorToARGB(C'195,209,223'));
|
|
foreground.FillRectangle(x+1, y+1, x+CELL_W*2-1, y+CELL_H-1, 0x00FFFFFF);
|
|
foreground.FontSet("Calibri", -100, FW_BLACK);
|
|
foreground.TextOut(x+(CELL_W*2)/2, y+CELL_H/2, "Input parameters", ::ColorToARGB(clrMidnightBlue), TA_CENTER|TA_VCENTER);
|
|
|
|
//--- Задаём таблице её идентификатор и рисуем сетку таблицы
|
|
table_inp.SetID(TABLE_OPT_INP_ID+10*tab_id);
|
|
table_inp.DrawGrid(background, x, y+CELL_H, 0, rows, 2, CELL_H, CELL_W, C'200,200,200', false);
|
|
|
|
//--- Обновим канвас фона и переднего плана
|
|
background.Update(false);
|
|
foreground.Update(chart_redraw);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Рисует график оптимизации на указанной вкладке |
|
|
//+------------------------------------------------------------------+
|
|
void CFrameViewer::ChartOptDraw(const uint tab_id, const bool opt_completed, const bool chart_redraw)
|
|
{
|
|
//--- Проверяем переданный идентификатор вкладки
|
|
if(tab_id>4)
|
|
{
|
|
::PrintFormat("%s: Error: Invalid tab ID passed (%u)",__FUNCTION__, tab_id);
|
|
return;
|
|
}
|
|
//--- Получаем указатели на используемые объекты на указанной вкладке
|
|
CCanvas *background=this.m_tab_control.GetTabBackground(tab_id);
|
|
CCanvas *foreground=this.m_tab_control.GetTabForeground(tab_id);
|
|
CTab *tab=this.m_tab_control.GetTab(tab_id);
|
|
CTableDataControl *table_stat=this.GetTableStats(tab_id);
|
|
CStatChart *chart_stat=this.GetChartStats(tab_id);
|
|
|
|
if(background==NULL || foreground==NULL || tab==NULL || table_stat==NULL || chart_stat==NULL)
|
|
return;
|
|
|
|
//--- Рассчитаем координаты четырёх углов графика результатов оптимизации
|
|
int x1=table_stat.X2()+10;
|
|
int y1=table_stat.Y1();
|
|
int x2=tab.GetField().Right()-10;
|
|
int y2=tab.GetField().Bottom()-tab.GetButton().Height()-12;
|
|
|
|
//--- Проверим ограничения размеров по минимальным ширине и высоте (480 x 180)
|
|
int w_min=480;
|
|
if(x2-x1<w_min)
|
|
x2=x1+w_min;
|
|
if(y2-y1<180)
|
|
y2=y1+180;
|
|
|
|
//--- Установим размеры ограничивающего прямоугольника графика результатов оптимизации
|
|
chart_stat.SetChartBounds(x1, y1, x2, y2);
|
|
|
|
//--- Цвет и текст заголовка графика
|
|
color clr=clrLightGreen; // цвет заголовка при завершении оптимизации
|
|
string suff=
|
|
(
|
|
tab_id==1 ? "Results by Sharpe Ratio" :
|
|
tab_id==2 ? "Results by Net Profit" :
|
|
tab_id==3 ? "Results by Profit Factor" :
|
|
tab_id==4 ? "Results by Recovery Factor" :
|
|
"Click to Replay"
|
|
);
|
|
string text="Optimization Completed: "+suff;
|
|
|
|
//--- Если оптимизация не завершена, укажем цвет и текст заголовка
|
|
if(!opt_completed)
|
|
{
|
|
clr=C'195,209,223';
|
|
text=::StringFormat("Optimization%sprogress%s", (tab_id==0 ? " " : " in "), (tab_id==0 ? "" : ": Waiting ... "));
|
|
}
|
|
|
|
//--- Рисуем заголовок и текст
|
|
background.FillRectangle(x1, 4, x2, y1, ::ColorToARGB(clr));
|
|
foreground.FillRectangle(x1, 4, x2, y2, 0x00FFFFFF);
|
|
foreground.FontSet("Calibri", -100, FW_BLACK);
|
|
foreground.TextOut((x1+x2)/2, 4+CELL_H/2, text, ::ColorToARGB(clrMidnightBlue), TA_CENTER|TA_VCENTER);
|
|
|
|
//--- Стираем полностью весь график результатов оптимизации
|
|
background.FillRectangle(x1, y1, x2, y2, 0x00FFFFFF);
|
|
foreground.FillRectangle(x1, y1, x2, y2, 0x00FFFFFF);
|
|
|
|
//--- Обновляем график оптимизации
|
|
chart_stat.Update(clrNONE, 0, chart_redraw);
|
|
}
|
|
//+-------------------------------------------------------------------+
|
|
//|Управляет отображением управляющих объектов на графиках оптимизации|
|
|
//+-------------------------------------------------------------------+
|
|
void CFrameViewer::ControlObjectsView(const uint tab_id)
|
|
{
|
|
//--- Получаем индекс активной вкладки
|
|
int tab_index=this.m_tab_control.GetSelectedTabID();
|
|
|
|
//--- Получаем указатель на активную вкладку и таблицу статистики оптимизации
|
|
CTab *tab=this.m_tab_control.GetTab(tab_index);
|
|
CTableDataControl *table_stat=this.GetTableStats(tab_index);
|
|
|
|
if(tab==NULL || table_stat==NULL)
|
|
return;
|
|
|
|
//--- Координаты левой и правой границ заголовка графика результатов оптимизации
|
|
int w=0, cpx=0, x=0, y=0;
|
|
int x1=table_stat.X2()+10;
|
|
int x2=tab.GetField().Right()-10;
|
|
|
|
//--- В зависимости от индекса выбранной вкладки
|
|
switch(tab_index)
|
|
{
|
|
//--- Optimization
|
|
case 0 :
|
|
//--- Смещаем кнопку Replay к центру заголовка
|
|
w=this.m_chart_stat_0.ButtonReplay().Width();
|
|
cpx=(x1+x2)/2;
|
|
x=cpx-w/2;
|
|
this.m_chart_stat_0.ButtonReplay().MoveX(x);
|
|
|
|
//--- Если оптимизация завершена - показываем кнопку на переднем плане
|
|
if(this.m_completed)
|
|
{
|
|
this.m_chart_stat_0.ButtonReplay().Show();
|
|
this.m_chart_stat_0.ButtonReplay().BringToTop();
|
|
}
|
|
|
|
//--- Скрываем кнопки всех остальных вкладок
|
|
this.m_chart_stat_1.ButtonsResultHide();
|
|
this.m_chart_stat_2.ButtonsResultHide();
|
|
this.m_chart_stat_3.ButtonsResultHide();
|
|
this.m_chart_stat_4.ButtonsResultHide();
|
|
break;
|
|
|
|
//--- Sharpe Ratio
|
|
case 1 :
|
|
//--- Скрываем кнопку Replay
|
|
this.m_chart_stat_0.ButtonReplay().Hide();
|
|
|
|
//--- Получаем координату Y и смещаем на неё кнопку-переключатель
|
|
y=this.m_chart_stat_1.ProgressBarY1()+CELL_H+2;
|
|
this.m_chart_stat_1.ButtonResult().MoveY(y);
|
|
|
|
//--- Кнопку-переключатель на вкладке 1 переносим на передний план,
|
|
//--- а все остальные кнопки на других вкладках скрываем
|
|
this.m_chart_stat_1.ButtonsResultBringToTop();
|
|
this.m_chart_stat_2.ButtonsResultHide();
|
|
this.m_chart_stat_3.ButtonsResultHide();
|
|
this.m_chart_stat_4.ButtonsResultHide();
|
|
break;
|
|
|
|
//--- Net Profit
|
|
case 2 :
|
|
this.m_chart_stat_0.ButtonReplay().Hide();
|
|
|
|
//--- Получаем координату Y и смещаем на неё кнопку-переключатель
|
|
y=this.m_chart_stat_2.ProgressBarY1()+CELL_H+2;
|
|
this.m_chart_stat_2.ButtonResult().MoveY(y);
|
|
|
|
//--- Кнопку-переключатель на вкладке 2 переносим на передний план,
|
|
//--- а все остальные кнопки на других вкладках скрываем
|
|
this.m_chart_stat_2.ButtonsResultBringToTop();
|
|
this.m_chart_stat_1.ButtonsResultHide();
|
|
this.m_chart_stat_3.ButtonsResultHide();
|
|
this.m_chart_stat_4.ButtonsResultHide();
|
|
break;
|
|
|
|
//--- Profit Factor
|
|
case 3 :
|
|
this.m_chart_stat_0.ButtonReplay().Hide();
|
|
|
|
//--- Получаем координату Y и смещаем на неё кнопку-переключатель
|
|
y=this.m_chart_stat_3.ProgressBarY1()+CELL_H+2;
|
|
this.m_chart_stat_3.ButtonResult().MoveY(y);
|
|
|
|
//--- Кнопку-переключатель на вкладке 3 переносим на передний план,
|
|
//--- а все остальные кнопки на других вкладках скрываем
|
|
this.m_chart_stat_3.ButtonsResultBringToTop();
|
|
this.m_chart_stat_1.ButtonsResultHide();
|
|
this.m_chart_stat_2.ButtonsResultHide();
|
|
this.m_chart_stat_4.ButtonsResultHide();
|
|
break;
|
|
|
|
//--- Recovery Factor
|
|
case 4 :
|
|
this.m_chart_stat_0.ButtonReplay().Hide();
|
|
|
|
//--- Получаем координату Y и смещаем на неё кнопку-переключатель
|
|
y=this.m_chart_stat_4.ProgressBarY1()+CELL_H+2;
|
|
this.m_chart_stat_4.ButtonResult().MoveY(y);
|
|
|
|
//--- Кнопку-переключатель на вкладке 4 переносим на передний план,
|
|
//--- а все остальные кнопки на других вкладках скрываем
|
|
this.m_chart_stat_4.ButtonsResultBringToTop();
|
|
this.m_chart_stat_1.ButtonsResultHide();
|
|
this.m_chart_stat_2.ButtonsResultHide();
|
|
this.m_chart_stat_3.ButtonsResultHide();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
//--- Перерисовываем график
|
|
::ChartRedraw();
|
|
}
|
|
//+------------------------------------------------------------------+
|