//+------------------------------------------------------------------+ //| CtrlPanelLevels.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #ifndef CTRL_PANEL_LEVELS_MQH #define CTRL_PANEL_LEVELS_MQH /* Параметры доступные для настройки данной панели color color_grid_line=clrDeepSkyBlue int width_grid_line=1 color color_no_loss_line=clrAqua int width_no_loss_line=1 color color_stop_line=clrRed int width_stop_line=1 bool show_grid_lvl; bool show_stop_lvl; bool show_no_loss_lvl; */ // Значения по умолчанию #define CTRL_PANEL_COLOR_GRID_LINE clrDeepSkyBlue #define CTRL_PANEL_STYLE_GRID_LINE STYLE_DASHDOTDOT #define CTRL_PANEL_WIDTH_GRID_LINE 1 #define CTRL_PANEL_COLOR_NO_LOSS_LINE clrAqua #define CTRL_PANEL_STYLE_NO_LOSS_LINE STYLE_SOLID #define CTRL_PANEL_WIDTH_NO_LOSS_LINE 1 #define CTRL_PANEL_COLOR_STOP_LINE clrRed #define CTRL_PANEL_STYLE_STOP_LINE STYLE_DASHDOTDOT #define CTRL_PANEL_WIDTH_STOP_LINE 1 #define CTRL_PANEL_COLOR_NEXT_TP_LINE clrRed #define CTRL_PANEL_STYLE_NEXT_TP_LINE STYLE_DASHDOTDOT #define CTRL_PANEL_WIDTH_NEXT_TP_LINE 1 #define CTRL_PANEL_COLOR_FIRST_ORDER_LINE clrGreen #define CTRL_PANEL_STYLE_FIRST_ORDER_LINE STYLE_DASHDOT #define CTRL_PANEL_WIDTH_FIRST_ORDER_LINE 1 #define CTRL_PANEL_COLOR_NO_FIRST_ORDER_LINE clrBrown #define CTRL_PANEL_STYLE_NO_FIRST_ORDER_LINE STYLE_DASHDOT #define CTRL_PANEL_WIDTH_NO_FIRST_ORDER_LINE 2 //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CLevelLine { public: CLevelLine(); public: string _name; string _tooltip; double _price; double _volume; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CLevelLine::CLevelLine():_price(0),_volume(0) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CCtrlPanelLevels: public CCtrlPanelBase { public: CCtrlPanelLevels(); CCtrlPanelLevels(const string param); //--- Инициализация/деинициализация virtual bool OnInitEvent(void); virtual void OnDeinitEvent(const int reason); //--- Обработчик тиков virtual void OnTickCalculateEvent(); virtual void DrawPanelInfo(); protected: color _color_grid_line; uint _width_grid_line; color _color_no_loss_line; uint _width_no_loss_line; color _color_stop_line; uint _width_stop_line; color _color_next_tp_line; uint _width_next_tp_line; color _color_first_order_line; uint _width_first_order_line; color _color_no_first_order_line; uint _width_no_first_order_line; bool _show_grid_lvl; bool _show_stop_lvl; bool _show_no_loss_lvl; bool _show_first_order_lvl; bool _show_no_first_order_lvl; protected: double _balance; int _last_hour; int _sell_lvl; double _sell_volume_max; double _sell_len_max; list _sell_grid_levels; // Уровни открытия колен сетки CLevelLine _sell_lvl_no_loss; // Уровень БУ CLevelLine _sell_lvl_stop; // Уровень Стопа CLevelLine _sell_lvl_first_order; // Уровень ордера открытия по цене CLevelLine _sell_lvl_no_first_order; // Уровень блокировки открытия первого ордера int _buy_lvl; double _buy_volume_max; double _buy_len_max; list _buy_grid_levels; // Уровни открытия колен сетки CLevelLine _buy_lvl_no_loss; // Уровень БУ CLevelLine _buy_lvl_stop; // Уровень Стопа CLevelLine _buy_lvl_first_order; // Уровень ордера открытия по цене CLevelLine _buy_lvl_no_first_order; // Уровень блокировки открытия первого ордера protected: void InitGridParam(logic_handler* hdl,double& volume_max, double& len_max, list& lvls, CLevelLine& lvl_no_loss, CLevelLine& lvl_stop, CLevelLine& lvl_first_order,CLevelLine& lvl_no_first_order); bool OnChangeGridLevel(logic_handler* hdl, int& lvl, list& lvls, CLevelLine& lvl_no_loss, CLevelLine& lvl_stop, CLevelLine& lvl_first_order); void CalculateStopLevel(logic_handler* hdl, CLevelLine& lvl_stop); void DrawGridLevel(list& lvls, CLevelLine& lvl_no_loss,CLevelLine& lvl_stop, CLevelLine& lvl_first_order,CLevelLine& lvl_no_first_order); void DeleteGridLevel(list& lvls, CLevelLine& lvl_no_loss,CLevelLine& lvl_stop, CLevelLine& lvl_first_order,CLevelLine& lvl_no_first_order); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CCtrlPanelLevels::CCtrlPanelLevels(const string param):CCtrlPanelBase(param),_sell_lvl(0),_buy_lvl(0) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CCtrlPanelLevels::CCtrlPanelLevels():_sell_lvl(0),_buy_lvl(0) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CCtrlPanelLevels::OnInitEvent(void) { _color_grid_line=GetParamColor("color_grid_line",CTRL_PANEL_COLOR_GRID_LINE); _width_grid_line=GetParamInt("width_grid_line",CTRL_PANEL_WIDTH_GRID_LINE); _color_no_loss_line=GetParamColor("color_no_loss_line",CTRL_PANEL_COLOR_NO_LOSS_LINE); _width_no_loss_line=GetParamInt("width_no_loss_line",CTRL_PANEL_WIDTH_NO_LOSS_LINE); _color_stop_line=GetParamColor("color_stop_line",CTRL_PANEL_COLOR_STOP_LINE); _width_stop_line=GetParamInt("width_stop_line",CTRL_PANEL_WIDTH_STOP_LINE); _color_first_order_line=GetParamColor("color_first_order_line",CTRL_PANEL_COLOR_FIRST_ORDER_LINE); _width_first_order_line=GetParamInt("width_first_order_line",CTRL_PANEL_WIDTH_FIRST_ORDER_LINE); _color_no_first_order_line=GetParamColor("color_no_first_order_line",CTRL_PANEL_COLOR_NO_FIRST_ORDER_LINE); _width_no_first_order_line=GetParamInt("width_no_first_order_line",CTRL_PANEL_WIDTH_NO_FIRST_ORDER_LINE); _show_grid_lvl=GetParamBool("show_grid_lvl",false); _show_stop_lvl=GetParamBool("show_stop_lvl",false); _show_no_loss_lvl=GetParamBool("show_no_loss_lvl",false); _show_first_order_lvl=GetParamBool("show_first_order_lvl",false); _show_no_first_order_lvl=GetParamBool("show_no_first_order_lvl",false); InitGridParam(short_hd, _sell_volume_max, _sell_len_max, _sell_grid_levels, _sell_lvl_no_loss,_sell_lvl_stop,_sell_lvl_first_order,_sell_lvl_no_first_order); InitGridParam(long_hd, _buy_volume_max, _buy_len_max, _buy_grid_levels, _buy_lvl_no_loss,_buy_lvl_stop,_buy_lvl_first_order,_buy_lvl_no_first_order); _balance=kernel_account::balance(); _last_hour=0; return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CCtrlPanelLevels::OnDeinitEvent(const int reason) { DeleteGridLevel(_sell_grid_levels,_sell_lvl_no_loss,_sell_lvl_stop,_sell_lvl_first_order,_sell_lvl_no_first_order); DeleteGridLevel(_buy_grid_levels,_buy_lvl_no_loss,_buy_lvl_stop,_buy_lvl_first_order,_buy_lvl_no_first_order); _sell_grid_levels.clear(); _buy_grid_levels.clear(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CCtrlPanelLevels::OnTickCalculateEvent() { if(_show_grid_lvl||_show_stop_lvl||_show_no_loss_lvl||_show_first_order_lvl) { _readyForUpdate=OnChangeGridLevel(short_hd, _sell_lvl, _sell_grid_levels,_sell_lvl_no_loss,_sell_lvl_stop,_sell_lvl_first_order); _readyForUpdate|=OnChangeGridLevel(long_hd, _buy_lvl, _buy_grid_levels,_buy_lvl_no_loss,_buy_lvl_stop,_buy_lvl_first_order); } double balance=kernel_account::balance(); MqlDateTime dt_struct; TimeCurrent(dt_struct); if(CP(balance,0.01)!=_balance || _last_hour!=dt_struct.hour ) { if(_show_stop_lvl) { CalculateStopLevel(short_hd,_sell_lvl_stop); CalculateStopLevel(long_hd,_buy_lvl_stop); _readyForUpdate=true; } _balance=balance; _last_hour=dt_struct.hour; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CCtrlPanelLevels::DrawGridLevel(list& lvls, CLevelLine& lvl_no_loss,CLevelLine& lvl_stop,CLevelLine& lvl_first_order,CLevelLine& lvl_no_first_order) { if(_show_grid_lvl) { int i=1; LIST_FOREACH(lvls, CLevelLine*,item, { HLineDelete(0,item._name); if(item._price!=0) { HLineCreate(0, item._name,0,item._price,_color_grid_line, CTRL_PANEL_STYLE_GRID_LINE,_width_grid_line,item._tooltip,true,false,true,0); } i++; }); } if(_show_no_loss_lvl) { // Линия БУ HLineDelete(0,lvl_no_loss._name); if(lvl_no_loss._price) { HLineCreate(0, lvl_no_loss._name,0,lvl_no_loss._price,_color_no_loss_line, CTRL_PANEL_STYLE_NO_LOSS_LINE,_width_no_loss_line,lvl_no_loss._tooltip,true,false,true,0); } } if(_show_stop_lvl) { // Линия Стопа HLineDelete(0,lvl_stop._name); if(lvl_stop._price) { HLineCreate(0, lvl_stop._name,0,lvl_stop._price,_color_stop_line, CTRL_PANEL_STYLE_STOP_LINE,_width_stop_line,lvl_stop._tooltip,true,false,true,0); } } if(_show_first_order_lvl) { // Линия Стопа HLineDelete(0,lvl_first_order._name); if(lvl_first_order._price) { HLineCreate(0, lvl_first_order._name,0,lvl_first_order._price,_color_first_order_line, CTRL_PANEL_STYLE_FIRST_ORDER_LINE,_width_first_order_line,lvl_first_order._tooltip,true,false,true,0); } } if(_show_no_first_order_lvl) { // Линия Стопа HLineDelete(0,lvl_no_first_order._name); if(lvl_no_first_order._price) { HLineCreate(0, lvl_no_first_order._name,0,lvl_no_first_order._price,_color_no_first_order_line, CTRL_PANEL_STYLE_NO_FIRST_ORDER_LINE,_width_no_first_order_line,lvl_no_first_order._tooltip,true,false,true,0); } } } void CCtrlPanelLevels::DeleteGridLevel(list& lvls, CLevelLine& lvl_no_loss,CLevelLine& lvl_stop,CLevelLine& lvl_first_order,CLevelLine& lvl_no_first_order) { LIST_FOREACH(lvls, CLevelLine*, item, {HLineDelete(0,item._name);}); HLineDelete(0,lvl_no_loss._name); HLineDelete(0,lvl_stop._name); HLineDelete(0,lvl_first_order._name); HLineDelete(0,lvl_no_first_order._name); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CCtrlPanelLevels::DrawPanelInfo() { DrawGridLevel(_sell_grid_levels,_sell_lvl_no_loss,_sell_lvl_stop,_sell_lvl_first_order,_sell_lvl_no_first_order); DrawGridLevel(_buy_grid_levels,_buy_lvl_no_loss,_buy_lvl_stop,_buy_lvl_first_order,_buy_lvl_no_first_order); _readyForUpdate=false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CCtrlPanelLevels::CalculateStopLevel(logic_handler* hdl, CLevelLine& lvl_stop) { if(!(CloseAllOrders_ByDrawdownMoney!=0 || (CloseAllOrders_ByDrawdownPercent!=0 && kernel_account::balance()!=0) || hdl._settings.close_orders_by_drawdown_for_001lot!=0 )) return; double stop=kernel_account::balance()*CloseAllOrders_ByDrawdownPercent/100; if(CP(stop,0.01)==0) { stop=CloseAllOrders_ByDrawdownMoney; } else { stop=(CloseAllOrders_ByDrawdownMoney==0)?stop:MathMin(stop,CloseAllOrders_ByDrawdownMoney); } double stopsize=0; if(hdl._settings.close_orders_by_drawdown_for_001lot!=0) { c_order* ord=layer_order::get_min(hdl._order_settings); if(GC_CHECK_PTR(ord)) { stopsize= hdl._settings.close_orders_by_drawdown_for_001lot / 0.01 * ord.lot; stop=(stop==0)?stopsize:MathMin(stop,stopsize); } } double drawdown=0; double lot_sum=0; double last_level=0; double dist=0; double cur_dd=0; double ppl=layer_market::price_for_point_per_lot(); double swap=0; list* orders = layer_order::get(hdl._order_settings); LIST_FOREACH(orders, c_order *, item,{swap+=item.commission+item.swap;}); LIST_FOREACH(orders, c_order *, item, { if(item.is_order()) { if(last_level!=0) { dist=tool_order::get_distance(hdl._order_settings.order_type,last_level,item.open_price); cur_dd=dist*lot_sum*ppl; if(CP((drawdown+cur_dd+swap),0.01)>stop) break; drawdown+=cur_dd; } last_level=item.open_price; lot_sum += item.lot; } }); if(lot_sum==0) { lvl_stop._price=0; return; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ dist=layer_market::to_points((stop+drawdown+swap)/ lot_sum / layer_market::tick_value()); lvl_stop._price=tool_order::deduct_points(hdl._order_settings.order_type, last_level, dist) ; log_debug(string("==CALC STOP LEVEL FOR ")+string(hdl._order_settings.is_buy_direction?"BUY":"SELL")); log_debug(string("STOP SIZE ")+dtos(stop)+ layer_account::currency()); log_debug(string("LAST LEVEL PRICE ")+dtos(last_level)); log_debug(string("GRID VOLUME ")+dtos(lot_sum)); log_debug(string("GRID DRAWDAWN ")+dtos(drawdown)+ layer_account::currency()); log_debug(string("GRID SWAP ")+dtos(swap)+ layer_account::currency()); log_debug(string("POINT PER LOT ")+dtos(ppl)); log_debug(string("TICK VALUE ")+dtos(layer_market::tick_value())); log_debug(string("DISTANCE TO STOP ")+dtos(dist)); log_debug(string("STOP PRICE ")+dtos(lvl_stop._price)); log_debug(string("TIME CURRENT ")+itos(TimeCurrent())); lvl_stop._tooltip="STOP "+(hdl._order_settings.is_buy_direction?"BUY":"SELL")+ " LVL "+DoubleToString(stop,2)+" "+layer_account::currency(); if(CP(stop)!=CloseAllOrders_ByDrawdownMoney && CP(stop)!=stopsize && CloseAllOrders_ByDrawdownPercent!=0) { lvl_stop._tooltip+="("+ DoubleToString(CloseAllOrders_ByDrawdownPercent,2)+"%)"; } lvl_stop._tooltip+="\n"+dtos(lvl_stop._price); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CCtrlPanelLevels::OnChangeGridLevel(logic_handler* hdl, int& lvl, list& lvls, CLevelLine& lvl_no_loss, CLevelLine& lvl_stop, CLevelLine& lvl_first_order) { bool result=false; order_count_t* orders_count= layer_order::get_count(hdl._order_settings); int cur_lvl=orders_count.all(); // если открыто новое колено if(cur_lvl!=lvl) { list* orders = layer_order::get(hdl._order_settings); double price_for_level= cur_lvl?orders.last_or_default().open_price:0; for(int i=1; i<=hdl._settings.max_open_orders; i++) { if(i<=cur_lvl) { lvls.items[i-1]._price=0; } else { price_for_level=layer::correct_price(hdl.get_order_position(price_for_level, i)); lvls.items[i-1]._price= price_for_level; } } CalculateStopLevel(hdl,lvl_stop); result=true; } if(cur_lvl>1) { lvl_no_loss._price=layer::correct_price(calc_take_profit::get(hdl._settings, hdl._order_settings, tp_level_without_loss, 0)); } else { lvl_no_loss._price=0; } lvl_first_order._price=cur_lvl?0:hdl._lvl_for_open; lvl=orders_count.all(); return result; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CCtrlPanelLevels::InitGridParam(logic_handler* hdl,double& volume_max, double& len_max, list& lvls, CLevelLine& lvl_no_loss, CLevelLine& lvl_stop, CLevelLine& lvl_first_order, CLevelLine& lvl_no_first_order) { lvls.clear(); volume_max=0; len_max=0; for(int i=1; i<=hdl._settings.max_open_orders; i++) { CLevelLine* lvl=new CLevelLine(); lvl._volume=NormalizeDouble(calc_lot::get(hdl._settings, hdl._order_settings, i,no_gap,0, false),hdl._settings.lot_exp); volume_max+=lvl._volume; lvl._name=(hdl._order_settings.is_buy_direction?"BUY":"SELL")+" LVL #"+IntegerToString(i)+" vol "+DoubleToString(lvl._volume,2)+"("+DoubleToString(volume_max,2)+"/" ; len_max+=(i==1)?0:calc_grid_step::get(hdl._settings,i); lvls.add(lvl); } LIST_FOREACH(lvls, CLevelLine*, item, { item._name+=DoubleToString(volume_max,2) +")"; }); lvl_no_loss._name="NO LOSS LVL FOR"+(hdl._order_settings.is_buy_direction?"BUY":"SELL")+ " GRID"; lvl_stop._name="STOP LVL FOR " +(hdl._order_settings.is_buy_direction?"BUY":"SELL")+ " GRID"; lvl_first_order._name="FIRST LVL FOR " +(hdl._order_settings.is_buy_direction?"BUY":"SELL")+ " GRID vol "+ DoubleToString(lvls.items[0]._volume,2) ; lvl_no_first_order._name="NO FIRST ORDER LVL FOR " +(hdl._order_settings.is_buy_direction?"BUY":"SELL")+ " GRID"; lvl_no_first_order._price=hdl._settings.open_first_order?hdl._settings.lvl_no_first_order:0; } #endif //+------------------------------------------------------------------+