379 lines
26 KiB
MQL5
379 lines
26 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| CtrlPanelLevels.mqh |
|
|
//| Copyright 2021, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef CTRL_PANEL_LOGGER_MQH
|
|
#define CTRL_PANEL_LOGGER_MQH
|
|
|
|
|
|
/*
|
|
Параметры доступные для настройки данной панели
|
|
bool log_dec_sep_comma=true true=","/false="."
|
|
string log_output_tf=PERIOD_H1 PERIOD_M1 / PERIOD_M5 / PERIOD_M15 / PERIOD_H1 / PERIOD_H4
|
|
bool log_format_bin=true; true/false
|
|
*/
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CCtrlPanelLogger: public CCtrlPanelLevels
|
|
{
|
|
public:
|
|
CCtrlPanelLogger();
|
|
//--- Инициализация/деинициализация
|
|
virtual bool OnInitEvent(void);
|
|
virtual void OnDeinitEvent(const int reason);
|
|
|
|
//--- Обработчик тиков
|
|
virtual void OnTickCalculateEvent();
|
|
virtual void DrawPanelInfo() {};
|
|
|
|
virtual bool IsEnableTickCalculateEvent() {return true; };
|
|
protected:
|
|
datetime _cur_candle_open_time;
|
|
int _next_candle_open_time;
|
|
|
|
LBFBalanceItem _balanceItems[];
|
|
|
|
int _balanceItemCount;
|
|
|
|
double _cur_balance;
|
|
double _cur_equity;
|
|
double _cur_free_margin;
|
|
|
|
int _cur_count_buy;
|
|
int _cur_count_sell;
|
|
|
|
double _cur_length_buy;
|
|
double _cur_length_sell;
|
|
|
|
double _cur_volume_buy;
|
|
double _cur_volume_sell;
|
|
|
|
|
|
double _max_drawdown; // Размер просадки по эквити
|
|
double _max_profit; // Размер прибыли по эквити
|
|
double _max_margin; // Максимальный размер маржи
|
|
|
|
double _max_drawdown_buy; // Размер просадки по эквити
|
|
double _max_drawdown_sell; // Размер просадки по эквити
|
|
|
|
double _tick_value;
|
|
|
|
bool _is_first_init;
|
|
|
|
string _start_test_date;
|
|
string _end_test_date;
|
|
|
|
protected:
|
|
void SaveDataToFile();
|
|
void SaveBalanceToArray();
|
|
void ResetBalance();
|
|
void UpdateGridInfo(logic_handler* hdl,list<CLevelLine*>& lvls,int lvl, int& cur_lvl, double& cur_len, double& cur_vol);
|
|
|
|
string GetFileName();
|
|
ENUM_TIMEFRAMES GetOutputTf(string tf);
|
|
|
|
protected:
|
|
ENUM_TIMEFRAMES _output_timeframe;
|
|
bool _decimal_separator_comma;
|
|
bool _bin_format;
|
|
};
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
CCtrlPanelLogger::CCtrlPanelLogger():CCtrlPanelLevels("show_grid_lvl=true;show_stop_lvl=false;show_no_loss_lvl=false;")
|
|
{
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_TIMEFRAMES CCtrlPanelLogger::GetOutputTf(string tf)
|
|
{
|
|
ENUM_TIMEFRAMES ret=PERIOD_H1;
|
|
StringToUpper(tf);
|
|
|
|
if(StringFind(tf,"M1")!=-1)
|
|
ret=PERIOD_M1;
|
|
else
|
|
if(StringFind(tf,"M5")!=-1)
|
|
ret=PERIOD_M5;
|
|
else
|
|
if(StringFind(tf,"M15")!=-1)
|
|
ret=PERIOD_M15;
|
|
else
|
|
if(StringFind(tf,"H1")!=-1)
|
|
ret=PERIOD_H1;
|
|
else
|
|
if(StringFind(tf,"H4")!=-1)
|
|
ret=PERIOD_H4;
|
|
|
|
return ret;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CCtrlPanelLogger::OnInitEvent(void)
|
|
{
|
|
if(!CCtrlPanelLevels::OnInitEvent())
|
|
return false;
|
|
|
|
_balanceItemCount=0;
|
|
_is_first_init=true;
|
|
|
|
_decimal_separator_comma=GetParamBool("log_dec_sep_comma",true);
|
|
_output_timeframe=GetOutputTf(GetParamString("log_output_tf","H1"));
|
|
_bin_format=GetParamBool("log_format_bin",true);
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CCtrlPanelLogger::OnDeinitEvent(const int reason)
|
|
{
|
|
if(!(reason==REASON_REMOVE || reason==REASON_PROGRAM))
|
|
return;
|
|
|
|
if(layer_account::is_testing())
|
|
{
|
|
SaveDataToFile();
|
|
}
|
|
|
|
CCtrlPanelLevels::OnDeinitEvent(reason);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CCtrlPanelLogger::OnTickCalculateEvent()
|
|
{
|
|
|
|
if(!(layer_account::is_testing()))
|
|
return;
|
|
|
|
CCtrlPanelLevels::OnTickCalculateEvent();
|
|
|
|
if(_is_first_init)
|
|
{
|
|
_is_first_init=false;
|
|
ResetBalance();
|
|
_start_test_date=TimeToString(_cur_candle_open_time,TIME_DATE);
|
|
}
|
|
|
|
double drawdown=layer_account::drawdown_currency();
|
|
if(drawdown>_max_drawdown)
|
|
{
|
|
_cur_balance=kernel_account::balance();
|
|
_cur_equity=kernel_account::equity();
|
|
_cur_free_margin=kernel_account::free_margin();
|
|
UpdateGridInfo(short_hd,_sell_grid_levels, _sell_lvl,_cur_count_sell, _cur_length_sell,_cur_volume_sell);
|
|
UpdateGridInfo(long_hd,_buy_grid_levels, _buy_lvl,_cur_count_buy, _cur_length_buy,_cur_volume_buy);
|
|
|
|
_max_drawdown=drawdown; //если текущая просадка больше предыдущей, записываем текущую.
|
|
|
|
}
|
|
|
|
layer_order_data* order_data=layer_order::get_by_setting(short_hd._order_settings);
|
|
double drawdown_sell=order_data.drawdown_currency();
|
|
if(drawdown_sell>_max_drawdown_sell)
|
|
_max_drawdown_sell=drawdown_sell; //если текущая просадка больше предыдущей, записываем текущую.
|
|
|
|
|
|
order_data=layer_order::get_by_setting(long_hd._order_settings);
|
|
double drawdown_buy=order_data.drawdown_currency();
|
|
if(drawdown_buy>_max_drawdown_buy)
|
|
_max_drawdown_buy=drawdown_buy; //если текущая просадка больше предыдущей, записываем текущую.
|
|
|
|
|
|
double margin=kernel_account::margin();
|
|
if(margin>_max_margin)
|
|
_max_margin=margin;
|
|
|
|
_tick_value=layer_market::tick_value();
|
|
|
|
if(((int)layer::time_current()) > _next_candle_open_time)
|
|
{
|
|
SaveBalanceToArray();
|
|
ResetBalance();
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
string CCtrlPanelLogger::GetFileName()
|
|
{
|
|
string tf=EnumToString(_output_timeframe);
|
|
string sn=set_name;
|
|
|
|
if(StringCompare(sn,DEFAULT_SET_NAME,false)==0)
|
|
sn="";
|
|
|
|
if(StringLen(sn))
|
|
sn="."+sn;
|
|
|
|
StringReplace(tf,"PERIOD_","");
|
|
StringReplace(_start_test_date,".","");
|
|
StringReplace(_end_test_date,".","");
|
|
return StringFormat("DD.%s.%d%s.%s.%s-%s.%d.%s",Symbol(), MagicNumber,sn, tf,_start_test_date,_end_test_date,_balanceItemCount,_bin_format?"bin":"csv");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CCtrlPanelLogger::SaveDataToFile()
|
|
{
|
|
_balanceItemCount=ArrayRange(_balanceItems,0);
|
|
|
|
if(_balanceItemCount==0)
|
|
return;
|
|
|
|
ResetBalance();
|
|
SaveBalanceToArray();
|
|
_end_test_date=TimeToString(_cur_candle_open_time,TIME_DATE);
|
|
//--- сбросим значение ошибки
|
|
ResetLastError();
|
|
|
|
string fileName=GetFileName(),str;
|
|
|
|
int handle=FileOpen(fileName, FILE_READ | FILE_WRITE | (_bin_format?FILE_BIN:FILE_CSV|FILE_UNICODE),"\t");
|
|
|
|
if(handle == INVALID_HANDLE)
|
|
{
|
|
log_error("Ошибка при создание файла просадки "+TerminalInfoString(TERMINAL_DATA_PATH)+"\\Files\\"+fileName+" , код ="+IntegerToString(GetLastError()));
|
|
return;
|
|
}
|
|
|
|
|
|
if(_bin_format)
|
|
{
|
|
if(!LBFWriteHeaderToFile(handle, _balanceItemCount))
|
|
{
|
|
log_error("Ошибка записи заголовка бинарного файла. Код ошибки="+IntegerToString(GetLastError()));
|
|
FileClose(handle);
|
|
return;
|
|
}
|
|
|
|
uint byteswritten=0;
|
|
for(int i=0; i<_balanceItemCount; i++)
|
|
{
|
|
byteswritten=FileWriteStruct(handle,_balanceItems[i]);
|
|
if(byteswritten!=sizeof(LBFBalanceItem))
|
|
{
|
|
log_error("Ошибка записи элемента данных. Item index:"+ IntegerToString(i) +" Код ошибки="+IntegerToString(GetLastError()));
|
|
}
|
|
}
|
|
|
|
if(!LBFWriteSetToFile(handle))
|
|
{
|
|
log_error("Ошибка записи данных сета. Код ошибки="+IntegerToString(GetLastError()));
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
LBFWriteHeaderCVSFile(handle);
|
|
|
|
for(int i=0; i<_balanceItemCount; i++)
|
|
{
|
|
LBFWriteItemToCVSFile(handle, _balanceItems[i],_decimal_separator_comma);
|
|
}
|
|
}
|
|
log_print(StringFormat("Создан файл просадки %s%s%s, записан массив из %d элементов!",TerminalInfoString(TERMINAL_DATA_PATH), #ifdef __MQL5__"\\MQL5" + #else "\\tester" + #endif "\\Files\\",fileName, _balanceItemCount));
|
|
FileClose(handle);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CCtrlPanelLogger::ResetBalance()
|
|
{
|
|
|
|
_cur_candle_open_time=tool_candle::get_time(CURRENT_SYMBOL, _output_timeframe, 0);
|
|
_next_candle_open_time=(int)_cur_candle_open_time+(c_time::get_seconds_from_time_frame(_output_timeframe));
|
|
|
|
_max_drawdown=0;
|
|
_max_margin=0;
|
|
|
|
_max_drawdown_buy=0;
|
|
_max_drawdown_sell=0;
|
|
|
|
_cur_balance=kernel_account::balance();
|
|
_cur_equity=kernel_account::equity();
|
|
_cur_free_margin=kernel_account::free_margin();
|
|
|
|
UpdateGridInfo(short_hd,_sell_grid_levels, _sell_lvl,_cur_count_sell, _cur_length_sell,_cur_volume_sell);
|
|
UpdateGridInfo(long_hd,_buy_grid_levels, _buy_lvl,_cur_count_buy, _cur_length_buy,_cur_volume_buy);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CCtrlPanelLogger::SaveBalanceToArray()
|
|
{
|
|
if(ArrayResize(_balanceItems,_balanceItemCount+1))
|
|
{
|
|
_balanceItems[_balanceItemCount].dt=_cur_candle_open_time; /*Time[0];*/ //prevtimeEQ/*-TFEQ*60*/;( int )
|
|
_balanceItems[_balanceItemCount].drawdown=_max_drawdown;
|
|
|
|
_balanceItems[_balanceItemCount].balance=_cur_balance;
|
|
_balanceItems[_balanceItemCount].equity=_cur_equity;
|
|
_balanceItems[_balanceItemCount].free_margin=_cur_free_margin;
|
|
_balanceItems[_balanceItemCount].count_buy=_cur_count_buy;
|
|
_balanceItems[_balanceItemCount].count_sell=_cur_count_sell;
|
|
|
|
_balanceItems[_balanceItemCount].drawdown=_max_drawdown;
|
|
_balanceItems[_balanceItemCount].margin=_max_margin;
|
|
|
|
_balanceItems[_balanceItemCount].drawdown_buy=_max_drawdown_buy;
|
|
_balanceItems[_balanceItemCount].drawdown_sell=_max_drawdown_sell;
|
|
|
|
_balanceItems[_balanceItemCount].length_buy=_cur_length_buy;
|
|
_balanceItems[_balanceItemCount].length_sell=_cur_length_sell;
|
|
|
|
_balanceItems[_balanceItemCount].volume_buy=_cur_volume_buy;
|
|
_balanceItems[_balanceItemCount].volume_sell=_cur_volume_sell;
|
|
|
|
_balanceItems[_balanceItemCount].tick_value=_tick_value;
|
|
|
|
_balanceItemCount++;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CCtrlPanelLogger::UpdateGridInfo(logic_handler* hdl,list<CLevelLine*>& lvls, int lvl, int& cur_lvl, double& cur_len, double& cur_vol)
|
|
{
|
|
cur_lvl=lvl;
|
|
|
|
if(lvl<=1)
|
|
{
|
|
cur_len=0;
|
|
cur_vol=lvl?lvls.items[0]._volume:0;
|
|
return;
|
|
}
|
|
|
|
list<c_order *>* orders = layer_order::get(hdl._order_settings);
|
|
cur_len=MathAbs((orders.last_or_default().open_price-orders.first_or_default().open_price)/layer_market::tick_size());
|
|
|
|
cur_vol=0;
|
|
|
|
for(int i=0; i<lvl; i++)
|
|
{
|
|
cur_vol+=lvls.items[i]._volume;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
//+------------------------------------------------------------------+
|