Indicator · Pine Script v5 · Own pane

Pairs Trading Z-Score

by dcrown99· code credited to TradingView

An indicator in a separate pane that compares the chart symbol with a second symbol chosen in the settings.

View on GitHub

GitHub stars1on the repository
Pine versionv5runs as is
Lines of code92
Settings7inputs
LicenceNone statedfile: MPL-2.0
Last update4 months ago16 Jun 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

92 lines of Pine Script v5 from mt5/research/PairTrading_ZScore_TradingView.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

PairTrading_ZScore_TradingView.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TradingView

//@version=5
indicator("Pairs Trading Z-Score [Antigravity]", shorttitle="Z-Score Pair", overlay=false)

// =========================================================================
// Inputs
// =========================================================================
grp_setup = "=== Pair Setup ==="
sym2      = input.symbol("BINANCE:ETHUSDT", "Secondary Symbol (Denominator)", group=grp_setup, tooltip="Primary is chart symbol (Numerator). Ratio = Primary / Secondary")
use_log   = input.bool(true, "Use Log Ratio", group=grp_setup, tooltip="Use natural log of ratio for cointegration (Log(A/B) = Log(A) - Log(B))")

grp_z     = "=== Z-Score Parameters ==="
z_len     = input.int(20, "Z-Score Window", minval=2, group=grp_z)
entry_z   = input.float(2.5, "Entry Z-Score (±)", minval=0.1, step=0.1, group=grp_z, tooltip="Trigger entry when Z > +Entry or Z < -Entry")
exit_z    = input.float(0.0, "Exit Z-Score (±)", minval=0.0, step=0.1, group=grp_z, tooltip="Trigger exit (mean reversion) when |Z| crosses Exit")
stop_z    = input.float(3.0, "Stop Z-Score (±)", minval=0.1, step=0.1, group=grp_z, tooltip="Emergency stop when cointegration breaks completely")

grp_ui    = "=== UI Settings ==="
show_bg   = input.bool(true, "Highlight Entry Zones", group=grp_ui)

// =========================================================================
// Data Fetch & Calculations
// =========================================================================
// Fetch secondary symbol data
close2 = request.security(sym2, timeframe.period, close)

// Calculate Spread / Ratio
float spread = na
if not na(close) and not na(close2) and close2 > 0
    if use_log
        spread := math.log(close / close2)
    else
        spread := close / close2

// Calculate Z-Score
float sma_spread = ta.sma(spread, z_len)
float std_spread = ta.stdev(spread, z_len)
float z_score    = (spread - sma_spread) / std_spread

// =========================================================================
// Trading Logic & Signals
// =========================================================================
// LONG Trigger: Z-Score < -Entry (Ratio is abnormally cheap)
// -> Buy Primary, Sell Secondary
bool go_long  = ta.crossunder(z_score, -entry_z)
bool long_tp  = ta.crossover(z_score, -exit_z)
bool long_sl  = ta.crossunder(z_score, -stop_z)

// SHORT Trigger: Z-Score > +Entry (Ratio is abnormally expensive)
// -> Sell Primary, Buy Secondary
bool go_short = ta.crossover(z_score, entry_z)
bool short_tp = ta.crossunder(z_score, exit_z)
bool short_sl = ta.crossover(z_score, stop_z)

// =========================================================================
// Plotting
// =========================================================================
// Baseline & Thresholds
hline(0, "Mean", color=color.gray, linestyle=hline.style_solid)
hline(entry_z, "+Entry", color=color.red, linestyle=hline.style_dashed)
hline(-entry_z, "-Entry", color=color.green, linestyle=hline.style_dashed)
hline(exit_z, "+Exit", color=color.new(color.red, 50), linestyle=hline.style_dotted)
hline(-exit_z, "-Exit", color=color.new(color.green, 50), linestyle=hline.style_dotted)
hline(stop_z, "+Stop Loss", color=color.maroon, linestyle=hline.style_solid)
hline(-stop_z, "-Stop Loss", color=color.new(color.maroon, 0), linestyle=hline.style_solid)

// Z-Score Line
z_color = z_score > entry_z ? color.red : z_score < -entry_z ? color.green : color.orange
plot(z_score, "Z-Score", color=z_color, linewidth=2)

// Background Highlighting
bgcolor(show_bg and z_score > entry_z ? color.new(color.red, 90) : na, title="Short Zone")
bgcolor(show_bg and z_score < -entry_z ? color.new(color.green, 90) : na, title="Long Zone")

// Signal Markers on Indicator
plotshape(go_long, title="Long Signal", style=shape.triangleup, location=location.bottom, color=color.green, size=size.small)
plotshape(go_short, title="Short Signal", style=shape.triangledown, location=location.top, color=color.red, size=size.small)

// =========================================================================
// Alerts
// =========================================================================
// Aggregated conditions for any Entry
bool any_entry = go_long or go_short
bool any_exit  = long_tp or short_tp
bool any_stop  = long_sl or short_sl

alertcondition(go_long, title="Long Entry Alert", message="Pair Trade LONG Threshold Reached: {{ticker}} is under-valued vs secondary. Z-Score: {{plot_0}}")
alertcondition(go_short, title="Short Entry Alert", message="Pair Trade SHORT Threshold Reached: {{ticker}} is over-valued vs secondary. Z-Score: {{plot_0}}")
alertcondition(any_exit, title="Take Profit Alert", message="Pair Trade EXIT (Mean Reversion). Z-Score reverted to Exit Threshold.")
alertcondition(any_stop, title="Stop Loss Alert", message="Pair Trade STOP LOSS (Cointegration Break). Z-Score extreme deviation.")

Overview

Pairs Trading Z-Score takes the ratio of the two prices, optionally as a log ratio, and converts it into a rolling Z-score over a set window. Horizontal lines mark entry, exit and stop Z-score levels, and the background can highlight entry zones. It assumes both symbols trade on the same timeframe and does not place orders.

Details

Published by

dcrown99 on GitHub

Code credit

TradingView, as written in the script's header

Type

Indicator, Pine Script v5

Draws

Separate pane below the chart

Category

Mean reversion

Markets

Crypto

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Secondary Symbol (Denominator)symbolBINANCE:ETHUSDTsym2
Use Log Ratiobooltrueuse_log
Z-Score Windowint20z_len
Entry Z-Score (±)float2.5entry_z
Exit Z-Score (±)float0.0exit_z
Stop Z-Score (±)float3.0stop_z
Highlight Entry Zonesbooltrueshow_bg
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

261 stars

This indicator calculates standardized z-scores of price data across four different lookback periods (6, 14, 26, and 100). Standardizing data subtracts the moving average from the price and divides by the standard deviation. It applies a Double Exponential Mov

Indicator v5 Mean reversionVolatility & bands
114 stars

This indicator implements a dual-direction variation of the Williams Vix Fix oscillator in a separate pane. It normalizes price data using min-max scaling to compute both upward and downward synthetic volatility metrics across two selectable calculation modes

Indicator v4 Volatility & bandsMean reversion
Updated 1 year ago · None stated GitHub repository of Combo Williams Vix Fix (Twin)
99 stars

The Augmented Dickey-Fuller indicator calculates statistical stationarity and tests for unit roots in price series over a rolling window. It performs ordinary least squares regression on differenced historical price data to compute a t-statistic and an estimat

Indicator v6 Mean reversionTrend
Updated 4 weeks ago · Apache-2.0 GitHub repository of ADF - Augmented Dickey-Fuller Test
47 stars

This indicator applies the hyperbolic tangent to a chosen source series and plots the result in a separate pane. It centers the transform on a 200-bar simple moving average of that source, multiplies the deviation by a steepness factor, and maps the output int

Indicator v6 Moving averagesMean reversion
Updated 8 months ago · MIT GitHub repository of Hyperbolic Tangent (TANH)

AutoCorrelation Function

by Anderson-ALGO
9 stars

This indicator calculates sample autocorrelation across multiple lag intervals for a selected price series. It computes correlation values over a user-specified lookback period using raw prices, log returns, absolute log returns, or squared log returns. Plotte

Indicator v4 TrendMean reversion
Updated 5 years ago · None stated GitHub repository of AutoCorrelation Function

Calculates a composite carry and decay oscillator fusing impulse-autocorrelation half-life, a Hurst exponent approximation, path efficiency, and range expansion ratios. It evaluates whether an impulse is likely to persist or mean-revert, plotting the oscillato

Indicator v6 TrendOscillators & momentum
Updated 2 days ago · MIT GitHub repository of Market Memory & Decay Oscillator [WavesUnchained]

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers