//+------------------------------------------------------------------+ //| 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& 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& 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* 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