| //+------------------------------------------------------------------+
|
| //| RiskBox.mqh |
|
| //| Copyright 2026, Allan Niñal |
|
| //| https://www.allanninal.dev/mql5 |
|
| //+------------------------------------------------------------------+
|
| //| 1.2: the lines drawn as a risk/reward box (Chart Kit): loss and |
|
| //| profit zones behind the candles, a tag on each line with its |
|
| //| price, money and R, live tooltips, theme colours (auto light or |
|
| //| dark from the chart, colour-blind friendly switch). The lines |
|
| //| stay the source of truth; the box only shows what they say. |
|
| //+------------------------------------------------------------------+
|
| // SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 (see LICENSE)
|
| #ifndef RTM_BOX_MQH
|
| #define RTM_BOX_MQH
|
|
|
| #include "RiskView.mqh"
|
| #include "../ChartKit/CkRiskBox.mqh"
|
| #include "../ChartKit/CkDebug.mqh"
|
|
|
| #define RTM_BOX "RTB_" // box objects (own prefix: the panel and the lines have theirs)
|
|
|
| CkTheme g_th;
|
|
|
| //--- theme tokens for the box, the lines and the panel
|
| void RTMTheme(const ENUM_CK_THEME mode,const bool cb)
|
| {
|
| CkThemeLoad(g_th,mode,cb);
|
| g_ui_light=g_th.light;
|
| g_ui_cb=cb;
|
| }
|
|
|
| //--- an input colour, or the theme's when the input is None
|
| color RTMColour(const color in,const color token) { return(in==clrNONE ? token : in); }
|
|
|
| //--- recolour lines that already exist (theme or chart background changed)
|
| void RTMLineColours(const color ce,const color cs,const color ct)
|
| {
|
| if(!CkCanDraw())
|
| return;
|
| if(ObjectFind(0,L_ENTRY)>=0) CkSetI(L_ENTRY,OBJPROP_COLOR,ce);
|
| if(ObjectFind(0,L_SL)>=0) CkSetI(L_SL,OBJPROP_COLOR,cs);
|
| if(ObjectFind(0,L_TP)>=0) CkSetI(L_TP,OBJPROP_COLOR,ct);
|
| }
|
|
|
| string RTMKind(const RTMCalc &c)
|
| {
|
| if(g_entry_market)
|
| return("at market");
|
| return((c.buy ? c.entry<SymbolInfoDouble(_Symbol,SYMBOL_ASK) : c.entry>SymbolInfoDouble(_Symbol,SYMBOL_BID)) ? "limit" : "stop");
|
| }
|
|
|
| //--- debug: one line when the plan's result changes (not every timer tick)
|
| string g_dbg_plan="";
|
| void RTMDebugPlan(const RTMCalc &c)
|
| {
|
| if(!g_ck_dbg)
|
| return;
|
| //--- no money amounts: with risk in % they would give away the balance, and users paste these lines in public
|
| string k=(c.entry<=0 ? "no lines" : StringFormat("%s entry %s sl %s tp %s, %s, stop %.0f pts, lot %s (raw %.4f), loss per lot %.2f, risk %.2f%% of balance%s",
|
| c.buy ? "BUY" : "SELL",DoubleToString(c.entry,_Digits),DoubleToString(c.sl,_Digits),DoubleToString(c.tp,_Digits),RTMKind(c),
|
| c.stop_pts,DoubleToString(c.lots,RTMLotDigits()),c.lots_raw,c.loss_per_lot,c.real_risk_pct,c.why!="" ? ", note: "+c.why : ""));
|
| if(k==g_dbg_plan)
|
| return;
|
| g_dbg_plan=k;
|
| CkDbg("plan "+k);
|
| }
|
|
|
| void RTMDrawBox(const RTMCalc &c,const bool show)
|
| {
|
| RTMDebugPlan(c);
|
| if(!CkCanDraw())
|
| return;
|
| if(!show || c.entry<=0 || c.sl<=0 || c.entry==c.sl)
|
| {
|
| CkRiskBoxDelete(RTM_BOX);
|
| return;
|
| }
|
| string cur=" "+AccountInfoString(ACCOUNT_CURRENCY);
|
| string lot=(c.lots>0 ? DoubleToString(c.lots,RTMLotDigits())+" lot" : "no lot");
|
| CkBoxSpec b;
|
| CkBoxDefaults(b);
|
| b.entry=c.entry; b.sl=c.sl; b.tp=c.tp;
|
| b.tag_entry=(c.buy ? "BUY " : "SELL ")+lot+" "+RTMKind(c);
|
| b.tag_sl="SL "+DoubleToString(c.sl,_Digits)+(c.lots>0 ? " -"+Money(c.real_risk)+cur+" "+Pct(c.real_risk_pct,2)
|
| : StringFormat(" %.0f pts",c.stop_pts));
|
| b.tag_tp=(c.tp>0 ? "TP "+DoubleToString(c.tp,_Digits)+(c.lots>0 ? " +"+Money(c.reward)+cur : "")
|
| +(c.rr>0 ? " "+DoubleToString(c.rr,1)+"R" : "") : "");
|
| b.tip_sl=StringFormat("Loss zone: entry to stop, %.0f points",c.stop_pts)+(c.lots>0 ? ", -"+Money(c.real_risk)+cur+" at "+lot : "");
|
| b.tip_tp=(c.tp>0 ? StringFormat("Profit zone: entry to target, %.0f points",c.target_pts)+(c.lots>0 ? ", +"+Money(c.reward)+cur : "") : "");
|
| CkRiskBoxDraw(RTM_BOX,g_th,b);
|
| //--- the lines' own tooltips carry the same numbers while hovering
|
| if(ObjectFind(0,L_SL)>=0)
|
| CkSetS(L_SL,OBJPROP_TOOLTIP,b.tag_sl+"\nDrag the line to move the stop");
|
| if(ObjectFind(0,L_TP)>=0)
|
| CkSetS(L_TP,OBJPROP_TOOLTIP,(b.tag_tp!="" ? b.tag_tp : "Take profit")+"\nDrag the line to move the target");
|
| if(ObjectFind(0,L_ENTRY)>=0)
|
| CkSetS(L_ENTRY,OBJPROP_TOOLTIP,b.tag_entry+"\nDrag the line for a pending order price");
|
| }
|
|
|
| #endif
|
| //+------------------------------------------------------------------+
|