| //+------------------------------------------------------------------+
|
| //| CkRiskBox.mqh |
|
| //| Copyright 2026, Allan Niñal |
|
| //| https://www.allanninal.dev/mql5 |
|
| //+------------------------------------------------------------------+
|
| //| Chart kit risk/reward box: a loss zone (entry to stop) and a |
|
| //| profit zone (entry to target) behind the candles, and one tag |
|
| //| per line at the box's right end ("SL 1.08210 -50.00 USD 1R"). |
|
| //| The product's lines stay the source of truth: the box is redrawn |
|
| //| from their prices and never moves them. Draw only; it sends no |
|
| //| order and needs no trade permission. |
|
| //+------------------------------------------------------------------+
|
| #ifndef CK_RISKBOX_MQH
|
| #define CK_RISKBOX_MQH
|
|
|
| #include "CkTheme.mqh"
|
| #include "CkObj.mqh"
|
|
|
| struct CkBoxSpec
|
| {
|
| double entry,sl,tp; // tp 0 = no target
|
| string tag_entry,tag_sl,tag_tp; // the line tags ("" = none)
|
| string tip_sl,tip_tp; // zone tooltips
|
| int bars_back,bars_ahead; // box width around the current bar
|
| };
|
|
|
| void CkBoxDefaults(CkBoxSpec &b)
|
| {
|
| b.entry=0; b.sl=0; b.tp=0;
|
| b.tag_entry=""; b.tag_sl=""; b.tag_tp="";
|
| b.tip_sl=""; b.tip_tp="";
|
| b.bars_back=24; b.bars_ahead=10;
|
| }
|
|
|
| //--- bars visible to the right of the current bar: the chart-shift area (0 with chart shift off)
|
| int CkBarsAhead(void)
|
| {
|
| if(!ChartGetInteger(0,CHART_SHIFT))
|
| return(0);
|
| double shift=ChartGetDouble(0,CHART_SHIFT_SIZE); // % of the chart width
|
| long wb=ChartGetInteger(0,CHART_WIDTH_IN_BARS);
|
| return((int)MathMax(0,MathFloor(wb*shift/100.0)-1));
|
| }
|
|
|
| //--- the box's time span: from `bars_back` bars before the current bar to `bars_ahead` bars after it, cut to what the
|
| //--- chart shows to the right, so the tags at the right end stay on screen
|
| void CkBoxTimes(const CkBoxSpec &b,datetime &t1,datetime &t2)
|
| {
|
| int sec=PeriodSeconds(_Period);
|
| datetime t0=iTime(_Symbol,_Period,0);
|
| if(t0==0)
|
| t0=TimeCurrent();
|
| t1=t0-(datetime)(b.bars_back*sec);
|
| t2=t0+(datetime)(MathMin(b.bars_ahead,CkBarsAhead())*sec);
|
| }
|
|
|
| void CkRiskBoxDelete(const string pfx) { CkDeleteAll(pfx); }
|
|
|
| void CkRiskBoxDraw(const string pfx,const CkTheme &th,const CkBoxSpec &b)
|
| {
|
| if(!CkCanDraw())
|
| return;
|
| if(b.entry<=0 || b.sl<=0 || b.entry==b.sl)
|
| {
|
| CkRiskBoxDelete(pfx);
|
| return;
|
| }
|
| datetime t1,t2;
|
| CkBoxTimes(b,t1,t2);
|
| bool buy=b.sl<b.entry;
|
| CkRect(pfx+"loss",t1,b.entry,t2,b.sl,th.zone_neg,b.tip_sl);
|
| if(b.tp>0)
|
| CkRect(pfx+"gain",t1,b.entry,t2,b.tp,th.zone_pos,b.tip_tp);
|
| else
|
| CkDelete(pfx+"gain");
|
| //--- the stop and target tags sit on the outer side of their lines, the entry tag inside the loss zone: no overlap
|
| if(b.tag_entry!="")
|
| CkText(pfx+"t_en",t2,b.entry,b.tag_entry,th.acc,buy ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER);
|
| else
|
| CkDelete(pfx+"t_en");
|
| if(b.tag_sl!="")
|
| CkText(pfx+"t_sl",t2,b.sl,b.tag_sl,th.neg,buy ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER);
|
| else
|
| CkDelete(pfx+"t_sl");
|
| if(b.tp>0 && b.tag_tp!="")
|
| CkText(pfx+"t_tp",t2,b.tp,b.tag_tp,th.pos,buy ? ANCHOR_RIGHT_LOWER : ANCHOR_RIGHT_UPPER);
|
| else
|
| CkDelete(pfx+"t_tp");
|
| }
|
|
|
| #endif
|
| //+------------------------------------------------------------------+
|