Understanding market extremes and shifts in sentiment is crucial for short-term traders and positional players alike. Today, I’m sharing an advanced Amibroker AFL script built using Lowess Smoothing logic that helps:
- Spot extreme price zones (Overbought / Oversold)
- Detect transitions like “Falling from Overbought” or “Rising from Oversold”
- Visualize short-to-mid-term sentiment through trend shifts and RSI momentum
- Present real-time insight using a GFX Table Dashboard

Core Concept: What is Lowess?
LOWESS (Locally Weighted Scatterplot Smoothing) is a powerful technique for filtering noisy price data and understanding smoothed directional bias. By wrapping it with ATR bands, we can:
- Define overbought/oversold thresholds
- Capture the underlying trend without lag from moving averages
- Add contextual overlays like momentum and RSI smoothing
Key Features of the AFL
1. Lowess Channel with ATR Bands
- A regression-smoothed line plots the central price trend.
- Two outer bands (
BandUpper2
,BandLower2
) derived using ATR define dynamic price envelopes.
2. Extreme Price Behavior Detector
The logic detects:
- When price enters extreme zones
- When it falls from Overbought
- When it rises from Oversold
- Or simply remains within a normal range
Each condition is color-coded:
Behavior | Color |
---|---|
Entered Overbought | Orange |
Entered Oversold | Bright Green |
Falling from Overbought | Pink |
Rising from Oversold | Aqua |
Still Overbought | Red |
Still Oversold | Green |
Normal | Grey |
3. Trend Reversal and Momentum Detector
- Detects slope change in the center line using a 3-bar reference
- Plots hollow star diamonds when trend color shifts
- Classifies trend states:
- Uptrend Emerging, Downtrend Emerging, Trend Reversal, Stable Trend
4. Sentiment via HMA of RSI
- Uses a double-smoothed RSI to eliminate false whipsaws
- Highlights:
- Bullish Bias (rising RSI)
- Bearish Bias (falling RSI)
- Neutral Bias (flattening RSI)
GFX Table: Your Real-Time Trading Dashboard
The script renders a floating GFX Table on the chart with live sentiment analysis:
Market Insight | Observation |
---|---|
Extreme Price Behavior | e.g., “Rising from Oversold” |
Trend Reversal / Momentum | e.g., “Uptrend Emerging” |
Short-to-Mid Term Sentiment | e.g., “Bullish Bias” |
This makes it incredibly intuitive for discretionary or systematic traders to make decisions at a glance.
AFL Source Code
//Rajandran R - Creator of OpenAlgo
//website - openalgo.in / marketcalls.in
//Lowess Channel with Trend Sentiment Detector
//Date - 29/03/2025
_SECTION_BEGIN("Lowess Channel");
/* === Chart Settings === */
SetChartOptions(0, chartShowArrows | chartShowDates);
_N(Title = StrFormat("{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}",
O, H, L, C, SelectedValue(ROC(C, 1))));
/* === User Inputs === */
length = Param("Lowess Length", 20, 5, 100, 1);
multi = Param("Bands Multiplier", 1.5, 0.1, 10, 0.1);
rsilength = Param("Hull of RSI Length",7,1,100,1);
atrlength = Param("ATR Length",100,1,200,1);
bandwidth = 10;
/* === ATR Calculation === */
iatr = ATR(atrlength);
/* === LOWESS Smoothing Function === */
function LowessSmooth(src, len, bw) {
result = Null;
for (i = len; i < BarCount; i++) {
sumW = sumWY = sumWXY = sumWX = sumWX2 = 0;
for (j = 0; j < len; j++) {
x = j;
idx = i - j;
weight = exp(-0.5 * (x / bw) * (x / bw));
y = Nz(src[idx], 0);
sumW += weight;
sumWX += weight * x;
sumWY += weight * y;
sumWXY += weight * x * y;
sumWX2 += weight * x * x;
}
meanX = sumWX / sumW;
meanY = sumWY / sumW;
beta = (sumWXY - meanX * meanY * sumW) / (sumWX2 - meanX * meanX * sumW);
alpha = meanY - beta * meanX;
result[i] = alpha + beta * (len / 2);
}
return result;
}
/* === Band Calculation === */
centerLine = LowessSmooth(C + (iatr * 0), length, bandwidth);
bandUpper1 = LowessSmooth(C + (iatr * multi), length, bandwidth);
bandUpper2 = LowessSmooth(C + (iatr * multi * 2), length, bandwidth);
bandLower1 = LowessSmooth(C - (iatr * multi), length, bandwidth);
bandLower2 = LowessSmooth(C - (iatr * multi * 2), length, bandwidth);
/* === Color Detection Logic === */
trendColor = IIf(Ref(centerLine, -3) < centerLine, colorLime,
IIf(Ref(centerLine, -3) > centerLine, colorViolet, colorGrey50));
/* === Plot Bands === */
Plot(bandUpper2, "Upper Band 2", colorYellow, styleThick);
Plot(bandUpper1, "Upper Band 1", colorDarkGreen, styleLine);
Plot(centerLine, "Lowess Center", trendColor, styleThick);
Plot(bandLower1, "Lower Band 1", colorDarkGreen, styleLine);
Plot(bandLower2, "Lower Band 2", colorYellow, styleThick);
/* === Trend Color Change Detection and Diamond Plot === */
trendChange = trendColor != Ref(trendColor, -1);
shapeY = Ref(centerLine, -1); // Diamond at previous bar’s centerLine
PlotShapes(trendChange * shapeHollowStar, trendColor, 0, shapeY, 0);
candlecolor = IIf(high>bandUpper2 AND Close>bandUpper2, colorRed, IIf(low<bandLower2,colorGreen,colorDefault));
Plot(Close, "Close", candlecolor, styleNoTitle | ParamStyle("Style") | GetPriceStyle());
_SECTION_END();
////////////////////////////////////////////////////////////////////////////////
// This AFL code enhances the GFX table by adding dynamic background colors
// to the observation values based on Lowess Channel logic.
////////////////////////////////////////////////////////////////////////////////
_SECTION_BEGIN("GFX Table - Lowess Channel Summary (Colored)");
cellHeight = 34;
cellWidth = 220;
startX = 50;
startY = 70;
tableFillColor = colorBlack;
tableBorderColor = colorBlue;
function TableCell(text, col, row, bgColor, textColor)
{
x1 = startX + (col - 1) * cellWidth;
y1 = startY + (row - 1) * cellHeight;
x2 = x1 + cellWidth;
y2 = y1 + cellHeight;
textX = x1 + 10;
textY = y1 + 10;
GfxSelectPen(tableBorderColor, 1);
GfxSelectSolidBrush(SelectedValue(bgColor));
GfxRectangle(x1, y1, x2, y2);
GfxSetBkColor(SelectedValue(bgColor));
GfxSetTextAlign(0);
GfxSetTextColor(textColor);
GfxTextOut(text, textX, textY);
}
// === Logic Derived from Lowess Channel ===
extremeUp = High > bandUpper2 AND Close >bandUpper2;
extremeDn = Low < bandLower2;
prevExtremeUp = Ref(extremeUp, -1);
prevExtremeDn = Ref(extremeDn, -1);
// Extended Extreme Price Behavior
extremeStatus =
WriteIf(extremeUp AND !prevExtremeUp, "Just Entered Overbought Zone",
WriteIf(extremeDn AND !prevExtremeDn, "Just Entered Oversold Zone",
WriteIf(!extremeUp AND prevExtremeUp AND Close < Ref(Close,-1), "Falling from Overbought",
WriteIf(!extremeDn AND prevExtremeDn AND Close > Ref(Close,-1), "Rising from Oversold",
WriteIf(extremeUp, "Above Upper Band (Overbought)",
WriteIf(extremeDn, "Below Lower Band (Oversold)", "Normal Range"))))));
// Dynamic Cell Color Coding
extremeColor =
IIf(extremeUp AND !prevExtremeUp, colorOrange,
IIf(extremeDn AND !prevExtremeDn, colorBrightGreen,
IIf(!extremeUp AND prevExtremeUp, colorPink,
IIf(!extremeDn AND prevExtremeDn, colorAqua,
IIf(extremeUp, colorRed,
IIf(extremeDn, colorGreen, colorDarkGrey))))));
// Trend Detection and Reversal
rising = Ref(centerLine, -3) < centerLine;
falling = Ref(centerLine, -3) > centerLine;
trendReversal = trendColor != Ref(trendColor, -1);
trendShift = WriteIf(rising AND !falling, "Uptrend Emerging",
WriteIf(falling AND !rising, "Downtrend Emerging",
WriteIf(trendReversal, "Trend Reversal", "Stable Trend")));
trendColorCell = IIf(trendReversal, colorOrange,
IIf(rising, colorLime,
IIf(falling, colorViolet, colorDarkGrey)));
hrsi = HMA(RSI(rsilength),rsilength);
hrsi = 2*hrsi-50;
rise = hrsi >= Ref(hrsi,-1);
fall = hrsi < Ref(hrsi,-1);
// Sentiment View
sentiment = WriteIf(rise, "Bullish Bias",
WriteIf(fall, "Bearish Bias", "Sideways/Neutral"));
sentimentColor = IIf(sentiment == "Bullish Bias", colorDarkGreen,
IIf(sentiment == "Bearish Bias", colorRed, colorGrey50));
// === Table Headers ===
TableCell("Market Insight", 1, 1, colorBlue, colorWhite);
TableCell("Current Observation", 2, 1, colorBlue, colorWhite);
// === Row 1 - Price Behavior
TableCell("Extreme Price Behavior", 1, 2, colorGrey40, colorWhite);
TableCell(extremeStatus, 2, 2, extremeColor, colorWhite);
// === Row 2 - Trend Reversal
TableCell("Trend Reversal / Momentum Shift", 1, 3, colorGrey40, colorWhite);
TableCell(trendShift, 2, 3, trendColorCell, colorWhite);
// === Row 3 - Sentiment
TableCell("Short-to-Mid Term Sentiment", 1, 4, colorGrey40, colorWhite);
TableCell(sentiment, 2, 4, sentimentColor, colorWhite);
_SECTION_END();
If you’re interested in customizing it for intraday timeframes, multi-timeframe overlays, or adding trading signals — feel free to request via comment.
Applications
- Reversal Zone Trading (mean-reversion)
- Trend Following with Confirmation
- Swing Trading Filters
- Sentiment-Driven Dashboards
Download & Usage
- Import the AFL into Amibroker
- Apply it on higher timeframes above hourly charts with reasonable amount of historical data.
- Adjust parameters like:
- Lowess Length, ATR Period, RSI Hull Length
- Hover your select the candles over the charts to read the GFX summary live
Final Thoughts
This strategy is part of a broader initiative to build visual, informative dashboards for discretionary traders in AmiBroker. It’s clean, informative, and easy to extend into trade signals or automation.
Happy Trading!
Is it possible to add trading signals for swing trades along with backtesting parameters
will try