Bell Curve on basic charts

Dear Forun,

I am trying to put in a bell curve on a basic line graph that tracks the variation from the 200 DMA. The very basic code

_SECTION_BEGIN("200 DMA var");

DEMA200 = EMA(C,200);
DMA200VarPer = ((C-DEMA200)/DEMA200)*100;
Plot(DMA200VarPer,"DMA200Var",colorRed,styleLine);
Plot(0,"Zero",colorBlack,styleLine|styleDashed);

Filter =1;
AddColumn(C,"Close",1.2);
AddColumn(DEMA200,"DEMA200",1.2);
AddColumn(DMA200VarPer,"DEMA200Var",1.2);

_SECTION_END();

Wish to have a bell curve (normal distribution with probability) plotted on the 2nd pane with red line chart. How do we visualise it?

Request guidance on whether to use - Visual Range or Predefine Range for bars.

// Bell Curve: Close Price vs 200-Day MA Distribution
// Plots histogram + Gaussian fit for (Close / MA200) ratios

// Parameters
MA_Period = 200;
Num_Bins = 50; // Adjust for smoother histogram
Min_Ratio = 0.5; // X-axis range
Max_Ratio = 1.5;
Bin_Width = (Max_Ratio - Min_Ratio) / Num_Bins;

// Compute ratios (only where MA is valid)
MA200 = MA(Close, MA_Period);
Ratio = IIf(MA200 > 0, Close / MA200, Null); // Avoid div by zero
Valid_Ratio = IIf(BarIndex() >= MA_Period - 1 AND !IsNull(Ratio), Ratio, Null);

// Collect valid ratios into an array
Num_Bars = BarCount;
Ratio_Array = Null;
for(i = MA_Period - 1; i < Num_Bars; i++) {
    if(!IsNull(Valid_Ratio[i])) {
        Ratio_Array[i] = Valid_Ratio[i];
    }
}

// Compute histogram
Hist = 0;
for(i = MA_Period - 1; i < Num_Bars; i++) {
    if(!IsNull(Ratio_Array[i])) {
        bin_idx = Floor((Ratio_Array[i] - Min_Ratio) / Bin_Width);
        if(bin_idx >= 0 AND bin_idx < Num_Bins) {
            Hist[bin_idx] = Nz(Hist[bin_idx]) + 1;
        }
    }
}

// Compute Gaussian parameters (mean and std dev)
Sum_R = 0; Count = 0; Sum_Sq = 0;
for(i = MA_Period - 1; i < Num_Bars; i++) {
    r = Ratio_Array[i];
    if(!IsNull(r)) {
        Sum_R += r;
        Sum_Sq += r * r;
        Count++;
    }
}
Mean = IIf(Count > 0, Sum_R / Count, 0);
Variance = IIf(Count > 0, (Sum_Sq / Count) - (Mean * Mean), 0);
StdDev = IIf(Count > 0, sqrt(Max(Variance, 0)), 0);

// Normalize histogram
Max_Hist = 0;
for(i = 0; i < Num_Bins; i++) {
    if(Nz(Hist[i]) > Max_Hist) Max_Hist = Nz(Hist[i]);
}
Norm_Hist = IIf(Max_Hist > 0, Hist / Max_Hist, 0);

// Gaussian curve points
GraphX = 0; GraphY = 0; Max_GraphY = 0;
for(i = 0; i < Num_Bins; i++) {
    x = Min_Ratio + (i + 0.5) * Bin_Width;
    GraphX[i] = x;
    if(StdDev > 0.0001) { // Added small threshold to prevent div by zero
        GraphY[i] = exp(-((x - Mean) / (2 * StdDev * StdDev)) ^ 2) / (StdDev * sqrt(2 * 3.14159));
    } else {
        GraphY[i] = 0;
    }
    if(GraphY[i] > Max_GraphY) Max_GraphY = GraphY[i];
}
GraphY = IIf(Max_GraphY > 0, GraphY * Max_Hist / Max_GraphY, 0);

// Plot main chart
Plot(Close, "Close", colorBlack, styleCandle);
Plot(MA200, "200 DMA", colorBlue, styleLine);

// Custom pane for bell curve
_SECTION_BEGIN("Bell Curve");
SetChartOptions(0, chartShowArrows | chartShowDates);
SetChartBkColor(colorWhite);

// Gfx setup
Pane_Width = Status("pxwidth") / 2; // Dynamic scaling
Pane_Height = 100;
GfxSetOverlayMode(1);
GfxSelectFont("Arial", 10);
GfxSetBkMode(1);

// Draw axes
GfxSelectPen(colorRGB(128,128,128), 1);
GfxMoveTo(10, Pane_Height - 10); GfxLineTo(Pane_Width - 10, Pane_Height - 10); // X axis
GfxMoveTo(10, 10); GfxLineTo(10, Pane_Height - 10); // Y axis

// Labels
GfxTextOut("Ratio (Close / 200DMA)", Pane_Width / 2 - 50, Pane_Height - 5);
GfxTextOut("Frequency", 5, Pane_Height / 2);

// Draw histogram bars
GfxSelectSolidBrush(colorRGB(135,206,250)); // Light blue
for(i = 0; i < Num_Bins; i++) {
    bar_height = (Pane_Height - 20) * Nz(Norm_Hist[i]);
    bar_x = 10 + (i * ((Pane_Width - 20) / Num_Bins));
    GfxRectangle(bar_x, Pane_Height - 10 - bar_height, bar_x + ((Pane_Width - 20) / Num_Bins) - 2, Pane_Height - 10);
}

// Draw Gaussian curve
GfxSelectPen(colorRed, 2);
first_point = True;
for(i = 0; i < Num_Bins; i++) {
    curve_y = 10 + (Pane_Height - 20) * Nz(GraphY[i] / Max_Hist);
    curve_x = 10 + (i * ((Pane_Width - 20) / Num_Bins));
    if(first_point) {
        GfxMoveTo(curve_x, Pane_Height - 10 - curve_y);
        first_point = False;
    } else {
        GfxLineTo(curve_x, Pane_Height - 10 - curve_y);
    }
}

// Mean line
if(Count > 0) {
    GfxSelectPen(colorGreen, 1, 2); // Dashed
    GfxSetCoordsMode(1);
    x_scaled = 10 + ((Mean - Min_Ratio) / (Max_Ratio - Min_Ratio)) * (Pane_Width - 20);
    GfxMoveTo(x_scaled, 0);
    GfxLineTo(x_scaled, Pane_Height);
}

// Text annotations
GfxTextOut("Mean: " + NumToStr(Mean, 1.3), 10, 5);
GfxTextOut("StdDev: " + NumToStr(StdDev, 1.3), 10, 20);
GfxTextOut("Data Points: " + NumToStr(Count, 1.0), 10, 35);
_SECTION_END();

// Title
Title = StrFormat("{{NAME}} - {{INTERVAL}} {{DATE}} O=%.2f H=%.2f L=%.2f C=%.2f (%.1f%%) " +
                  "Ratio Mean=%.3f Std=%.3f",
                  O, H, L, C, SelectedValue(ROC(Close, 1)), Mean, StdDev);
4 Likes

@Anthony many thanks. That indeed is the bell curve. A bit simplistic approach I found through the inbuilt Gemini help is as follows:

_SECTION_BEGIN("200 DMA var");

LookbackPeriod = Param("Lookback for Stats", 252, 10, 500, 1); // e.g., 252 for a year of daily data
MAPeriod = Param("Moving Averages", 200, 10, 500, 1); // e.g., Moving Average of daily data

DEMA200 = EMA(C,MAPeriod);
DMA200VarPer = ((C-DEMA200)/DEMA200)*100;
Plot(DMA200VarPer,"DMA200Var",colorRed,styleLine|styleThick);
Plot(0,"Zero",colorBlack,styleLine);
// Bell Curve related plots (Mean and Standard Deviation lines)


MeanDMA200Var = MA(DMA200VarPer, LookbackPeriod);
StDevDMA200Var = StDev(DMA200VarPer, LookbackPeriod);

Plot(MeanDMA200Var, "Mean DMA200Var", colorViolet, styleLine);
Plot(MeanDMA200Var + StDevDMA200Var, "+1 StdDev", colorBlue, styleLine | styleDashed);
Plot(MeanDMA200Var - StDevDMA200Var, "-1 StdDev", colorBlue, styleLine | styleDashed);
Plot(MeanDMA200Var + 2 * StDevDMA200Var, "+2 StdDev", colorGreen, styleLine | styleDashed);
Plot(MeanDMA200Var - 2 * StDevDMA200Var, "-2 StdDev", colorGreen, styleLine | styleDashed);
Plot(MeanDMA200Var + 3 * StDevDMA200Var, "+3 StdDev", colorRed, styleLine | styleDashed);
Plot(MeanDMA200Var - 3 * StDevDMA200Var, "-3 StdDev", colorRed, styleLine | styleDashed);

Filter =1;
AddColumn(C,"Close",1.2);
AddColumn(DEMA200,"DEMA200",1.2);
AddColumn(DMA200VarPer,"DEMA200Var",1.2);

_SECTION_END();

Still thinking a bit, whether there should be a lookback or LAG inserted in there to understand where we stand as per historical data?

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.