Pretty Good Oscillator: price distance in true-range units
The Pretty Good Oscillator (PGO) divides the distance between the current close and its simple moving average by a smoothed true range. A value of +2 means price is two smoothed ranges above that average. This guide uses EMA smoothing for the denominator, so its result differs from a version built with Pine Script's default RMA-based ATR.
PGO = (Close - SMA(Close, N)) / EMA(True Range, N)
Example: a close of 106, SMA of 100 and smoothed range of 2 give PGO = 3. This is a distance ratio, not a 3% return.
Specify the smoothing before comparing PGO values
Trading Technologies documents an EMA of true range in its PGO formula. TradingView's default ATR uses RMA. Both average the same range input, but with different weights: EMA uses alpha = 2 / (N + 1), while RMA uses alpha = 1 / N. Applying an EMA to ATR adds another smoothing stage and does not reproduce this page's formula.
TR = max(High - Low, |High - Previous Close|, |Low - Previous Close|)
For a positive denominator, the sign comes entirely from Close - SMA. Zero means the close equals its current SMA; positive means above, and negative means below. The oscillator has no fixed upper or lower bound and does not use volume.
A four-bar calculation you can reproduce
These are synthetic prices, not a backtest. Set N = 3, so EMA alpha is 0.5. Start the EMA at the first true range of 2; with no previous close, use High - Low on that first bar. The SMA requires three closes. Values below are rounded only for display.
| Bar | High | Low | Close | TR | EMA(TR, 3) | SMA(C, 3) | PGO |
|---|---|---|---|---|---|---|---|
| 1 | 101 | 99 | 100 | 2 | 2.0000 | Not ready | Not ready |
| 2 | 103 | 100 | 102 | 3 | 2.5000 | Not ready | Not ready |
| 3 | 105 | 102 | 104 | 3 | 2.7500 | 102.0000 | 0.7273 |
| 4 | 111 | 108 | 110 | 7 | 4.8750 | 105.3333 | 0.9573 |
On bar 4, the gap makes TR = max(3, 7, 4) = 7. The EMA becomes 0.5 × 7 + 0.5 × 2.75 = 4.875. The SMA is (102 + 104 + 110) / 3 = 105.3333, giving PGO = (110 - 105.3333) / 4.875 = 0.9573.
RMA seeded from the first three true ranges instead starts at 8/3 on bar 3 and becomes 37/9 on bar 4. Substituting that denominator gives 42/37 = 1.1351. The prices and length are identical; smoothing and initialization explain the difference.
Formula references: Trading Technologies PGO, TradingView ATR. The table is an independent arithmetic example.
Pretty Good Oscillator Pine Script v6
Paste this indicator into TradingView's Pine Editor and add it to a standard candle chart. It retains the 89-bar starting length from Pineify's PGO configuration. Change the length to 3 to follow the arithmetic above; actual chart data will have its own preceding history and EMA state.
//@version=6
indicator("Pretty Good Oscillator: EMA true range", shorttitle="PGO", overlay=false, precision=4)
length = input.int(89, "Length in bars", minval=2, maxval=500)
level = input.float(3.0, "Reference level", minval=0.1, step=0.1)
minRangeTicks = input.float(0.0, "Minimum denominator (ticks)", minval=0.0, step=0.1)
basis = ta.sma(close, length)
trueRange = ta.tr(true)
rangeEma = ta.ema(trueRange, length)
rangeFloor = minRangeTicks * syminfo.mintick
valid = not na(basis) and not na(rangeEma) and rangeEma > rangeFloor
pgo = valid ? (close - basis) / rangeEma : na
plot(pgo, "PGO (EMA true range)", color=color.blue, linewidth=2, style=plot.style_linebr)
hline(0, "Zero", color=color.gray)
hline(level, "Upper reference", color=color.red)
hline(-level, "Lower reference", color=color.red)
plot(basis, "SMA of close", display=display.data_window)
plot(rangeEma, "EMA of true range", display=display.data_window)
plot(trueRange, "True range", display=display.data_window)
crossUp = ta.crossover(pgo, level)
crossDown = ta.crossunder(pgo, -level)
confirmedUp = crossUp and barstate.isconfirmed
confirmedDown = crossDown and barstate.isconfirmed
plotshape(confirmedUp, "Confirmed upper cross", shape.triangleup, location.bottom, color.green, size=size.tiny)
plotshape(confirmedDown, "Confirmed lower cross", shape.triangledown, location.top, color.red, size=size.tiny)
alertcondition(confirmedUp, "PGO upper cross", "PGO crossed above its upper reference on a closed chart bar.")
alertcondition(confirmedDown, "PGO lower cross", "PGO crossed below its lower reference on a closed chart bar.")Inspect the SMA, true range and denominator in the Data Window. The script uses ta.tr(true), which falls back to High - Low when the previous close is unavailable. It leaves PGO missing until the SMA is ready and when the denominator fails the minimum-range check. It does not replace missing values with zero.
The live line can move before the candle closes. Crossing markers and alert conditions wait for barstate.isconfirmed; create an alert from the indicator if you want notifications. The script places no orders and reports no strategy performance.
Test continuation and reversion as separate hypotheses
A crossing above +3 and a return below +3 are different events. Remaining above +3 for ten bars is also different from ten new crossings. PGO describes displacement; a rule determines how you act on it. The following are test specifications, not trade recommendations or validated profitable systems.
| Hypothesis | Example long-side rule | Failure to inspect |
|---|---|---|
| Continuation | Enter after a confirmed cross above +3; exit after a confirmed cross below zero. | A gap can reverse immediately; a zero exit can return much of an open gain. |
| Mean reversion | Enter after a confirmed cross back above -3; exit after a confirmed cross above zero. | Price can resume its decline before reaching the average. |
Before testing, specify sizing, protective exits, maximum holding time, fill timing, commission and slippage. Freeze parameters before evaluating a later period. Keep symbol, timeframe, session and price adjustments consistent. A ratio removes price units; it does not equalize signal frequency, tail risk or profitability across instruments.
Limits that change the reading
- A near-zero smoothed range amplifies small deviations. For a hypothetical deviation of 0.10 and denominator of 0.01, PGO is 10. Check trading activity and tick size before treating the ratio as meaningful.
- A sustained trend can keep PGO beyond a reference level. A high reading does not specify when price must reverse.
- A volatility jump changes both the numerator and denominator. PGO may fall while price rises if the smoothed range expands faster than the price-to-SMA distance.
- Different history lengths, smoothing seeds, sessions and synthetic chart prices can produce mismatches. Match these inputs before comparing implementations.
Educational information only, not investment advice. No threshold guarantees a profitable trade.
PGO, Momentum, ROC and ATR answer different questions
PGO measures distance from a moving average in smoothed-range units. Momentum subtracts a past price from the current price; ROC expresses that endpoint change as a percentage. ATR measures range in price units without the signed price-to-average numerator. None is interchangeable merely because its line rises.
Frequently asked questions
Sources and verification
Formula and platform documentation reviewed October 17, 2026. The numerical examples are synthetic; they are not performance results.
Turn a specified PGO rule into code
Pineify's Pine Script AI Coding Agent can help create and edit a script from explicit rules. Include the EMA true-range denominator, length, closed-bar conditions and exits in your request. Ask for continuation and mean-reversion versions separately, then request a syntax check and compare their chart values before testing costs and fills in TradingView.
Code generation and syntax checking do not verify profitability or automatically execute broker trades.
Explore Pine Script AI Coding Agent