Indicator · Pine Script v6 · On the chart

Options Max Pain Calculator [BackQuant]

by regalouisei· code credited to BackQuant

Options Max Pain Calculator [BackQuant] estimates options max pain theory levels, pin risk zones, and market maker gamma exposure directly on the price chart.

View on GitHub

GitHub stars2on the repository
Pine versionv6current
Lines of code517
Settings25inputs
LicenceNone statedfile: MPL-2.0
Last update8 months ago25 Feb 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

517 lines of Pine Script v6 from pinescript/editors_picks/options-max-pain-calculator-backquant.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

options-max-pain-calculator-backquant.pine
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © BackQuant

//@version=6
indicator(
 title="Options Max Pain Calculator [BackQuant]", 
 shorttitle="Max Pain [BackQuant]", 
 max_boxes_count=500,
 max_lines_count=500, 
 max_labels_count=100,
 overlay=true
 )
// Input Groups
const string calc       = "Calculation Settings"
const string strikes_g  = "Strike Price Settings"
const string volume_g   = "Volume and Open Interest"
const string vols       = "Volatility and Greeks"
const string mm         = "Market Maker Parameters"
const string visuals    = "Visual Settings"
const string analysis   = "Analysis Settings"
const string theme_g    = "Theme and Colors"
const string zones      = "Pin Zone Settings"

// Calculation Settings
int nStrikes = input.int(20, "Number of Strikes", minval=1, tooltip="Total number of strike levels to model", group=calc)
int expiry_days = input.int(7, "Days to Expiration", minval=1, maxval=60, tooltip="Calendar days until option expiry", group=calc)
float market_hours_per_day = input.float(6.5, "Trading Hours per Day", minval=1, maxval=24, step=0.5, tooltip="Used for time conversions, does not affect modeling", group=calc)

// Strike Price Settings
float strike_range_pct = input.float(10, "Strike Range %", minval=5, maxval=20, step=1, tooltip="Range around current price in percent", group=strikes_g)
float strike_increment = input.float(2.0, "Strike Increment $", minval=1.0, maxval=10.0, step=0.5, tooltip="Distance between generated strikes", group=strikes_g)
bool use_round_strikes = input.bool(true, "Use Round Strike Levels", tooltip="Rounds strikes to nearest 5s or 0s", group=strikes_g)
int major_strike_interval = input.int(10, "Major Strike Interval", minval=5, maxval=50, step=5, tooltip="Interval for showing major strike levels", group=strikes_g)

// Volume and Open Interest
int avg_daily_volume = input.int(1000000, "Average Daily Volume", minval=100000, tooltip="Baseline underlying trading volume", group=volume_g)
float put_call_ratio = input.float(1.2, "Put/Call Volume Ratio", minval=0.1, maxval=5.0, step=0.1, tooltip="Relative weighting of put to call volumes", group=volume_g)

// Volatility and Greeks
float implied_vol = input.float(30.0, "Implied Volatility %", minval=5, maxval=150, step=1, tooltip="Baseline volatility assumption for Greeks", group=vols) / 100

// Market Maker Parameters
float mm_inventory_factor = input.float(0.7, "Inventory Factor", minval=0.1, maxval=2.0, step=0.1, tooltip="Market maker position scaling", group=mm)
float gamma_hedging_strength = input.float(1.0, "Gamma Hedging Strength", minval=0.1, maxval=3.0, step=0.1, tooltip="Degree of gamma hedging effect modeled", group=mm)
float dealer_short_gamma_bias = input.float(0.6, "Dealer Short Gamma Bias", minval=0.1, maxval=1.5, step=0.1, tooltip="Bias toward short gamma positioning", group=mm)

// Visual Settings
bool show_max_pain_line = input.bool(true, "Show Max Pain Line", group=visuals)
bool show_strike_levels = input.bool(true, "Show Major Strike Levels", group=visuals)
bool show_pain_heatmap = input.bool(true, "Show Pain Heatmap", group=visuals)
bool show_gamma_exposure = input.bool(true, "Show Gamma Exposure Profile", group=visuals)
bool show_pin_zones = input.bool(true, "Show Pin Risk Zones", group=visuals)
int heatmap_transparency = input.int(40, "Heatmap Transparency", minval=0, maxval=100, tooltip="Adjust heatmap visibility", group=visuals)
int pinzone_transparency = input.int(80, "Pin Zone Transparency", minval=0, maxval=100, tooltip="Adjust pin zone shading", group=visuals)

// Analysis Settings
string update_frequency = input.string("Daily", "Update Frequency", options=["Every Bar","Daily"], tooltip="How often to update calculations", group=analysis)
bool include_weekly = input.bool(true, "Include Weekly Options Effects", tooltip="Boosts OI if expiry is weekly", group=analysis)
string model_complexity = input.string("Advanced", "Model Complexity", options=["Simple","Standard","Advanced"], tooltip="Controls modeling detail and features", group=analysis)

// Theme and Colors
string theme = input.string("Dark", "Theme", options=["Dark","Light"], tooltip="Switch chart color scheme", group=theme_g)

// Pin Zone Settings
float pin_zone_pct = input.float(2.0, "Pin Zone %", minval=0.5, maxval=5.0, step=0.5, tooltip="Defines percentage width around max pain considered a pin zone", group=zones)

max_pain_color  = #ff0000
support_color   = #00ff00
resistance_color= #00ffff
gamma_color     = color.purple
heatmap_hot     = color.red
heatmap_cold    = color.blue

// Effective palette
panel_bg = theme == "Dark" ? color.rgb(18,22,28, 100) : color.white
panel_fg = theme == "Dark" ? color.white : color.black
head_bg  = theme == "Dark" ? color.rgb(20,40,80, 100) : color.rgb(230,235,245)
head_fg  = theme == "Dark" ? color.white : color.black


// Feature gates
draw_strikes = show_strike_levels and (model_complexity == "Standard" or model_complexity == "Advanced")
draw_pin     = show_pin_zones     and (model_complexity == "Standard" or model_complexity == "Advanced")
draw_heat    = show_pain_heatmap  and model_complexity == "Advanced"
draw_gamma   = show_gamma_exposure and model_complexity == "Advanced"

// Calculation Start
time_to_expiry = expiry_days / 365.25
current_price = close
strike_range = current_price * strike_range_pct / 100
min_strike = current_price - strike_range
max_strike = current_price + strike_range

round_strike(strike) =>
    if use_round_strikes
        if strike >= 100
            math.round(strike / 5) * 5
        else if strike >= 20
            math.round(strike / 2.5) * 2.5
        else
            math.round(strike)
    else
        math.round(strike / strike_increment) * strike_increment

var strikes = array.new<float>()
var call_oi_data = array.new<float>()
var put_oi_data = array.new<float>()
var gamma_data = array.new<float>()

generate_strikes(nStrikes) =>
    array.clear(strikes)
    max_strikes = nStrikes
    actual_increment = math.max(strike_increment, (max_strike - min_strike) / max_strikes)
    
    s = min_strike
    count = 0
    while s <= max_strike and count < max_strikes
        rs = round_strike(s)
        if array.size(strikes) == 0 or rs != array.get(strikes, array.size(strikes) - 1)
            array.push(strikes, rs)
            count += 1
        s += actual_increment

// Black-Scholes helpers
bs_gamma_simple(S, K, T, vol) =>
    if T <= 0 or vol <= 0
        0.0
    else
        d1 = (math.log(S/K) + 0.5 * vol * vol * T) / (vol * math.sqrt(T))
        math.exp(-0.5 * d1 * d1) / (S * vol * math.sqrt(2 * math.pi * T))

// volume estimation
estimate_oi_simple(strike, is_call) =>
    distance_pct = math.abs(strike - current_price) / current_price
    base_factor = 1 / (1 + distance_pct * 8)
    decay_factor = math.pow(0.8, distance_pct * 15)
    
    base_volume = avg_daily_volume * base_factor * decay_factor * 0.02
    mm_position = base_volume * mm_inventory_factor * dealer_short_gamma_bias
    
    total_oi = base_volume + mm_position
    if not is_call
        total_oi *= put_call_ratio
    
    weekly_mult = include_weekly and expiry_days <= 7 ? 1.2 : 1.0
    total_oi * weekly_mult

// all data for strikes
populate_strike_data() =>
    array.clear(call_oi_data)
    array.clear(put_oi_data)
    array.clear(gamma_data)
    
    strike_count = array.size(strikes)
    i = 0
    while i < strike_count and i < nStrikes
        K = array.get(strikes, i)
        
        call_oi = estimate_oi_simple(K, true)
        put_oi = estimate_oi_simple(K, false)
        gamma_val = bs_gamma_simple(current_price, K, time_to_expiry, implied_vol)
        gamma_exp = (call_oi - put_oi) * gamma_val * gamma_hedging_strength * 100
        
        array.push(call_oi_data, call_oi)
        array.push(put_oi_data, put_oi)
        array.push(gamma_data, gamma_exp)
        
        i += 1

// Direct max pain calculation 
calculate_max_pain_direct() =>
    best_strike = current_price
    min_pain = 1e20
    
    strike_count = array.size(strikes)
    test_points = math.min(strike_count, nStrikes) 
    
    i = 0
    while i < test_points
        test_strike = array.get(strikes, i)
        total_pain = 0.0
        
        // Calculate pain at this strike
        j = 0
        while j < strike_count
            K = array.get(strikes, j)
            call_oi = array.get(call_oi_data, j)
            put_oi = array.get(put_oi_data, j)
            
            if test_strike < K  // Call pain
                total_pain += call_oi * (K - test_strike)
            if test_strike > K  // Put pain  
                total_pain += put_oi * (test_strike - K)
            
            j += 1
        
        if total_pain < min_pain
            min_pain := total_pain
            best_strike := test_strike
        
        i += 1
    
    best_strike

// Update
var float max_pain_level = na
var bool data_calculated = false

should_update = barstate.islast or (update_frequency == "Every Bar") or(update_frequency == "Daily" and not data_calculated) 

if should_update
    generate_strikes(nStrikes)
    populate_strike_data()
    max_pain_level := calculate_max_pain_direct()
    data_calculated := true

// Plotting
var int base_x = 0
if barstate.islast
    base_x := bar_index

// Max Pain Line 
var line max_pain_line = na
if show_max_pain_line and not na(max_pain_level) and barstate.islast
    line.delete(max_pain_line)
    max_pain_line := line.new(base_x - 50, max_pain_level, base_x + 10, max_pain_level, color=max_pain_color, width=3, extend=extend.none)

if draw_strikes and barstate.islast and array.size(strikes) > 0
    i = 0
    while i < math.min(array.size(strikes), 20)
        K = array.get(strikes, i)
        if math.round(K) % major_strike_interval == 0
            lvl_col = K > current_price ? resistance_color : support_color
            line.new(base_x - 20, K, base_x + 5, K, color=color.new(lvl_col, 30), width=1, style=line.style_dotted)
        i += 1

// Pin Zone
if draw_pin and not na(max_pain_level) and barstate.islast
    pin_range = current_price * pin_zone_pct / 100.0
    upper_pin = max_pain_level + pin_range
    lower_pin = max_pain_level - pin_range
    box.new(base_x - 30, lower_pin, base_x + 10, upper_pin, bgcolor=color.new(max_pain_color, pinzone_transparency), border_color=max_pain_color)

// GAMMA EXPOSURE 
if draw_gamma and barstate.islast and array.size(gamma_data) > 0
    max_gamma = 0.0
    total_long_gamma = 0.0
    total_short_gamma = 0.0
    
    i = 0
    while i < array.size(gamma_data)
        gamma_val = array.get(gamma_data, i)
        abs_gamma = math.abs(gamma_val)
        max_gamma := math.max(max_gamma, abs_gamma)
        
        if gamma_val > 0
            total_long_gamma += gamma_val
        else
            total_short_gamma += math.abs(gamma_val)
        i += 1
    
    if max_gamma > 0
        gamma_start_x = base_x + 15
        
        // Create gamma profile bars
        i = 0
        while i < math.min(array.size(gamma_data), 15)
            K = array.get(strikes, i)
            gamma_val = array.get(gamma_data, i)
            
            if math.abs(gamma_val) > max_gamma * 0.05
                normalized = gamma_val / max_gamma * 12
                bar_length = int(math.abs(normalized))
                
                if bar_length > 0
                    is_positive = gamma_val > 0
                    base_color = is_positive ? color.rgb(0, 200, 100) : color.rgb(255, 80, 80)
                    intensity = math.abs(gamma_val) / max_gamma
                    alpha_level = int(20 + intensity * 40)
                    
                    line.new(
                         x1=gamma_start_x, 
                         y1=K,
                         x2=gamma_start_x + (is_positive ? bar_length : -bar_length), 
                         y2=K,
                         color=color.new(base_color, alpha_level),
                         width=8,
                         style=line.style_solid)
                    
                    if math.abs(gamma_val) > max_gamma * 0.4
                        label_x = gamma_start_x + (is_positive ? bar_length + 2 : -bar_length - 2)
                        label_style = is_positive ? label.style_label_left : label.style_label_right
                        
                        label.new(
                             x=label_x,
                             y=K,
                             text=str.tostring(K, "#.##"),
                             color=color.new(base_color, 80),
                             textcolor=color.white,
                             style=label_style,
                             size=size.tiny)
            i += 1
        
        // Gamma titles and legends
        title_price = max_strike - (max_strike - min_strike) * 0.1
        label.new(
             x=gamma_start_x,
             y=title_price,
             text="GAMMA\nEXPOSURE",
             color=color.new(color.purple, 80),
             textcolor=color.white,
             style=label.style_label_center,
             size=size.small)
        
        // Zero line
        line.new(
             x1=gamma_start_x - 2,
             y1=min_strike,
             x2=gamma_start_x - 2,
             y2=max_strike,
             color=color.new(color.gray, 60),
             width=1,
             style=line.style_dashed)
        
        // Net gamma stats
        net_gamma = total_long_gamma - total_short_gamma
        gamma_bias = net_gamma > 0 ? "LONG" : "SHORT"
        gamma_bias_color = net_gamma > 0 ? color.green : color.red
        
        stats_price = min_strike + (max_strike - min_strike) * 0.15
        label.new(
             x=gamma_start_x + 8,
             y=stats_price,
             text="NET: " + gamma_bias + "\n" + str.tostring(math.abs(net_gamma), "#.#"),
             color=color.new(gamma_bias_color, 70),
             textcolor=color.white,
             style=label.style_label_left,
             size=size.tiny)
        
        // Gamma flip detection
        gamma_flip_found = false
        gamma_flip_price = current_price
        
        ix = 0
        while ix < array.size(gamma_data) - 1 and not gamma_flip_found
            current_gamma = array.get(gamma_data, ix)
            next_gamma = array.get(gamma_data, ix + 1)
            
            if (current_gamma > 0 and next_gamma < 0) or (current_gamma < 0 and next_gamma > 0)
                gamma_flip_price := array.get(strikes, ix)
                gamma_flip_found := true
            ix += 1
        
        if gamma_flip_found and math.abs(gamma_flip_price - current_price) < strike_range
            line.new(
                 x1=base_x - 10,
                 y1=gamma_flip_price,
                 x2=gamma_start_x + 20,
                 y2=gamma_flip_price,
                 color=color.new(color.yellow, 40),
                 width=2,
                 style=line.style_dotted)
            
            label.new(
                 x=base_x - 12,
                 y=gamma_flip_price,
                 text="Γ FLIP",
                 color=color.new(color.yellow, 60),
                 textcolor=color.black,
                 style=label.style_label_right,
                 size=size.tiny)

// Heatmap
if draw_heat and barstate.islast and array.size(strikes) > 0
    heatmap_start_x = base_x + 35 
    
    heatmap_levels = 12
    price_step = (max_strike - min_strike) / heatmap_levels
    
    pain_values = array.new<float>()
    test_prices = array.new<float>()
    
    // Calculate pain at each level
    level = 0
    while level < heatmap_levels
        test_price = min_strike + level * price_step
        array.push(test_prices, test_price)
        
        total_pain = 0.0
        strike_idx = 0
        while strike_idx < array.size(strikes)
            K = array.get(strikes, strike_idx)
            call_oi = array.get(call_oi_data, strike_idx)
            put_oi = array.get(put_oi_data, strike_idx)
            
            if test_price < K
                total_pain += call_oi * (K - test_price)
            if test_price > K
                total_pain += put_oi * (test_price - K)
            strike_idx += 1
        
        array.push(pain_values, total_pain)
        level += 1
    
    min_pain = array.min(pain_values)
    max_pain = array.max(pain_values)
    pain_range = max_pain - min_pain
    
    // Create heatmap 
    if pain_range > 0
        level = 0
        while level < heatmap_levels
            current_price_level = array.get(test_prices, level)
            current_pain = array.get(pain_values, level)
            
            normalized_pain = 1.0 - ((current_pain - min_pain) / pain_range)
            gradient_color = color.from_gradient(normalized_pain, 0, 1, color.rgb(0, 100, 200), color.rgb(255, 50, 50))
            
            box_bottom = current_price_level
            box_top = level < heatmap_levels - 1 ? array.get(test_prices, level + 1) : current_price_level + price_step
            
            box.new(
                 left=heatmap_start_x, 
                 top=box_top,
                 right=heatmap_start_x + 10,
                 bottom=box_bottom,
                 bgcolor=color.new(gradient_color, heatmap_transparency),
                 border_color=color.new(gradient_color, math.min(heatmap_transparency + 20, 90)),
                 border_width=1)
            
            level += 1
        
        // Heatmap labels
        legend_x = heatmap_start_x + 12
        
        // Find max pain level
        max_pain_idx = 0
        min_pain_value = array.get(pain_values, 0)
        check_idx = 1
        while check_idx < array.size(pain_values)
            if array.get(pain_values, check_idx) < min_pain_value
                min_pain_value := array.get(pain_values, check_idx)
                max_pain_idx := check_idx
            check_idx += 1
        
        max_pain_price = array.get(test_prices, max_pain_idx)
        
        label.new(
             x=legend_x, 
             y=max_pain_price, 
             text="MAX PAIN\nZONE", 
             color=color.new(color.red, 20), 
             textcolor=color.white,
             style=label.style_label_left, 
             size=size.small)
        
        // Heatmap title
        heatmap_title_price = min_strike + (max_strike - min_strike) * 0.9
        label.new(
             x=heatmap_start_x + 5, 
             y=heatmap_title_price, 
             text="PAIN\nHEATMAP", 
             color=color.new(color.gray, 80), 
             textcolor=color.white,
             style=label.style_label_center, 
             size=size.tiny)
        
        // Gradient legend
        legend_levels = 3
        legend_idx = 0
        while legend_idx < legend_levels
            legend_pain_norm = legend_idx / (legend_levels - 1)
            legend_color = color.from_gradient(legend_pain_norm, 0, 1, color.rgb(0, 100, 200), color.rgb(255, 50, 50))
            legend_price = min_strike + legend_idx * (max_strike - min_strike) / (legend_levels - 1)
            legend_text = legend_idx == 0 ? "HIGH\nPAIN" : legend_idx == legend_levels - 1 ? "LOW\nPAIN" : ""
            
            if legend_text != ""
                label.new(
                     x=heatmap_start_x + 15, 
                     y=legend_price, 
                     text=legend_text, 
                     color=color.new(legend_color, 70), 
                     textcolor=color.white,
                     style=label.style_label_left, 
                     size=size.tiny)
            legend_idx += 1

var table main_table = table.new(position.top_right, 2, 8, bgcolor=panel_bg, border_width=1, border_color=color.new(color.gray, 60), frame_color = color.white, frame_width = 1)

if barstate.islast
    table.cell(main_table, 0, 0, "Max Pain Analysis", bgcolor=head_bg, text_color=head_fg)
    table.cell(main_table, 1, 0, "Values", bgcolor=head_bg, text_color=head_fg)

    table.cell(main_table, 0, 1, "Current Price", text_color=panel_fg)
    table.cell(main_table, 1, 1, str.tostring(current_price, "#.##"), text_color=panel_fg)

    table.cell(main_table, 0, 2, "Max Pain Level", text_color=panel_fg)
    table.cell(main_table, 1, 2, str.tostring(max_pain_level, "#.##"), text_color=max_pain_color)

    distance_to_pain = not na(max_pain_level) ? ((max_pain_level - current_price) / current_price) * 100.0 : 0.0
    dist_color = math.abs(distance_to_pain) > 5 ? color.red : color.green
    table.cell(main_table, 0, 3, "Distance to Pain", text_color=panel_fg)
    table.cell(main_table, 1, 3, str.tostring(distance_to_pain, "#.##") + "%", text_color=dist_color)

    table.cell(main_table, 0, 4, "Days to Expiry", text_color=panel_fg)
    table.cell(main_table, 1, 4, str.tostring(expiry_days), text_color=panel_fg)

    table.cell(main_table, 0, 5, "Strike Count", text_color=panel_fg)
    table.cell(main_table, 1, 5, str.tostring(array.size(strikes)), text_color=panel_fg)

    pin_prob = math.abs(distance_to_pain) < 2 ? "HIGH" : math.abs(distance_to_pain) < 5 ? "MEDIUM" : "LOW"
    pin_color = pin_prob == "HIGH" ? color.red : pin_prob == "MEDIUM" ? color.orange : color.green
    table.cell(main_table, 0, 6, "Pin Risk", text_color=panel_fg)
    table.cell(main_table, 1, 6, pin_prob, text_color=pin_color)

    table.cell(main_table, 0, 7, "Model", text_color=panel_fg)
    table.cell(main_table, 1, 7, model_complexity, text_color=panel_fg)

Overview

Options Max Pain Calculator [BackQuant] models strike prices, put/call volume ratios, implied volatility, and dealer hedging parameters to project where option holders face the maximum financial loss upon expiration. Users can configure the number of strikes, strike increments, days to expiration, volatility, and market maker inventory factors. The script overlay includes max pain lines, strike levels, and pin zones. Note that options metrics are calculated through internal mathematical modeling based on user inputs rather than live external options chains.

Details

Published by

regalouisei on GitHub

Code credit

BackQuant, as written in the script's header

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Support & resistance

Markets

Stocks, Indices, Crypto

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.

Settings

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

SettingTypeDefaultVariable
Number of Strikesint20nStrikes
Days to Expirationint7expiry_days
Trading Hours per Dayfloat6.5market_hours_per_day
Strike Range %float10strike_range_pct
Strike Increment $float2.0strike_increment
Use Round Strike Levelsbooltrueuse_round_strikes
Major Strike Intervalint10major_strike_interval
Average Daily Volumeint1000000avg_daily_volume
Put/Call Volume Ratiofloat1.2put_call_ratio
Implied Volatility %float30.0implied_vol
Inventory Factorfloat0.7mm_inventory_factor
Gamma Hedging Strengthfloat1.0gamma_hedging_strength
Show all 25 settings
SettingTypeDefaultVariable
Dealer Short Gamma Biasfloat0.6dealer_short_gamma_bias
Show Max Pain Linebooltrueshow_max_pain_line
Show Major Strike Levelsbooltrueshow_strike_levels
Show Pain Heatmapbooltrueshow_pain_heatmap
Show Gamma Exposure Profilebooltrueshow_gamma_exposure
Show Pin Risk Zonesbooltrueshow_pin_zones
Heatmap Transparencyint40heatmap_transparency
Pin Zone Transparencyint80pinzone_transparency
Update FrequencystringDailyupdate_frequency
Include Weekly Options Effectsbooltrueinclude_weekly
Model ComplexitystringAdvancedmodel_complexity
ThemestringDarktheme
Pin Zone %float2.0pin_zone_pct
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

948 stars

Moving Average Channel plots separate simple moving averages for upper and lower bands on the price chart. It fills the space between bands with semi-transparent color and colors bars green above the upper band, red below the lower band, or gray in between. Us

Indicator v4 Moving averagesSupport & resistance
Updated 10 days ago · GPL-3.0 GitHub repository of Moving Average Channel

ICT Equal Highs and Lows

by ArunKBhaskar
575 stars

This overlay indicator finds pivot highs and lows on the price chart and compares the last two highs and the last two lows. When the percentage difference between a pair is below the Equal HL threshold, it treats them as equal levels and draws a box. Left and

Indicator v5 SMC / ICT / price actionSupport & resistance
Updated 1 year ago · MPL-2.0 GitHub repository of ICT Equal Highs and Lows
330 stars

This indicator monitors multiple cryptocurrency symbols for trading signals. It calculates and plots entry prices, take profit, and stop loss levels for each symbol including BTCUSDT, ETHUSDT, BNBUSDT and others. Price crossovers trigger buy, sell or close ale

Indicator v5 Support & resistanceScreeners & dashboards
Updated 2 months ago · None stated GitHub repository of Multi Alert Long/Short [Alorse]

This overlay indicator draws a fixed-range volume profile on the price chart, splitting traded activity into up and down volume at each price level. It also plots a sentiment profile, a developing point of control, and value-area high and low around a configur

Indicator v5 VolumeSupport & resistance
Updated 2 months ago · MIT GitHub repository of Volume Profile and Indicator by DGT

BZ-tracker

by Allen1212
303 stars

The BZ-tracker indicator marks ARK Invest buy and sell transactions on stock charts with labels. It plots green labels for purchases and red labels for sales, each annotated with a multiplier such as X1 or X2 to show the number of ARK ETFs trading the stock on

Indicator v4 Support & resistanceScreeners & dashboards
Updated 2 years ago · None stated GitHub repository of BZ-tracker

Function - Linear Regression

by ricardosantos79
261 stars

This indicator fits a linear regression through stored pivot highs and lows and draws the resulting channel on the price chart. A function returns predicted values, maximum and minimum deviations, and the average absolute deviation. Confirmed two-bar pivots up

Indicator v5 TrendSupport & resistance
Updated 4 years ago · None stated GitHub repository of Function - Linear Regression

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