Strategy · Pine Script v6 · Own pane

Stochastic Divergence Strategy

by JoelPasapera

The Stochastic Divergence Strategy calculates the Stochastic oscillator (%K and %D) to identify regular bullish and bearish divergence patterns against price pivots.

View on GitHub

GitHub stars11on the repository
Pine versionv6current
Lines of code344
Settings31inputs
LicenceApache-2.0
Last update5 months ago23 May 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 strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

344 lines of Pine Script v6 from Stochastic Divergence Strategy/Stochastic Divergence Strategy.pine, published under Apache-2.0. Keep the author's credit and licence lines when you reuse it.

Stochastic Divergence Strategy.pine
//@version=6
strategy(title="Stochastic Divergence Strategy", shorttitle="Stoch", initial_capital = 10000, format=format.price, precision=2, max_lines_count=500, max_labels_count=500, overlay=false)

// lot size
int lot_size = input.int(1, title="Lot size", minval=1, group="Risk")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - Stochastic Parameters (unchanged from default)
// ═══════════════════════════════════════════════════════════════════════════════
periodK = input.int(14, title="%K Length", minval=1, group="Stochastic Settings")
smoothK = input.int(1, title="%K Smoothing", minval=1, group="Stochastic Settings")
periodD = input.int(3, title="%D Smoothing", minval=1, group="Stochastic Settings")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - Levels
// ═══════════════════════════════════════════════════════════════════════════════
obLevel = input.int(80, title="Overbought Level", minval=50, maxval=100, group="Levels")
osLevel = input.int(20, title="Oversold Level", minval=0, maxval=50, group="Levels")
midLevel = input.int(50, title="Middle Level", minval=0, maxval=100, group="Levels")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - Line Visibility
// ═══════════════════════════════════════════════════════════════════════════════
showK = input.bool(true, title="Show %K Line", group="Line Visibility")
showD = input.bool(true, title="Show %D Line", group="Line Visibility")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - Colors (shared for directional coloring)
// ═══════════════════════════════════════════════════════════════════════════════
colorUp = input.color(color.green, title="Rising Color", group="Colors")
colorDown = input.color(color.red, title="Falling Color", group="Colors")
colorSideways = input.color(color.white, title="Sideways Color", group="Colors")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - Alert Visuals
// ═══════════════════════════════════════════════════════════════════════════════
showAlerts = input.bool(true, title="Show OB/OS Alerts", group="Alert Visuals")
alertStyleOB = input.string("Circle", title="Overbought Style", options=["Circle", "Arrow Down", "Cross"], group="Alert Visuals")
alertStyleOS = input.string("Circle", title="Oversold Style", options=["Circle", "Arrow Up", "Cross"], group="Alert Visuals")
alertColorOB = input.color(color.red, title="Overbought Alert Color", group="Alert Visuals")
alertColorOS = input.color(color.green, title="Oversold Alert Color", group="Alert Visuals")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - Divergence Settings
// ═══════════════════════════════════════════════════════════════════════════════
showDivergence = input.bool(true, title="Show Divergences", group="Divergence Settings")
divTicker = input.symbol("", title="Ticker for Divergence", tooltip="Leave empty to use current chart ticker", group="Divergence Settings")
pivotLookbackLeft = input.int(5, title="Pivot Lookback Left", minval=1, group="Divergence Settings")
pivotLookbackRight = input.int(5, title="Pivot Lookback Right", minval=1, group="Divergence Settings")
maxBarsBack = input.int(100, title="Max Bars to Look Back", minval=10, maxval=500, group="Divergence Settings")
removeBrokenDiv = input.bool(true, title="Remove Broken Divergences", group="Divergence Settings")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - Divergence Colors
// ═══════════════════════════════════════════════════════════════════════════════
bullDivColor = input.color(#4caf50, title="Bullish Divergence Color", group="Divergence Colors")
bearDivColor = input.color(#f23645, title="Bearish Divergence Color", group="Divergence Colors")
divLineWidth = input.int(3, title="Divergence Line Width", minval=1, maxval=5, group="Divergence Colors")
showDivLabels = input.bool(true, title="Show Divergence Labels", group="Divergence Colors")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - STRATEGY SETTINGS (NUEVO)
// ═══════════════════════════════════════════════════════════════════════════════
grpStrategy = "Strategy Settings"
useSL = input.bool(true, "Use Stop Loss", group=grpStrategy)
slPerc = input.float(2.0, "Stop Loss (%)", minval=0.1, step=0.1, group=grpStrategy) / 100
useTP = input.bool(true, "Use Take Profit", group=grpStrategy)
tpPerc = input.float(4.0, "Take Profit (%)", minval=0.1, step=0.1, group=grpStrategy) / 100

// ═══════════════════════════════════════════════════════════════════════════════
// CALCULATIONS - Original Stochastic Formula (unchanged)
// ═══════════════════════════════════════════════════════════════════════════════
k = ta.sma(ta.stoch(close, high, low, periodK), smoothK)
d = ta.sma(k, periodD)

// ═══════════════════════════════════════════════════════════════════════════════
// GET PRICE DATA FROM SELECTED TICKER
// ═══════════════════════════════════════════════════════════════════════════════
tickerToUse = divTicker == "" ? syminfo.tickerid : divTicker
[tickerClose, tickerHigh, tickerLow] = request.security(tickerToUse, timeframe.period, [close, high, low])

// ═══════════════════════════════════════════════════════════════════════════════
// DIRECTIONAL COLOR LOGIC - %K
// ═══════════════════════════════════════════════════════════════════════════════
kDirection = k > k[1] ? 1 : k < k[1] ? -1 : 0
kColor = kDirection == 1 ? colorUp : kDirection == -1 ? colorDown : colorSideways

// ═══════════════════════════════════════════════════════════════════════════════
// DIRECTIONAL COLOR LOGIC - %D
// ═══════════════════════════════════════════════════════════════════════════════
dDirection = d > d[1] ? 1 : d < d[1] ? -1 : 0
dColor = dDirection == 1 ? colorUp : dDirection == -1 ? colorDown : colorSideways

// ═══════════════════════════════════════════════════════════════════════════════
// OVERBOUGHT / OVERSOLD DETECTION (based on %D)
// ═══════════════════════════════════════════════════════════════════════════════
crossedIntoOB = ta.crossover(d, obLevel)
crossedIntoOS = ta.crossunder(d, osLevel)

// ═══════════════════════════════════════════════════════════════════════════════
// PIVOT DETECTION ON PRICE ONLY (from selected ticker)
// ═══════════════════════════════════════════════════════════════════════════════
pricePivotLow = ta.pivotlow(tickerLow, pivotLookbackLeft, pivotLookbackRight)
pricePivotHigh = ta.pivothigh(tickerHigh, pivotLookbackLeft, pivotLookbackRight)

// ═══════════════════════════════════════════════════════════════════════════════
// ARRAYS TO STORE PIVOT DATA
// ═══════════════════════════════════════════════════════════════════════════════
// Store price swing lows and their corresponding %D values
var int[] swingLowBars = array.new_int()
var float[] swingLowPrices = array.new_float()
var float[] swingLowStochD = array.new_float()

// Store price swing highs and their corresponding %D values
var int[] swingHighBars = array.new_int()
var float[] swingHighPrices = array.new_float()
var float[] swingHighStochD = array.new_float()

// Store divergence lines for potential removal
var line[] bullLines = array.new_line()
var line[] bearLines = array.new_line()
var label[] bullLabels = array.new_label()
var label[] bearLabels = array.new_label()
var float[] bullLineEndD = array.new_float()
var float[] bearLineEndD = array.new_float()

// ═══════════════════════════════════════════════════════════════════════════════
// RECORD PRICE SWING LOWS AND %D VALUES AT THOSE BARS
// ═══════════════════════════════════════════════════════════════════════════════
if not na(pricePivotLow)
    pivotBar = bar_index - pivotLookbackRight
    pivotPrice = pricePivotLow
    pivotStochD = d[pivotLookbackRight]  // Get %D value at the pivot bar
    
    // Store the swing low data
    array.push(swingLowBars, pivotBar)
    array.push(swingLowPrices, pivotPrice)
    array.push(swingLowStochD, pivotStochD)
    
    // Limit array size
    if array.size(swingLowBars) > 50
        array.shift(swingLowBars)
        array.shift(swingLowPrices)
        array.shift(swingLowStochD)

// ═══════════════════════════════════════════════════════════════════════════════
// RECORD PRICE SWING HIGHS AND %D VALUES AT THOSE BARS
// ═══════════════════════════════════════════════════════════════════════════════
if not na(pricePivotHigh)
    pivotBar = bar_index - pivotLookbackRight
    pivotPrice = pricePivotHigh
    pivotStochD = d[pivotLookbackRight]  // Get %D value at the pivot bar
    
    // Store the swing high data
    array.push(swingHighBars, pivotBar)
    array.push(swingHighPrices, pivotPrice)
    array.push(swingHighStochD, pivotStochD)
  
    // Limit array size
    if array.size(swingHighBars) > 50
        array.shift(swingHighBars)
        array.shift(swingHighPrices)
        array.shift(swingHighStochD)

// ═══════════════════════════════════════════════════════════════════════════════
// BULLISH DIVERGENCE DETECTION
// Price: Lower Low | Stochastic %D: Higher Low (at same bars)
// ═══════════════════════════════════════════════════════════════════════════════
var bool bullDivDetected = false
bullDivDetected := false

if showDivergence and not na(pricePivotLow) and array.size(swingLowBars) >= 2
    // Current swing low (just added)
    currentBar = array.get(swingLowBars, array.size(swingLowBars) - 1)
    currentPrice = array.get(swingLowPrices, array.size(swingLowPrices) - 1)
    currentStochD = array.get(swingLowStochD, array.size(swingLowStochD) - 1)
    
    // Look for previous swing low within maxBarsBack
    for i = array.size(swingLowBars) - 2 to 0
        prevBar = array.get(swingLowBars, i)
        prevPrice = array.get(swingLowPrices, i)
        prevStochD = array.get(swingLowStochD, i)
        
        // Check if within range
        if currentBar - prevBar <= maxBarsBack
            // BULLISH DIVERGENCE: Price Lower Low + Stochastic %D Higher Low
            if currentPrice < prevPrice and currentStochD > prevStochD
                // Draw line from previous %D to current %D
                divLine = line.new(prevBar, prevStochD, currentBar, currentStochD,
                     color=bullDivColor, width=divLineWidth, style=line.style_dotted)
                array.push(bullLines, divLine)
                array.push(bullLineEndD, currentStochD)
        
                // Add label
                if showDivLabels
                    divLabel = label.new(currentBar, currentStochD, "Bull",
                         color=bullDivColor, textcolor=color.white, style=label.style_label_up, size=size.small)
                    array.push(bullLabels, divLabel)
                
                bullDivDetected := true
                break

// ═══════════════════════════════════════════════════════════════════════════════
// BEARISH DIVERGENCE DETECTION
// Price: Higher High | Stochastic %D: Lower High (at same bars)
// ═══════════════════════════════════════════════════════════════════════════════
var bool bearDivDetected = false
bearDivDetected := false

if showDivergence and not na(pricePivotHigh) and array.size(swingHighBars) >= 2
    // Current swing high (just added)
    currentBar = array.get(swingHighBars, array.size(swingHighBars) - 1)
    currentPrice = array.get(swingHighPrices, array.size(swingHighPrices) - 1)
    currentStochD = array.get(swingHighStochD, array.size(swingHighStochD) - 1)
    
    // Look for previous swing high within maxBarsBack
    for i = array.size(swingHighBars) - 2 to 0
        prevBar = array.get(swingHighBars, i)
        prevPrice = array.get(swingHighPrices, i)
        prevStochD = array.get(swingHighStochD, i)
        
        // Check if within range
        if currentBar - prevBar <= maxBarsBack
            // BEARISH DIVERGENCE: Price Higher High + Stochastic %D Lower High
            if currentPrice > prevPrice and currentStochD < prevStochD
                // Draw line from previous %D to current %D
                divLine = line.new(prevBar, prevStochD, currentBar, currentStochD,
                     color=bearDivColor, width=divLineWidth, style=line.style_dotted)
                array.push(bearLines, divLine)
                array.push(bearLineEndD, currentStochD)
        
                // Add label
                if showDivLabels
                    divLabel = label.new(currentBar, currentStochD, "Bear",
                         color=bearDivColor, textcolor=color.white, style=label.style_label_down, size=size.small)
                    array.push(bearLabels, divLabel)
                
                bearDivDetected := true
                break

// ═══════════════════════════════════════════════════════════════════════════════
// REMOVE BROKEN DIVERGENCES
// ═══════════════════════════════════════════════════════════════════════════════
if removeBrokenDiv and showDivergence
    // Remove broken bullish divergences (if %D breaks below the divergence end point)
    if array.size(bullLines) > 0
        for i = array.size(bullLines) - 1 to 0
            if i < array.size(bullLines) and i < array.size(bullLineEndD)
                endD = array.get(bullLineEndD, i)
                if d < endD - 5
                    line.delete(array.get(bullLines, i))
                    array.remove(bullLines, i)
                    array.remove(bullLineEndD, i)
                    if i < array.size(bullLabels)
                        label.delete(array.get(bullLabels, i))
                        array.remove(bullLabels, i)
    
    // Remove broken bearish divergences (if %D breaks above the divergence end point)
    if array.size(bearLines) > 0
        for i = array.size(bearLines) - 1 to 0
            if i < array.size(bearLines) and i < array.size(bearLineEndD)
                endD = array.get(bearLineEndD, i)
                if d > endD + 5
                    line.delete(array.get(bearLines, i))
                    array.remove(bearLines, i)
                    array.remove(bearLineEndD, i)
                    if i < array.size(bearLabels)
                        label.delete(array.get(bearLabels, i))
                        array.remove(bearLabels, i)

// ═══════════════════════════════════════════════════════════════════════════════
// PLOTS - Main Stochastic Lines
// ═══════════════════════════════════════════════════════════════════════════════
plot(showK ? k : na, title="%K", color=kColor, linewidth=2)
plot(showD ? d : na, title="%D", color=dColor, linewidth=1)

// ═══════════════════════════════════════════════════════════════════════════════
// PLOTS - Horizontal Bands
// ═══════════════════════════════════════════════════════════════════════════════
h0 = hline(obLevel, "Upper Band", color=#787B86)
hline(midLevel, "Middle Band", color=color.new(#787B86, 50))
h1 = hline(osLevel, "Lower Band", color=#787B86)
fill(h0, h1, color=color.rgb(33, 150, 243, 90), title="Background")

// ═══════════════════════════════════════════════════════════════════════════════
// PLOTS - Alert Shapes for Overbought (based on %D)
// ═══════════════════════════════════════════════════════════════════════════════
plotshape(showAlerts and crossedIntoOB and alertStyleOB == "Circle" ? d : na,
     title="OB Circle", style=shape.circle, location=location.absolute,
     color=alertColorOB)

plotshape(showAlerts and crossedIntoOB and alertStyleOB == "Arrow Down" ? d : na,
     title="OB Arrow", style=shape.triangledown, location=location.absolute,
     color=alertColorOB)

plotshape(showAlerts and crossedIntoOB and alertStyleOB == "Cross" ? d : na,
     title="OB Cross", style=shape.xcross, location=location.absolute,
     color=alertColorOB)

// ═══════════════════════════════════════════════════════════════════════════════
// PLOTS - Alert Shapes for Oversold (based on %D)
// ═══════════════════════════════════════════════════════════════════════════════
plotshape(showAlerts and crossedIntoOS and alertStyleOS == "Circle" ? d : na,
     title="OS Circle", style=shape.circle, location=location.absolute,
     color=alertColorOS)

plotshape(showAlerts and crossedIntoOS and alertStyleOS == "Arrow Up" ? d : na,
     title="OS Arrow", style=shape.triangleup, location=location.absolute,
     color=alertColorOS)

plotshape(showAlerts and crossedIntoOS and alertStyleOS == "Cross" ? d : na,
     title="OS Cross", style=shape.xcross, location=location.absolute,
     color=alertColorOS)

// ═══════════════════════════════════════════════════════════════════════════════
// STRATEGY EXECUTION LOGIC
// ═══════════════════════════════════════════════════════════════════════════════

// Entrada Larga (Compra)
if bullDivDetected
    strategy.entry("Long Div", strategy.long, comment="Bull Div", qty = lot_size)

// Entrada Corta (Venta)
if bearDivDetected
    strategy.entry("Short Div", strategy.short, comment="Bear Div", qty = lot_size)

// Salidas con Stop Loss y Take Profit
if strategy.position_size > 0
    // Salida para Posición Larga
    strategy.exit("Exit Long", "Long Div", stop = strategy.position_avg_price * (1 - slPerc), limit = strategy.position_avg_price * (1 + tpPerc), comment="TP/SL")

if strategy.position_size < 0
    // Salida para Posición Corta
    strategy.exit("Exit Short", "Short Div", stop = strategy.position_avg_price * (1 + slPerc), limit = strategy.position_avg_price * (1 - tpPerc), comment="TP/SL")

// ═══════════════════════════════════════════════════════════════════════════════
// ALERTS - For TradingView Alert System (Mantenidos como referencia)
// ═══════════════════════════════════════════════════════════════════════════════
alertcondition(crossedIntoOB, title="Overbought Alert", message="Stochastic %D crossed above overbought level")
alertcondition(crossedIntoOS, title="Oversold Alert", message="Stochastic %D crossed below oversold level")
alertcondition(ta.crossover(k, d), title="%K Cross Above %D", message="Stochastic %K crossed above %D - Potential Buy")
alertcondition(ta.crossunder(k, d), title="%K Cross Below %D", message="Stochastic %K crossed below %D - Potential Sell")
alertcondition(bullDivDetected, title="Bullish Divergence", message="Bullish Divergence: Price Lower Low + Stochastic %D Higher Low")
alertcondition(bearDivDetected, title="Bearish Divergence", message="Bearish Divergence: Price Higher High + Stochastic %D Lower High")

Overview

Stochastic Divergence Strategy plots the oscillator and divergence lines in a separate indicator pane. Entries trigger on detected divergence signals, with optional percentage-based stop loss and take profit controls. Settings allow configuration of Stochastic periods, pivot lookback lengths, overbought/oversold thresholds, and visual alert styles.

Details

Published by

JoelPasapera on GitHub

Type

Strategy, Pine Script v6

Draws

Separate pane below the chart

Category

Oscillators & momentum, Divergence

Markets

Forex, Gold, Stocks

Timeframes

M15-H1, H4 and above

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.

Topics

botpinescriptstrategytradingtradingview

Settings

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

SettingTypeDefaultVariable
Lot sizeint1lot_size
%K Lengthint14periodK
%K Smoothingint1smoothK
%D Smoothingint3periodD
Overbought Levelint80obLevel
Oversold Levelint20osLevel
Middle Levelint50midLevel
Show %K LinebooltrueshowK
Show %D LinebooltrueshowD
Rising Colorcolorcolor.greencolorUp
Falling Colorcolorcolor.redcolorDown
Sideways Colorcolorcolor.whitecolorSideways
Show all 31 settings
SettingTypeDefaultVariable
Show OB/OS AlertsbooltrueshowAlerts
Overbought StylestringCirclealertStyleOB
Oversold StylestringCirclealertStyleOS
Overbought Alert Colorcolorcolor.redalertColorOB
Oversold Alert Colorcolorcolor.greenalertColorOS
Show DivergencesbooltrueshowDivergence
Ticker for DivergencesymboldivTicker
Pivot Lookback Leftint5pivotLookbackLeft
Pivot Lookback Rightint5pivotLookbackRight
Max Bars to Look Backint100maxBarsBack
Remove Broken DivergencesbooltrueremoveBrokenDiv
Bullish Divergence Colorcolor#4caf50bullDivColor
Bearish Divergence Colorcolor#f23645bearDivColor
Divergence Line Widthint3divLineWidth
Show Divergence LabelsbooltrueshowDivLabels
Use Stop LossbooltrueuseSL
Stop Loss (%)float2.0slPerc
Use Take ProfitbooltrueuseTP
Take Profit (%)float4.0tpPerc
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

330 stars

MACD + Divergences [Alorse] is a momentum strategy that calculates the MACD oscillator and identifies regular and hidden divergences using pivot high and low points. It filters trade signals with dual Exponential Moving Averages (EMA 150 and 600) to ensure tra

Strategy v4 Oscillators & momentumDivergence Repaints
Updated 2 months ago · None stated GitHub repository of MACD + Divergences [Alorse]

Awesome Oscillator - Eterna MCP

by EternaHybridExchange
9 stars

This strategy implements Bill Williams' Awesome Oscillator (AO) calculated as the difference between two simple moving averages of the median price. It generates long and short entries using three classic signal triggers: zero-line crossovers, twin peaks diver

Strategy v5 Oscillators & momentumMoving averages
Updated 7 months ago · MIT GitHub repository of Awesome Oscillator - Eterna MCP

This strategy calculates regular and hidden divergences across selectable momentum oscillators, including RSI, CCI, and MFI, and plots volatility-based divergence zones. It displays oscillator dynamics in a separate pane while projecting support and resistance

Strategy v6 DivergenceOscillators & momentum
Updated yesterday · MIT GitHub repository of Oscillator Divergence Zones [WavesUnchained] — Strategy

Alper

by avatar-lavventura
2 stars

This strategy combines a WaveTrend oscillator with a dual-period RSI differential to generate trend-following entries in a separate indicator pane. It calculates fast and slow RSI values, alongside WaveTrend channel lines and overbought or oversold thresholds.

Strategy v4 Oscillators & momentumDivergence
Updated 7 months ago · None stated GitHub repository of Alper

Trust Me Bro Strategy

by moonyroy
2 stars

A trading strategy that uses an RSI state machine tracking 50-level crossovers, pullbacks, and hidden divergences. Entries require confirmation from optional confluence filters, including an EMA touch, Fibonacci retracement zones, and Ichimoku cloud conditions

Strategy v6 Oscillators & momentumDivergence
Updated 7 weeks ago · MIT GitHub repository of Trust Me Bro Strategy

More strategys

Trade Your Charts With a Regulated Broker

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

Compare brokers