John Ehlers - PROJECTED MOVING AVERAGE INDICATOR AND ITS PREDICTION

Hello this is the implementation of the formula from John Ehlers Projected moving average indicator, a new simple but elegant solution to remove the lag of a moving average:

// Parameters
Length = Param("Length", 20, 5, 50, 1);

// Array initialization
PMA = Null;
Slope_val = Null;
SMA_val = Null;
Predict = Null;

// bars calculation
for (i = Length - 1; i < BarCount; i++)
{
    // variables initialization
    Sx = 0;
    Sy = 0;
    Sxx = 0;
    Syy = 0;
    Sxy = 0;
    
    // Cycle to calculate sums
    for (count = 1; count <= Length; count++)
    {
        Sx = Sx + count;
        Sy = Sy + Close[i - (count - 1)];
        Sxx = Sxx + count * count;
        Syy = Syy + Close[i - (count - 1)] * Close[i - (count - 1)];
        Sxy = Sxy + count * Close[i - (count - 1)];
    }
    
    // Slope Calculation
    Slope_val[i] = -(Length * Sxy - Sx * Sy) / (Length * Sxx - Sx * Sx);
    
    // SMA Calculation
    SMA_val[i] = Sy / Length;
    
    // PMA Calculation
    PMA[i] = SMA_val[i] + Slope_val[i] * Length / 2;
    
    // Predict Calculation
    if (i >= Length + 1) // Slope[i-2] at least
    {
        Predict[i] = PMA[i] + 0.5 * (Slope_val[i] - Slope_val[i-2]) * Length;
    }
}

// Plot results
Plot(PMA, "PMA(" + Length + ")", coloryellow, styleLine | styleThick);
Plot(Predict, "Predict", colorRed, styleLine);
Plot(Close, "Close", colorBlack, styleCandle);

// Plot optional for SMA
// Plot(SMA_val, "SMA", colorGreen, styleLine);

Here the PMA Oscillator with ribbon plot. You can change the buy/sell conditions according to your needs:

// PMA Slope Oscillator
// Converted from EasyLanguage to AmiBroker AFL
// Based on John F. Ehlers' PMA Slope and Its Prediction (2024)

// Parameters
Length = Param("Length", 20, 5, 50, 1);

// Array initialization
PMA = Null;
Slope_val = Null;
SMA_val = Null;
Predict = Null;

// Initialize arrays
PMA = C * 0;      // Initialize with same size as Close array
Slope_val = C * 0;
SMA_val = C * 0;
Predict = C * 0;

// Main calculation loop
for (i = Length - 1; i < BarCount; i++) 
{
    // Variables initialization for linear regression
    Sx = 0;
    Sy = 0;
    Sxx = 0;
    Syy = 0;
    Sxy = 0;
    
    // Calculate sums for linear regression
    for (count = 1; count <= Length; count++)
    {
        if (i - (count - 1) >= 0) // Boundary check
        {
            Sx = Sx + count;
            Sy = Sy + Close[i - (count - 1)];
            Sxx = Sxx + count * count;
            Syy = Syy + Close[i - (count - 1)] * Close[i - (count - 1)];
            Sxy = Sxy + count * Close[i - (count - 1)];
        }
    }
    
    // Slope Calculation (linear regression slope)
    denominator = Length * Sxx - Sx * Sx;
    if (denominator != 0)
    {
        Slope_val[i] = -(Length * Sxy - Sx * Sy) / denominator;
    }
    
    // SMA Calculation
    SMA_val[i] = Sy / Length;
    
    // PMA Calculation (Predicted Moving Average)
    PMA[i] = SMA_val[i] + Slope_val[i] * Length / 2;
    
    // Predict Calculation using Ehlers' method
    if (i >= 4) // Need at least 4 bars for Slope[i-4]
    {
        Predict[i] = 1.5 * Slope_val[i] - 0.5 * Slope_val[i-4];
    }
}

// Alternative vectorized calculation for better performance
// You can comment out the loop above and use this instead:
/*
// Vectorized Linear Regression Slope calculation
period = Length;
x = Cum(1);
sx = Sum(x, period);
sy = Sum(C, period);
sxx = Sum(x*x, period);
sxy = Sum(x*C, period);
Slope_vectorized = -(period * sxy - sx * sy) / (period * sxx - sx * sx);
SMA_vectorized = MA(C, period);
PMA_vectorized = SMA_vectorized + Slope_vectorized * period / 2;
Predict_vectorized = 1.5 * Slope_vectorized - 0.5 * Ref(Slope_vectorized, -4);
*/

// Plot the oscillator
Plot(Slope_val, "Slope", colorBlue, styleLine | styleThick);
Plot(0, "Zero Line", colorGrey40, styleDashed);
Plot(Predict, "Predict", colorRed, styleLine | styleThick);

// Optional: Add PMA and Close for reference (comment out if you want only oscillator)
// Plot(PMA, "PMA(" + Length + ")", colorYellow, styleLine);
// Plot(Close, "Close", colorBlack, styleCandle);

// Buy/Sell signals based on prediction vs slope
Buy = Cross(Predict, Slope_val);
Sell = Cross(Slope_val, Predict);

// Alternative signals based on zero crossings
// Buy = Cross(Predict, 0);
// Sell = Cross(0, Predict);

// Plot buy/sell arrows
PlotShapes(IIf(Buy, shapeSquare, shapeNone), colorGreen, 0, L, Offset = -40);
PlotShapes(IIf(Sell, shapeSquare, shapeNone), colorRed, 0, H, Offset = 40);

Ribbon_kol = IIf(Buy, colorAqua, IIf(SELL, colorPink, colorWhite));
Plot(100, "ribbon", Ribbon_kol, styleOwnScale | styleArea | styleNoLabel, -0.5, 100);


// Title
Title = "PMA Slope Oscillator - Length: " + Length + 
        "\nSlope: " + WriteVal(Slope_val, 1.4) + 
        " | Predict: " + WriteVal(Predict, 1.4);

If you are posting codes written by AI ChatGPT or similar, you should tell that and it is also good to do this in a smart way prompting the GPT to use array vector processing. The looping code is not necessary and inefficient.

1 Like

Yes. Thanks for the comment Tomasz, correct I used Claude 4 instead. Here the vectorized version of the formula: `// PMA Slope Oscillator - Vectorized Version

Vectorized Version
// Converted from EasyLanguage to AmiBroker AFL
// Based on John F. Ehlers' PMA Slope and Its Prediction (2024)
// Optimized using vectorized array processing

// Parameters
Length = Param("Length", 20, 5, 50, 1);

// Vectorized Linear Regression Slope calculation
// Create index array for regression
x = Cum(1);

// Calculate sums using vectorized operations
sx = Sum(x, Length);
sy = Sum(C, Length);
sxx = Sum(x * x, Length);
sxy = Sum(x * C, Length);

// Calculate slope using linear regression formula
denominator = Length * sxx - sx * sx;
Slope_val = (Length * sxy - sx * sy) / denominator;

// Simple Moving Average calculation
SMA_val = MA(C, Length);

// PMA Calculation (Predicted Moving Average)
PMA = SMA_val + Slope_val * Length / 2;

// Predict Calculation using Ehlers' method
// Using Ref() to get previous values efficiently
Predict = 1.5 * Slope_val - 0.5 * Ref(Slope_val, -4);

// Handle initial bars where Ref(-4) would be invalid
Predict = IIf(BarIndex() < 4, Null, Predict);

// Clean up any invalid values (division by zero, etc.)
Slope_val = IIf(denominator == 0, Null, Slope_val);
PMA = IIf(IsNull(Slope_val), Null, PMA);

// Plot the oscillator
Plot(Slope_val, "Slope", colorBlue, styleLine | styleThick);
Plot(0, "Zero Line", colorGrey40, styleDashed);
Plot(Predict, "Predict", colorRed, styleLine | styleThick);

// Optional: Add PMA and Close for reference (uncomment if needed)
// Plot(PMA, "PMA(" + Length + ")", colorYellow, styleLine);
// Plot(Close, "Close", colorBlack, styleCandle);

// Buy/Sell signals based on prediction vs slope
Buy = Cross(Predict, Slope_val);
Sell = Cross(Slope_val, Predict);

// Alternative signals based on zero crossings (uncomment if preferred)
// Buy = Cross(Predict, 0);
// Sell = Cross(0, Predict);

// Plot buy/sell arrows
PlotShapes(IIf(Buy, shapeSquare, shapeNone), colorGreen, 0, L, Offset = -40);
PlotShapes(IIf(Sell, shapeSquare, shapeNone), colorRed, 0, H, Offset = 40);

// Background ribbon
Ribbon_kol = IIf(Buy, colorAqua, IIf(Sell, colorPink, colorWhite));
Plot(100, "ribbon", Ribbon_kol, styleOwnScale | styleArea | styleNoLabel, -0.5, 100);

// Title with current values
Title = "PMA Slope Oscillator (Vectorized) - Length: " + Length +
"\nSlope: " + WriteVal(Slope_val, 1.4) +
" | Predict: " + WriteVal(Predict, 1.4) +
"\nPMA: " + WriteVal(PMA, 1.2);

// Optional: Add alert conditions
AlertIf(Buy, "SOUND C:\Windows\Media\ding.wav", "PMA Slope Buy Signal", 1);
AlertIf(Sell, "SOUND C:\Windows\Media\ding.wav", "PMA Slope Sell Signal", 2);Code button (use to enter/paste AFL formula)`
1 Like