| //+------------------------------------------------------------------+
|
| //| DinexSpace | Round Number Levels |
|
| //| Horizontal lines at round prices around the current price, with |
|
| //| stronger "major" lines every N steps. The step is chosen from |
|
| //| the price size or set by hand. Draws levels only, no signals. |
|
| //+------------------------------------------------------------------+
|
| #property copyright "(c) 2026 DinexSpace"
|
| #property version "1.00"
|
| #property description "Round-number price levels around the current price, with major lines every N steps."
|
| #property description "Automatic or manual step. Draws levels only, no signals."
|
| #property strict
|
| #property indicator_chart_window
|
| #property indicator_buffers 0
|
| #property indicator_plots 0
|
|
|
| input group "Step"
|
| input bool InpAuto = true; // Automatic minor step
|
| input double InpStep = 10.0; // Manual minor step (price)
|
| input int InpMajorK = 5; // Major line every N minor steps
|
| input int InpCount = 6; // Lines above and below price
|
|
|
| input group "Display"
|
| input color InpColMinor = C'70,70,70'; // Minor lines
|
| input color InpColMajor = clrSpringGreen; // Major lines
|
| input ENUM_LINE_STYLE InpMinorSty = STYLE_DOT; // Minor line style
|
| input bool InpLabels = true; // Price labels
|
| input bool InpLblMajor = true; // Labels on major lines only
|
|
|
| const string PFX = "DNXRN_";
|
| double g_lastBase = -1, g_lastStep = -1;
|
|
|
| // About 1/100 of price rounded to 1, 2 or 5 x a power of ten (gold ~4000 -> 50, EURUSD ~1.10 -> 0.01).
|
| double AutoStep(double px)
|
| {
|
| double raw = px / 100.0;
|
| double p10 = MathPow(10, MathFloor(MathLog10(raw)));
|
| double m = raw / p10;
|
| double nice = (m < 1.5) ? 1 : (m < 3.5) ? 2 : (m < 7.5) ? 5 : 10;
|
| return nice * p10;
|
| }
|
|
|
| int OnInit()
|
| {
|
| if(InpMajorK < 1 || InpCount < 1 || InpCount > 50 || (!InpAuto && InpStep <= 0))
|
| return INIT_PARAMETERS_INCORRECT;
|
| g_lastBase = -1;
|
| g_lastStep = -1;
|
| return INIT_SUCCEEDED;
|
| }
|
|
|
| void OnDeinit(const int reason)
|
| {
|
| ObjectsDeleteAll(0, PFX);
|
| }
|
|
|
| int OnCalculate(const int rates_total, const int prev_calculated,
|
| const datetime &time[], const double &open[], const double &high[],
|
| const double &low[], const double &close[], const long &tick_volume[],
|
| const long &volume[], const int &spread[])
|
| {
|
| if(rates_total < 1)
|
| return 0;
|
| ArraySetAsSeries(time, true);
|
| ArraySetAsSeries(close, true);
|
| double px = close[0];
|
| if(px <= 0)
|
| return rates_total;
|
| double step = InpAuto ? AutoStep(px) : InpStep;
|
| if(step <= 0)
|
| return rates_total;
|
| double base = MathFloor(px / step) * step;
|
| datetime tLbl = time[0] + 3 * PeriodSeconds(_Period);
|
|
|
| // Rebuild only when the set of levels changes; otherwise just keep the labels next to the last bar.
|
| bool rebuild = (base != g_lastBase || step != g_lastStep || prev_calculated == 0);
|
| if(rebuild)
|
| {
|
| ObjectsDeleteAll(0, PFX);
|
| g_lastBase = base;
|
| g_lastStep = step;
|
| }
|
| int dg = (int)MathMax(0, MathMin(_Digits, MathCeil(-MathLog10(step))));
|
| for(int k = -InpCount + 1; k <= InpCount; k++)
|
| {
|
| double y = base + k * step;
|
| if(y <= 0)
|
| continue;
|
| long idx = (long)MathRound(y / step);
|
| bool major = (idx % InpMajorK) == 0;
|
| string nm = PFX + IntegerToString(k + InpCount);
|
| if(rebuild)
|
| {
|
| ObjectCreate(0, nm, OBJ_HLINE, 0, 0, y);
|
| ObjectSetInteger(0, nm, OBJPROP_COLOR, major ? InpColMajor : InpColMinor);
|
| ObjectSetInteger(0, nm, OBJPROP_STYLE, major ? STYLE_SOLID : InpMinorSty);
|
| ObjectSetInteger(0, nm, OBJPROP_WIDTH, major ? 2 : 1);
|
| ObjectSetInteger(0, nm, OBJPROP_BACK, true);
|
| ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
|
| ObjectSetInteger(0, nm, OBJPROP_HIDDEN, true);
|
| }
|
| if(InpLabels && (major || !InpLblMajor))
|
| {
|
| string lb = nm + "_lbl";
|
| if(ObjectFind(0, lb) < 0)
|
| {
|
| ObjectCreate(0, lb, OBJ_TEXT, 0, tLbl, y);
|
| ObjectSetInteger(0, lb, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
|
| ObjectSetInteger(0, lb, OBJPROP_FONTSIZE, 8);
|
| ObjectSetInteger(0, lb, OBJPROP_COLOR, major ? InpColMajor : InpColMinor);
|
| ObjectSetInteger(0, lb, OBJPROP_SELECTABLE, false);
|
| ObjectSetInteger(0, lb, OBJPROP_HIDDEN, true);
|
| ObjectSetString(0, lb, OBJPROP_TEXT, DoubleToString(y, dg));
|
| }
|
| ObjectMove(0, lb, 0, tLbl, y);
|
| }
|
| }
|
| return rates_total;
|
| }
|
| //+------------------------------------------------------------------+
|