OPEN-SOURCE SCRIPT
Ornstein-Uhlenbeck Mean Reversion Probability Bands [UAlgo]

Ornstein-Uhlenbeck Mean Reversion Probability Bands is a statistical mean reversion indicator that models price as a mean reverting process and projects dynamic probability style zones around an estimated equilibrium mean. The script uses a rolling lookback of closing prices, fits an Ornstein-Uhlenbeck inspired parameter set from recent behavior, and then converts that estimate into inner and outer deviation bands around the current mean.
The indicator runs directly on price (overlay=true) and is built to help traders identify when price is stretched away from its estimated equilibrium. Instead of using a fixed moving average and static standard deviation, the script attempts to infer a mean reverting structure from the data itself. It estimates the long term mean, the speed of reversion, and an equilibrium style dispersion measure, then plots two upside and two downside mean reversion zones.
When price pushes into the upper or lower band regions, the script calculates a standardized distance from the estimated mean and displays a probability style label with both the percentage score and the current z score. This gives the user a quick visual read of how statistically extended price is relative to the model.
A key strength of this script is that it combines:
A rolling Ornstein-Uhlenbeck style parameter estimation
Adaptive mean reversion zones
Probability style stretch labels at band events
A clean overlay presentation with visible upper and lower probability regions
Important note: The percentage label in this script is a normal distribution coverage style score derived from the current z score. It is best understood as a probabilistic stretch measure, not a literal exact OU first passage probability.
🔹 Features
🔸 1) Ornstein-Uhlenbeck Inspired Mean Reversion Model
The script estimates a mean reverting process from recent closing prices instead of relying only on a moving average. It uses a rolling regression style approach on consecutive price observations, then converts those estimates into Ornstein-Uhlenbeck style parameters.
This makes the indicator more model driven than a standard band tool.
🔸 2) Rolling Adaptive Mean Line
The central mean line is not a fixed average only. It is the estimated equilibrium level (mu) of the fitted process. As the rolling price sample changes, the model updates and the mean shifts with changing market structure.
The mean line also changes color depending on whether current price is above or below that estimated equilibrium.
🔸 3) Dual Mean Reversion Zones (Inner and Outer)
The script builds two sets of reversion bands around the mean:
Inner bands using the inner multiplier
Outer bands using the outer multiplier
This creates a layered framework where the inner zone marks an early stretch area and the outer zone marks a more extreme statistical extension.
🔸 4) Probability Style Stretch Labels
When price crosses into the upper or lower band regions, the script calculates a z score based on current distance from the estimated mean and converts it into a percentage style probability score.
The label shows:
A directional marker
The probability style percentage
The current z score
This gives the user both a visual event trigger and a numeric measure of extension.
🔸 5) Visual Zone Based Design
The indicator uses filled upper and lower zones rather than emphasizing the band lines themselves. This creates a cleaner chart display where the mean line stays visible and the stretch regions are highlighted as colored areas above and below it.
This makes the indicator easy to read during fast chart scanning.
🔸 6) Configurable Lookback, Time Step, and Band Width
Users can customize:
The rolling lookback period used for model estimation
The time step parameter (dt) used in OU conversion
The inner band multiplier
The outer band multiplier
This makes the script adaptable to different timeframes, instruments, and preferred sensitivity levels.
🔸 7) Built In Estimation Safeguards
The parameter estimation logic includes fallback protections. If the inferred model parameters are unstable or unrealistic, the script falls back to simpler sample statistics. This helps prevent unusable outputs during difficult market regimes or low quality fits.
🔸 8) Directional Touch Event Logic
The script tracks both upper side and lower side band interaction:
Upper side events can signal statistically stretched bullish price movement
Lower side events can signal statistically stretched bearish price movement
Labels are only created on crossing events, which helps reduce repeated prints while price remains outside the band.
🔹 Calculations
1) Rolling Price Queue Management
The script stores recent closing prices in an array with a fixed maximum length:
Pine Script®
The queue update method behaves differently depending on bar state:
On a new bar, it pushes the latest value
On an updating live bar, it overwrites the last stored value
This keeps the rolling sample aligned with the current chart state without duplicating the active bar.
2) Fallback Mean and Dispersion Estimates
Before attempting the OU style fit, the script calculates simple fallback values:
Pine Script®
These act as safety defaults if the regression based OU estimate is not reliable.
Important note:
In this script, fallback_sigma is a simple sample standard deviation of price levels, not return volatility.
3) AR(1) Style Regression on Consecutive Prices
The model estimation is built from consecutive price pairs:
x = price[t]
y = price[t+1]
The script computes:
Mean of x
Mean of y
Covariance between x and y
Variance of x
Then it estimates:
Pine Script®
This creates an AR(1) style coefficient that is later translated into OU style parameters.
4) Conversion from AR(1) Form to OU Style Parameters
If the estimated b is within a valid range:
Pine Script®
the script computes:
Pine Script®
Interpretation:
mu_exact is the estimated long run mean.
theta_exact is the implied mean reversion speed.
The conversion assumes the AR(1) relation is a discrete time representation of a mean reverting process.
5) Residual Variance and Equilibrium Dispersion
The script next measures residual error from the AR(1) fit:
Pine Script®
Then it converts that residual variance into an equilibrium variance estimate:
Pine Script®
Finally:
Pine Script®
Important implementation note:
The variable named sigma in this script is used as an equilibrium style standard deviation around the mean, not as the continuous time OU diffusion coefficient from the SDE form.
6) Stability Filter for the Estimated Sigma
Even if the AR(1) fit is mathematically valid, the script only accepts the calculated sigma when it is reasonably close to the fallback sample standard deviation:
Pine Script®
If this test fails, the script keeps the fallback values instead.
This helps avoid unstable band widths caused by bad short term fits.
7) Final Parameter Output
The estimation method returns:
Pine Script®
Where:
theta is the estimated reversion speed
mu is the estimated equilibrium mean
sigma_eq is the accepted equilibrium dispersion measure
These parameters are then used to build the bands.
8) Band Construction
The script computes four band levels around the estimated mean:
Pine Script®
Interpretation:
Inner bands represent a milder deviation from the mean.
Outer bands represent a more extreme deviation from the mean.
9) Mean and Zone Visualization
The mean line is explicitly plotted:
Pine Script®
The inner and outer band plots are also created, but their colors are fully transparent:
Pine Script®
This means the visible structure mainly comes from the zone fills:
Pine Script®
So the user sees clean upper and lower probability zones rather than several bright boundary lines.
10) Touch and Crossing Logic
The script first checks whether price is currently inside a stretch area:
Pine Script®
Then it checks for fresh crossings:
Pine Script®
Labels are only created when price is touching the region and a fresh crossing occurs. This avoids creating labels on every bar that remains outside the band.
11) Z Score Calculation
When an event occurs, the script calculates the standardized distance from the mean:
Pine Script®
Interpretation:
A z score of 1 means price is one equilibrium standard deviation away from the estimated mean.
Higher values indicate a more statistically stretched condition.
12) Probability Style Score Calculation
The script converts the z score into a percentage style score using an approximation of the error function:
Pine Script®
Because erf(|z| / sqrt(2)) corresponds to the probability mass within plus or minus that z distance under a normal distribution, the output behaves like a confidence or coverage score.
Important note:
This is not a direct OU mean reversion probability in the strict stochastic process sense. It is a normal distribution style stretch score based on the current z distance.
13) Upper Event Label Logic
When price crosses into the upper band region:
Pine Script®
the script prints a bearish styled label above the bar:
Pine Script®
This reflects the idea that price is statistically extended above the mean and may be vulnerable to reversion.
14) Lower Event Label Logic
When price crosses into the lower band region:
Pine Script®
the script prints a bullish styled label below the bar:
Pine Script®
This reflects the idea that price is statistically extended below the mean and may be vulnerable to reversion.
15) Role of the Time Step Input
The dt_input parameter affects the conversion from the AR(1) coefficient into the OU reversion speed:
Pine Script®
A larger dt lowers the inferred theta for the same b.
A smaller dt raises the inferred theta for the same b.
The indicator runs directly on price (overlay=true) and is built to help traders identify when price is stretched away from its estimated equilibrium. Instead of using a fixed moving average and static standard deviation, the script attempts to infer a mean reverting structure from the data itself. It estimates the long term mean, the speed of reversion, and an equilibrium style dispersion measure, then plots two upside and two downside mean reversion zones.
When price pushes into the upper or lower band regions, the script calculates a standardized distance from the estimated mean and displays a probability style label with both the percentage score and the current z score. This gives the user a quick visual read of how statistically extended price is relative to the model.
A key strength of this script is that it combines:
A rolling Ornstein-Uhlenbeck style parameter estimation
Adaptive mean reversion zones
Probability style stretch labels at band events
A clean overlay presentation with visible upper and lower probability regions
Important note: The percentage label in this script is a normal distribution coverage style score derived from the current z score. It is best understood as a probabilistic stretch measure, not a literal exact OU first passage probability.
🔹 Features
🔸 1) Ornstein-Uhlenbeck Inspired Mean Reversion Model
The script estimates a mean reverting process from recent closing prices instead of relying only on a moving average. It uses a rolling regression style approach on consecutive price observations, then converts those estimates into Ornstein-Uhlenbeck style parameters.
This makes the indicator more model driven than a standard band tool.
🔸 2) Rolling Adaptive Mean Line
The central mean line is not a fixed average only. It is the estimated equilibrium level (mu) of the fitted process. As the rolling price sample changes, the model updates and the mean shifts with changing market structure.
The mean line also changes color depending on whether current price is above or below that estimated equilibrium.
🔸 3) Dual Mean Reversion Zones (Inner and Outer)
The script builds two sets of reversion bands around the mean:
Inner bands using the inner multiplier
Outer bands using the outer multiplier
This creates a layered framework where the inner zone marks an early stretch area and the outer zone marks a more extreme statistical extension.
🔸 4) Probability Style Stretch Labels
When price crosses into the upper or lower band regions, the script calculates a z score based on current distance from the estimated mean and converts it into a percentage style probability score.
The label shows:
A directional marker
The probability style percentage
The current z score
This gives the user both a visual event trigger and a numeric measure of extension.
🔸 5) Visual Zone Based Design
The indicator uses filled upper and lower zones rather than emphasizing the band lines themselves. This creates a cleaner chart display where the mean line stays visible and the stretch regions are highlighted as colored areas above and below it.
This makes the indicator easy to read during fast chart scanning.
🔸 6) Configurable Lookback, Time Step, and Band Width
Users can customize:
The rolling lookback period used for model estimation
The time step parameter (dt) used in OU conversion
The inner band multiplier
The outer band multiplier
This makes the script adaptable to different timeframes, instruments, and preferred sensitivity levels.
🔸 7) Built In Estimation Safeguards
The parameter estimation logic includes fallback protections. If the inferred model parameters are unstable or unrealistic, the script falls back to simpler sample statistics. This helps prevent unusable outputs during difficult market regimes or low quality fits.
🔸 8) Directional Touch Event Logic
The script tracks both upper side and lower side band interaction:
Upper side events can signal statistically stretched bullish price movement
Lower side events can signal statistically stretched bearish price movement
Labels are only created on crossing events, which helps reduce repeated prints while price remains outside the band.
🔹 Calculations
1) Rolling Price Queue Management
The script stores recent closing prices in an array with a fixed maximum length:
price_array.update_queue(close, length_input)
The queue update method behaves differently depending on bar state:
On a new bar, it pushes the latest value
On an updating live bar, it overwrites the last stored value
This keeps the rolling sample aligned with the current chart state without duplicating the active bar.
2) Fallback Mean and Dispersion Estimates
Before attempting the OU style fit, the script calculates simple fallback values:
float fallback_mu = src_array.avg()
float fallback_sigma = src_array.stdev()
These act as safety defaults if the regression based OU estimate is not reliable.
Important note:
In this script, fallback_sigma is a simple sample standard deviation of price levels, not return volatility.
3) AR(1) Style Regression on Consecutive Prices
The model estimation is built from consecutive price pairs:
x = price[t]
y = price[t+1]
The script computes:
Mean of x
Mean of y
Covariance between x and y
Variance of x
Then it estimates:
float b = sum_cov / sum_var_x
This creates an AR(1) style coefficient that is later translated into OU style parameters.
4) Conversion from AR(1) Form to OU Style Parameters
If the estimated b is within a valid range:
if b > 0.05 and b < 0.95
the script computes:
float a = mean_y - b * mean_x
float mu_exact = a / (1.0 - b)
float theta_exact = -math.log(b) / dt
Interpretation:
mu_exact is the estimated long run mean.
theta_exact is the implied mean reversion speed.
The conversion assumes the AR(1) relation is a discrete time representation of a mean reverting process.
5) Residual Variance and Equilibrium Dispersion
The script next measures residual error from the AR(1) fit:
float err = y_i - (a + b * x_i)
float var_err = sum_err_sq / (n - 1)
Then it converts that residual variance into an equilibrium variance estimate:
float var_eq = var_err / (1.0 - b * b)
Finally:
float calc_sigma = math.sqrt(var_eq)
Important implementation note:
The variable named sigma in this script is used as an equilibrium style standard deviation around the mean, not as the continuous time OU diffusion coefficient from the SDE form.
6) Stability Filter for the Estimated Sigma
Even if the AR(1) fit is mathematically valid, the script only accepts the calculated sigma when it is reasonably close to the fallback sample standard deviation:
if calc_sigma < fallback_sigma * 1.5 and calc_sigma > fallback_sigma * 0.5
If this test fails, the script keeps the fallback values instead.
This helps avoid unstable band widths caused by bad short term fits.
7) Final Parameter Output
The estimation method returns:
OU_Params.new(theta, mu, sigma_eq)
Where:
theta is the estimated reversion speed
mu is the estimated equilibrium mean
sigma_eq is the accepted equilibrium dispersion measure
These parameters are then used to build the bands.
8) Band Construction
The script computes four band levels around the estimated mean:
float up_out = mean_val + (dev_val * mult_outer)
float up_in = mean_val + (dev_val * mult_inner)
float dn_in = mean_val - (dev_val * mult_inner)
float dn_out = mean_val - (dev_val * mult_outer)
Interpretation:
Inner bands represent a milder deviation from the mean.
Outer bands represent a more extreme deviation from the mean.
9) Mean and Zone Visualization
The mean line is explicitly plotted:
p_mean = plot(ou_bands.mean, color=color_mean, linewidth=2, title="Mean")
The inner and outer band plots are also created, but their colors are fully transparent:
color color_inner_up = color.new(#ffb74d, 100)
color color_outer_up = color.new(#ef5350, 100)
...
This means the visible structure mainly comes from the zone fills:
fill(p_ui, p_uo, ...)
fill(p_li, p_lo, ...)
So the user sees clean upper and lower probability zones rather than several bright boundary lines.
10) Touch and Crossing Logic
The script first checks whether price is currently inside a stretch area:
bool touch_upper = close >= ou_bands.upper_inner
bool touch_lower = close <= ou_bands.lower_inner
Then it checks for fresh crossings:
bool cross_up_in = ta.crossover(close, ou_bands.upper_inner)
bool cross_up_out = ta.crossover(close, ou_bands.upper_outer)
bool cross_dn_in = ta.crossunder(close, ou_bands.lower_inner)
bool cross_dn_out = ta.crossunder(close, ou_bands.lower_outer)
Labels are only created when price is touching the region and a fresh crossing occurs. This avoids creating labels on every bar that remains outside the band.
11) Z Score Calculation
When an event occurs, the script calculates the standardized distance from the mean:
float current_z_score = dev_val != 0 ? math.abs(close - mean_val) / dev_val : 0.0
Interpretation:
A z score of 1 means price is one equilibrium standard deviation away from the estimated mean.
Higher values indicate a more statistically stretched condition.
12) Probability Style Score Calculation
The script converts the z score into a percentage style score using an approximation of the error function:
float x = math.abs(z_score) / math.sqrt(2.0)
...
float prob = erf_approx * 100.0
Because erf(|z| / sqrt(2)) corresponds to the probability mass within plus or minus that z distance under a normal distribution, the output behaves like a confidence or coverage score.
Important note:
This is not a direct OU mean reversion probability in the strict stochastic process sense. It is a normal distribution style stretch score based on the current z distance.
13) Upper Event Label Logic
When price crosses into the upper band region:
if (touch_upper and cross_up_in) or (touch_upper and cross_up_out)
the script prints a bearish styled label above the bar:
"▼ %" + str.tostring(probability, "#.##") + "\n(Z:" + str.tostring(current_z_score, "#.##") + ")"
This reflects the idea that price is statistically extended above the mean and may be vulnerable to reversion.
14) Lower Event Label Logic
When price crosses into the lower band region:
if (touch_lower and cross_dn_in) or (touch_lower and cross_dn_out)
the script prints a bullish styled label below the bar:
"▲ %" + str.tostring(probability, "#.##") + "\n(Z:" + str.tostring(current_z_score, "#.##") + ")"
This reflects the idea that price is statistically extended below the mean and may be vulnerable to reversion.
15) Role of the Time Step Input
The dt_input parameter affects the conversion from the AR(1) coefficient into the OU reversion speed:
float theta_exact = -math.log(b) / dt
A larger dt lowers the inferred theta for the same b.
A smaller dt raises the inferred theta for the same b.
Open-source script
In true TradingView spirit, the creator of this script has made it open-source, so that traders can review and verify its functionality. Kudos to the author! While you can use it for free, remember that republishing the code is subject to our House Rules.
Get exclusive indicators: pinescriptmarket.com
All content provided by UAlgo is for informational & educational purposes only. Past performance does not guarantee future results.
All content provided by UAlgo is for informational & educational purposes only. Past performance does not guarantee future results.
Disclaimer
The information and publications are not meant to be, and do not constitute, financial, investment, trading, or other types of advice or recommendations supplied or endorsed by TradingView. Read more in the Terms of Use.
Open-source script
In true TradingView spirit, the creator of this script has made it open-source, so that traders can review and verify its functionality. Kudos to the author! While you can use it for free, remember that republishing the code is subject to our House Rules.
Get exclusive indicators: pinescriptmarket.com
All content provided by UAlgo is for informational & educational purposes only. Past performance does not guarantee future results.
All content provided by UAlgo is for informational & educational purposes only. Past performance does not guarantee future results.
Disclaimer
The information and publications are not meant to be, and do not constitute, financial, investment, trading, or other types of advice or recommendations supplied or endorsed by TradingView. Read more in the Terms of Use.