Indicator · Pine Script v5 · Own pane

BTC/ETH Pairs Trading Z-Score & RSI/MACD

by Maderana· code credited to YourTradingName

This indicator, drawn in a separate pane, pulls Bitcoin and Ethereum prices from two user-chosen symbols, forms the BTC to ETH price ratio and converts it to a z-score against a rolling average and standard deviation (30 bars by default).

View on GitHub

GitHub stars1on the repository
Pine versionv5runs as is
Lines of code141
Settings15inputs
LicenceNone statedfile: MPL-2.0
Last update11 months ago17 Nov 2025

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

141 lines of Pine Script v5 from indicators/pair-trading/BTC-ETH_pair_trading/BTC-ETH_pair_trading_Z-score_indicator.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

BTC-ETH_pair_trading_Z-score_indicator.pine
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © YourTradingName

//@version=5
indicator("BTC/ETH Pairs Trading Z-Score & RSI/MACD", overlay=false, shorttitle="BTC/ETH Correl") // Updated shorttitle for clarity

// Input Parameters
int myzScoreLookback = input.int(30, "Z-Score Lookback Period", minval=1)
float upperThreshold = input.float(2.6, "Upper Z-Score Threshold (Sell)", step=0.1)
float lowerThreshold = input.float(-2.6, "Lower Z-Score Threshold (Buy)", step=0.1)
float signalOffset = input.float(0.1, "Signal Plot Offset", step=0.01)
string btcSymbol = input.symbol("COINBASE:BTCUSD", "Bitcoin Symbol")
string ethSymbol = input.symbol("COINBASE:ETHUSD", "Ethereum Symbol")

// --- RSI Input Parameters ---
int rsiLength = input.int(14, "RSI Lookback Period", minval=1)
float rsiUpper = input.float(70, "RSI Overbought Level", minval=0, maxval=100)
float rsiLower = input.float(30, "RSI Oversold Level", minval=0, maxval=100)

// --- MACD Input Parameters ---
int macdFastLength = input.int(12, "MACD Fast Length", minval=1)
int macdSlowLength = input.int(26, "MACD Slow Length", minval=1)
int macdSignalLength = input.int(9, "MACD Signal Length", minval=1)

float btcLotSize = input.float(1.0, "BTC Lot Size", minval=0.01, step=0.01)
float btcSpreadPerLot = input.float(60.0, "BTC Spread per Lot ($)", minval=0, step=0.1)
float ethSpreadPerLot = input.float(4.9, "ETH Spread per Lot ($)", minval=0, step=0.1)


// Fetch BTC and ETH prices using request.security
float btcPrice = request.security(btcSymbol, timeframe.period, close)
float ethPrice = request.security(ethSymbol, timeframe.period, close)

float ethLotSize = (btcLotSize * (btcPrice + btcSpreadPerLot)) / (ethPrice - ethSpreadPerLot)

// log.info("Bar: " + str.tostring(time) + " - BTC: " + str.tostring(btcPrice) + ", ETH: " + str.tostring(ethPrice))

// Calculate the BTC/ETH price ratio
float priceRatio = btcPrice / ethPrice

// --- Plot Price Ratio (still useful for context) ---
// log.info("priceratio: "+ str.tostring(priceRatio))
// log.info("Bar: " + str.tostring(time) + " - priceRatio: " + str.tostring(priceRatio))


plot(priceRatio, title="BTC/ETH Ratio", color=color.rgb(255, 255, 255))

// Calculate moving average and standard deviation of the price ratio
float maRatio = ta.sma(priceRatio, myzScoreLookback)
// plot(maRatio, title="MA Ratio", color=color.rgb(56, 42, 42))
float stdevRatio = ta.stdev(priceRatio, myzScoreLookback)
// log.info("Bar: " + str.tostring(time) + " - stdevRatio: " + str.tostring(stdevRatio)) // Log its value on every bar
plot(stdevRatio, title="StDev Ratio", color=color.rgb(255, 255, 255), linewidth = 4)

// --- NEW DEBUG LOGS FOR STDEV RATIO ---
// log.info("DEBUG - Bar: " + str.tostring(time) + " - stdevRatio (before Z-Score check): " + str.tostring(stdevRatio))
// log.info("DEBUG - Bar: " + str.tostring(time) + " - (stdevRatio != 0) condition result: " + str.tostring(stdevRatio != 0))

// Calculate Z-Score
float myzScore = (priceRatio - maRatio) / (stdevRatio+0.00000012345)
// if stdevRatio!= 0
//     myzScore = (priceRatio - maRatio) / stdevRatio
//     // zScore = 3.0
//     log.info("Zscore is coming out to be: " + str.tostring(myzScore))
// else
//     log.warning("stdevRatio is coming out zero")

// --- NEW DEBUG LOG FOR FINAL Z-SCORE VALUE ---
// log.info("DEBUG - Bar: " + str.tostring(time) + " - zScore (final value for bar): " + str.tostring(myzScore))


// --- Plot Z-Score and thresholds ---


// plot(zScore, title="Z-Score", color=color.rgb(255, 255, 255), linewidth=8) // Reduced linewidth back to normal
plot(myzScore, title="Z-Score", color=color.fuchsia, linewidth=4) // Reduced linewidth back to normal

hline(upperThreshold, "Upper Threshold", color=color.red, linestyle=hline.style_dashed, linewidth=1)
hline(lowerThreshold, "Lower Threshold", color=color.green, linestyle=hline.style_dashed, linewidth=1)
hline(0, "Zero Line (Mean)", color=color.gray, linestyle=hline.style_dotted, linewidth=1)


// --- RSI Calculations ---
float rsiValue = ta.rsi(priceRatio, rsiLength) // RSI applied to the priceRatio
// plot(rsiValue, title="RSI", color=color.orange, linewidth=1)
hline(rsiUpper, "RSI Overbought", color=color.red, linestyle=hline.style_dotted, linewidth=1)
hline(rsiLower, "RSI Oversold", color=color.green, linestyle=hline.style_dotted, linewidth=1)


// --- MACD Calculations ---
// MACD components: macdLine, signalLine, hist
// Source for MACD is also the priceRatio
[macdLine, signalLine, hist] = ta.macd(priceRatio, macdFastLength, macdSlowLength, macdSignalLength)

// Plot MACD components
plot(macdLine, title="MACD Line", color=color.purple, linewidth=1)
plot(signalLine, title="Signal Line", color=color.fuchsia, linewidth=1)
plot(hist, title="MACD Histogram", color=(hist >= 0 ? color.new(color.teal, 50) : color.new(color.red, 50)), style=plot.style_columns, linewidth=1)
hline(0, "MACD Zero Line", color=color.gray, linestyle=hline.style_dotted, linewidth=1)


// Generate and plot buy/sell signals (Z-Score based, can be expanded with RSI/MACD)
// Current logic is only based on Z-score. You can add RSI/MACD conditions here if desired.
bool buySignal = ta.crossunder(myzScore, lowerThreshold)
bool sellSignal = ta.crossover(myzScore, upperThreshold)

// Calculate Predicted Profit at Signal Time
float buyPredictedProfitPct = (maRatio - priceRatio) / priceRatio * 100
float sellPredictedProfitPct = (priceRatio - maRatio) / priceRatio * 100


// Plot shapes for signals. location.absolute is used to plot at the Z-score value.
// The signalOffset helps ensure the arrows are clearly visible.
plotshape(buySignal ? myzScore - signalOffset : na, title="Buy Signal", style=shape.triangleup, location=location.absolute, color=color.new(color.green, 0), size=size.small, text="BUY BTC", textcolor=color.green)
plotshape(sellSignal ? myzScore + signalOffset : na, title="Sell Signal", style=shape.triangledown, location=location.absolute, color=color.new(color.red, 0), size=size.small, text="SELL BTC", textcolor=color.red)


// --- NEW: Plot Predicted Profit Percentage as a Histogram ---
var float predictedProfitHistogramValue = na // Initialize with na

if buySignal // Only assign value if a buy signal is strictly triggered
    predictedProfitHistogramValue := buyPredictedProfitPct
else if sellSignal // Only assign value if a sell signal is strictly triggered
    predictedProfitHistogramValue := sellPredictedProfitPct
else
    predictedProfitHistogramValue := na // Otherwise, no histogram bar

// Plot as columns (similar to histogram)
plot(predictedProfitHistogramValue, title="Predicted Profit % (at Signal)", color=color.new(color.fuchsia, 0), linewidth=2, style=plot.style_columns, display=display.pane)



// --- Lot Size Table ---
var table lotTable = table.new(position.top_right, 1, 2, bgcolor=color.new(color.blue, 90), border_color=color.blue, border_width=1)
table.cell(lotTable, 0, 0, "BTC Lots: " + str.tostring(btcLotSize), text_color=color.white)
table.cell(lotTable, 0, 1, "ETH Lots: " + str.tostring(ethLotSize), text_color=color.white)

// --- Alert Conditions ---
alertcondition(buySignal, title="Buy Signal Alert", message="Z-Score triggered Buy Signal on BTC/ETH Ratio!: Buy BTC / Sell ETH!")
alertcondition(sellSignal, title="Sell Signal Alert", message="Z-Score triggered Sell Signal on BTC/ETH Ratio!: Sell BTC / Buy ETH! ")
// You can add more alertconditions here based on RSI/MACD if you integrate them into the signal logic.

Overview

Upper and lower thresholds mark stretched readings as sell or buy signals. RSI and MACD settings are provided as extra confirmation inputs. Lot size and per-lot spread settings estimate a matching ETH position size for the pair trade. It plots signals only and places no orders.

Details

Published by

Maderana on GitHub

Code credit

YourTradingName, as written in the script's header

Type

Indicator, Pine Script v5

Draws

Separate pane below the chart

Category

Mean reversion, Oscillators & momentum

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
Z-Score Lookback Periodint30myzScoreLookback
Upper Z-Score Threshold (Sell)float2.6upperThreshold
Lower Z-Score Threshold (Buy)float-2.6lowerThreshold
Signal Plot Offsetfloat0.1signalOffset
Bitcoin SymbolsymbolCOINBASE:BTCUSDbtcSymbol
Ethereum SymbolsymbolCOINBASE:ETHUSDethSymbol
RSI Lookback Periodint14rsiLength
RSI Overbought Levelfloat70rsiUpper
RSI Oversold Levelfloat30rsiLower
MACD Fast Lengthint12macdFastLength
MACD Slow Lengthint26macdSlowLength
MACD Signal Lengthint9macdSignalLength
Show all 15 settings
SettingTypeDefaultVariable
BTC Lot Sizefloat1.0btcLotSize
BTC Spread per Lot ($)float60.0btcSpreadPerLot
ETH Spread per Lot ($)float4.9ethSpreadPerLot
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 calculates normalized percentage bounds around price highs and lows to anticipate local trend reversals. It oscillates within a fixed -1 to 1 channel and plots two boundary tracking curves alongside a background Karobein oscillator histogram for

Indicator v4 Mean reversionOscillators & momentum Repaints
Updated 1 year ago · None stated GitHub repository of Mean Reversion Indicator
99 stars

This indicator calculates the statistical autocorrelation coefficient of an input series over a user-defined lookback window at a specified lag. It tracks values using an internal circular buffer, computes sample mean, variance, and autocovariance, and normali

Indicator v6 TrendMean reversion
Updated 4 weeks ago · Apache-2.0 GitHub repository of Autocorrelation Function (ACF)

Pretty Good Oscillator

by mihakralj
47 stars

The Pretty Good Oscillator calculates the distance between the selected price source and its simple moving average, normalized by the Average True Range over a specified period. It renders as an oscillator line in a separate pane alongside a zero line and thre

Indicator v6 Oscillators & momentumMean reversion
Updated 8 months ago · MIT GitHub repository of Pretty Good Oscillator

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