Indicator · Pine Script v6 · On the chart

Adaptive Trend Finder (log)

by deepentropy· code credited to Julien_Eche

The Adaptive Trend Finder calculates logarithmic linear regression channels overlaid on the price chart.

View on GitHub

Repaints

GitHub stars13on the repository
Pine versionv6current
Lines of code368
Settings18inputs
LicenceMIT
Last update6 months ago29 Apr 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

368 lines of Pine Script v6 from examples/Adaptive Trend Finder (log).pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

Adaptive Trend Finder (log).pine
// © Julien_Eche
// Copyright (c) 2023-present, Julien Eche
// Adaptive Trend Finder script may be freely distributed under the terms of the GPL-3.0 license.
// Created in December 2023 by Julien Eche

//@version=6
indicator('Adaptive Trend Finder (log)', shorttitle = 'Adaptive Trend Finder', overlay = true, max_bars_back = 1200)

confidence(pearsonR) =>
    switch
        pearsonR < 0.2 => 'Extremely Weak'
        pearsonR < 0.3 => 'Very Weak'
        pearsonR < 0.4 => 'Weak'
        pearsonR < 0.5 => 'Mostly Weak'
        pearsonR < 0.6 => 'Somewhat Weak'
        pearsonR < 0.7 => 'Moderately Weak'
        pearsonR < 0.8 => 'Moderate'
        pearsonR < 0.9 => 'Moderately Strong'
        pearsonR < 0.92 => 'Mostly Strong'
        pearsonR < 0.94 => 'Strong'
        pearsonR < 0.96 => 'Very Strong'
        pearsonR < 0.98 => 'Exceptionally Strong'
        => 'Ultra Strong'

getTablePosition(string pos) =>
    switch pos
        'Bottom Right' => position.bottom_right
        'Bottom Center' => position.bottom_center
        'Bottom Left' => position.bottom_left
        'Top Right' => position.top_right
        'Top Left' => position.top_left
        'Top Center' => position.top_center
        'Middle Right' => position.middle_right
        => position.middle_left // "Middle Left" - default

// Calculate deviations for given length
calcDev(float source, int length) =>
    float logSource = math.log(source)
    var int period_1 = length - 1
    if barstate.islast
        float sumX = 0.0
        float sumXX = 0.0
        float sumYX = 0.0
        float sumY = 0.0
        for int i = 1 to length by 1
            float lSrc = logSource[i - 1]
            sumX := sumX + i
            sumXX := sumXX + i * i
            sumYX := sumYX + i * lSrc
            sumY := sumY + lSrc
            sumY
        float slope = nz((length * sumYX - sumX * sumY) / (length * sumXX - sumX * sumX))
        float average = sumY / length
        float intercept = average - slope * sumX / length + slope
        float sumDev = 0.0
        float sumDxx = 0.0
        float sumDyy = 0.0
        float sumDyx = 0.0
        float regres = intercept + slope * period_1 * 0.5
        float sumSlp = intercept
        for int i = 0 to period_1 by 1
            float lSrc = logSource[i]
            float dxt = lSrc - average
            float dyt = sumSlp - regres
            lSrc := lSrc - sumSlp
            sumSlp := sumSlp + slope
            sumDxx := sumDxx + dxt * dxt
            sumDyy := sumDyy + dyt * dyt
            sumDyx := sumDyx + dxt * dyt
            sumDev := sumDev + lSrc * lSrc
            sumDev
        float unStdDev = math.sqrt(sumDev / period_1) // unbiased
        float divisor = sumDxx * sumDyy
        float pearsonR = nz(sumDyx / math.sqrt(divisor))
        [unStdDev, pearsonR, slope, intercept]
    else
        [na, na, na, na]

string t1 = 'In Long-Term Channel mode, if the channel is not visible, scroll back on the chart for additional historical data. To view both Short-Term and Long-Term channels simultaneously, load this indicator twice on your chart.'
string t2 = 'Displays the length of the period automatically selected by the indicator that shows the strongest trend. This period is determined by identifying the highest correlation between price movements and trend direction.'
string t3 = 'Pearson\'s R is a statistical measure that evaluates the linear relationship between price movements and trend projection. A value closer to 1 indicates a strong positive correlation, increasing confidence in the trend direction based on historical data.'
string t4 = 'Displays the annualized return (CAGR) of the trend over the auto-selected period. This feature is available only for daily (D) and weekly (W) timeframes, providing insight into the expected yearly growth rate if the trend continues.'

sourceInput = input.source(close, title = 'Source')

string group0 = 'CHANNEL SETTINGS'
bool periodMode = input.bool(false, 'Use Long-Term Channel', group = group0, tooltip = t1)
float devMultiplier = input.float(2.0, 'Deviation Multiplier:', group = group0, step = 0.1)
color colorInput = input.color(color.gray, '', group = group0, inline = group0)
string lineStyle1 = input.string('Solid', '', group = group0, inline = group0, options = ['Solid', 'Dotted', 'Dashed'])
string extendStyle = input.string('Extend Right', '', group = group0, inline = group0, options = ['Extend Right', 'Extend Both', 'Extend None', 'Extend Left'])
int fillTransparency = input.int(93, 'Fill Transp:', group = group0, inline = 'mid', minval = 0, maxval = 100, step = 1)
int channelTransparency = input.int(40, 'Line Transp:', group = group0, inline = 'mid', minval = 0, maxval = 100, step = 1)

string group1 = 'MIDLINE SETTINGS'
color colorInputMidline = input.color(color.blue, '', group = group1, inline = group1)
int transpInput = input.int(100, 'Transp:', group = group1, inline = group1, minval = 0, maxval = 100, step = 10)
int lineWidth = input.int(1, 'Line Width:', group = group1, inline = group1)
string midLineStyle = input.string('Dashed', '', group = group1, inline = group1, options = ['Dotted', 'Solid', 'Dashed'])

string group2 = 'TABLE SETTINGS'
bool showAutoSelectedPeriod = input(true, 'Show Auto-Selected Period', group = group2, tooltip = t2)
bool showTrendStrength = input(true, 'Show Trend Strength', group = group2, inline = 'secondLine')
bool showPearsonInput = input.bool(false, 'Show Pearson\'s R', group = group2, inline = 'secondLine', tooltip = t3)
bool showTrendAnnualizedReturn = input(true, 'Show Trend Annualized Return', group = group2, tooltip = t4)
string tablePositionInput = input.string('Bottom Right', 'Table Position', options = ['Bottom Right', 'Bottom Left', 'Middle Right', 'Middle Left', 'Top Right', 'Top Left', 'Top Center', 'Bottom Center'], group = group2, inline = 'fourthLine')
string textSizeInput = input.string('Normal', 'Text Size', options = ['Normal', 'Large', 'Small'], group = group2, inline = 'fourthLine')

// Helper function to get the multiplier based on timeframe
get_tf_multiplier() =>
    var float multiplier = 1.0
    if syminfo.type == 'crypto'
        if timeframe.isdaily
            multiplier := 365 // ~365 trading days per year
            multiplier
        else if timeframe.isweekly
            multiplier := 52 // 52 weeks per year
            multiplier
        multiplier
    else // Default for stocks and other asset types
        if timeframe.isdaily
            multiplier := 252 // ~252 trading days per year
            multiplier
        else if timeframe.isweekly
            multiplier := 52 // 52 weeks per year
            multiplier
        multiplier

// Helper function to check if the timeframe is daily or weekly
is_valid_timeframe() =>
    timeframe.isdaily or timeframe.isweekly

var string EXTEND_STYLE = switch extendStyle
    'Extend Right' => extend.right
    'Extend Both' => extend.both
    'Extend None' => extend.none
    => extend.left

// Length Inputs
var array<int> Periods = periodMode ? array.from(na, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1050, 1100, 1150, 1200) : array.from(na, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200)

// Calculate deviations, correlation, slope, and intercepts for different lengths
[stdDev01, pearsonR01, slope01, intercept01] = calcDev(sourceInput, Periods.get(1))
[stdDev02, pearsonR02, slope02, intercept02] = calcDev(sourceInput, Periods.get(2))
[stdDev03, pearsonR03, slope03, intercept03] = calcDev(sourceInput, Periods.get(3))
[stdDev04, pearsonR04, slope04, intercept04] = calcDev(sourceInput, Periods.get(4))
[stdDev05, pearsonR05, slope05, intercept05] = calcDev(sourceInput, Periods.get(5))
[stdDev06, pearsonR06, slope06, intercept06] = calcDev(sourceInput, Periods.get(6))
[stdDev07, pearsonR07, slope07, intercept07] = calcDev(sourceInput, Periods.get(7))
[stdDev08, pearsonR08, slope08, intercept08] = calcDev(sourceInput, Periods.get(8))
[stdDev09, pearsonR09, slope09, intercept09] = calcDev(sourceInput, Periods.get(9))
[stdDev10, pearsonR10, slope10, intercept10] = calcDev(sourceInput, Periods.get(10))
[stdDev11, pearsonR11, slope11, intercept11] = calcDev(sourceInput, Periods.get(11))
[stdDev12, pearsonR12, slope12, intercept12] = calcDev(sourceInput, Periods.get(12))
[stdDev13, pearsonR13, slope13, intercept13] = calcDev(sourceInput, Periods.get(13))
[stdDev14, pearsonR14, slope14, intercept14] = calcDev(sourceInput, Periods.get(14))
[stdDev15, pearsonR15, slope15, intercept15] = calcDev(sourceInput, Periods.get(15))
[stdDev16, pearsonR16, slope16, intercept16] = calcDev(sourceInput, Periods.get(16))
[stdDev17, pearsonR17, slope17, intercept17] = calcDev(sourceInput, Periods.get(17))
[stdDev18, pearsonR18, slope18, intercept18] = calcDev(sourceInput, Periods.get(18))
[stdDev19, pearsonR19, slope19, intercept19] = calcDev(sourceInput, Periods.get(19))

if barstate.islast
    // Find the highest Pearson's R
    float highestPearsonR = math.max(pearsonR01, pearsonR02, pearsonR03, pearsonR04, pearsonR05, pearsonR06, pearsonR07, pearsonR08, pearsonR09, pearsonR10, pearsonR11, pearsonR12, pearsonR13, pearsonR14, pearsonR15, pearsonR16, pearsonR17, pearsonR18, pearsonR19)

    // Determine selected length, slope, intercept, and deviations
    int detectedPeriod = na
    float detectedSlope = na
    float detectedIntrcpt = na
    float detectedStdDev = na
    switch highestPearsonR
        pearsonR01 =>
            detectedPeriod := Periods.get(1)
            detectedSlope := slope01
            detectedIntrcpt := intercept01
            detectedStdDev := stdDev01
            detectedStdDev
        pearsonR02 =>
            detectedPeriod := Periods.get(2)
            detectedSlope := slope02
            detectedIntrcpt := intercept02
            detectedStdDev := stdDev02
            detectedStdDev
        pearsonR03 =>
            detectedPeriod := Periods.get(3)
            detectedSlope := slope03
            detectedIntrcpt := intercept03
            detectedStdDev := stdDev03
            detectedStdDev
        pearsonR04 =>
            detectedPeriod := Periods.get(4)
            detectedSlope := slope04
            detectedIntrcpt := intercept04
            detectedStdDev := stdDev04
            detectedStdDev
        pearsonR05 =>
            detectedPeriod := Periods.get(5)
            detectedSlope := slope05
            detectedIntrcpt := intercept05
            detectedStdDev := stdDev05
            detectedStdDev
        pearsonR06 =>
            detectedPeriod := Periods.get(6)
            detectedSlope := slope06
            detectedIntrcpt := intercept06
            detectedStdDev := stdDev06
            detectedStdDev
        pearsonR07 =>
            detectedPeriod := Periods.get(7)
            detectedSlope := slope07
            detectedIntrcpt := intercept07
            detectedStdDev := stdDev07
            detectedStdDev
        pearsonR08 =>
            detectedPeriod := Periods.get(8)
            detectedSlope := slope08
            detectedIntrcpt := intercept08
            detectedStdDev := stdDev08
            detectedStdDev
        pearsonR09 =>
            detectedPeriod := Periods.get(9)
            detectedSlope := slope09
            detectedIntrcpt := intercept09
            detectedStdDev := stdDev09
            detectedStdDev
        pearsonR10 =>
            detectedPeriod := Periods.get(10)
            detectedSlope := slope10
            detectedIntrcpt := intercept10
            detectedStdDev := stdDev10
            detectedStdDev
        pearsonR11 =>
            detectedPeriod := Periods.get(11)
            detectedSlope := slope11
            detectedIntrcpt := intercept11
            detectedStdDev := stdDev11
            detectedStdDev
        pearsonR12 =>
            detectedPeriod := Periods.get(12)
            detectedSlope := slope12
            detectedIntrcpt := intercept12
            detectedStdDev := stdDev12
            detectedStdDev
        pearsonR13 =>
            detectedPeriod := Periods.get(13)
            detectedSlope := slope13
            detectedIntrcpt := intercept13
            detectedStdDev := stdDev13
            detectedStdDev
        pearsonR14 =>
            detectedPeriod := Periods.get(14)
            detectedSlope := slope14
            detectedIntrcpt := intercept14
            detectedStdDev := stdDev14
            detectedStdDev
        pearsonR15 =>
            detectedPeriod := Periods.get(15)
            detectedSlope := slope15
            detectedIntrcpt := intercept15
            detectedStdDev := stdDev15
            detectedStdDev
        pearsonR16 =>
            detectedPeriod := Periods.get(16)
            detectedSlope := slope16
            detectedIntrcpt := intercept16
            detectedStdDev := stdDev16
            detectedStdDev
        pearsonR17 =>
            detectedPeriod := Periods.get(17)
            detectedSlope := slope17
            detectedIntrcpt := intercept17
            detectedStdDev := stdDev17
            detectedStdDev
        pearsonR18 =>
            detectedPeriod := Periods.get(18)
            detectedSlope := slope18
            detectedIntrcpt := intercept18
            detectedStdDev := stdDev18
            detectedStdDev
        =>  // pearsonR19
            detectedPeriod := Periods.get(19)
            detectedSlope := slope19
            detectedIntrcpt := intercept19
            detectedStdDev := stdDev19
            detectedStdDev

    var line upperLine = na
    var linefill upperFill = na
    var line baseLine = na
    var line lowerLine = na
    var linefill lowerFill = na

    // Calculate start and end price based on detected slope and intercept
    float startPrice = math.exp(detectedIntrcpt + detectedSlope * (detectedPeriod - 1))
    float endPrice = math.exp(detectedIntrcpt)

    int startAtBar = bar_index - detectedPeriod + 1
    var color ChannelColor = color.new(colorInput, channelTransparency)

    if na(baseLine)
        baseLine := line.new(startAtBar, startPrice, bar_index, endPrice, width = lineWidth, extend = EXTEND_STYLE, color = color.new(colorInputMidline, transpInput), style = midLineStyle == 'Dotted' ? line.style_dotted : midLineStyle == 'Dashed' ? line.style_dashed : line.style_solid)
        baseLine
    else
        line.set_xy1(baseLine, startAtBar, startPrice)
        line.set_xy2(baseLine, bar_index, endPrice)

    float upperStartPrice = startPrice * math.exp(devMultiplier * detectedStdDev)
    float upperEndPrice = endPrice * math.exp(devMultiplier * detectedStdDev)
    if na(upperLine)
        upperLine := line.new(startAtBar, upperStartPrice, bar_index, upperEndPrice, width = 1, extend = EXTEND_STYLE, color = ChannelColor, style = lineStyle1 == 'Dotted' ? line.style_dotted : lineStyle1 == 'Dashed' ? line.style_dashed : line.style_solid)
        upperLine
    else
        line.set_xy1(upperLine, startAtBar, upperStartPrice)
        line.set_xy2(upperLine, bar_index, upperEndPrice)
        line.set_color(upperLine, colorInput)

    float lowerStartPrice = startPrice / math.exp(devMultiplier * detectedStdDev)
    float lowerEndPrice = endPrice / math.exp(devMultiplier * detectedStdDev)
    if na(lowerLine)
        lowerLine := line.new(startAtBar, lowerStartPrice, bar_index, lowerEndPrice, width = 1, extend = EXTEND_STYLE, color = ChannelColor, style = lineStyle1 == 'Dotted' ? line.style_dotted : lineStyle1 == 'Dashed' ? line.style_dashed : line.style_solid)
        lowerLine
    else
        line.set_xy1(lowerLine, startAtBar, lowerStartPrice)
        line.set_xy2(lowerLine, bar_index, lowerEndPrice)
        line.set_color(lowerLine, colorInput)

    if na(upperFill)
        upperFill := linefill.new(upperLine, baseLine, color = color.new(colorInput, fillTransparency))
        upperFill
    if na(lowerFill)
        lowerFill := linefill.new(baseLine, lowerLine, color = color.new(colorInput, fillTransparency))
        lowerFill

    var table t = na
    if periodMode
        t := table.new(position.bottom_center, 2, 3)
        t
    else
        t := table.new(getTablePosition(tablePositionInput), 2, 3)
        t

    string text1 = periodMode ? 'Auto-Selected Period (Long Term): ' + str.tostring(detectedPeriod) : 'Auto-Selected Period: ' + str.tostring(detectedPeriod)
    var colorInputLight = color.new(colorInput, 0)

    // Display or hide the "Auto-Selected Period" cell
    if showAutoSelectedPeriod
        table.cell(t, 0, 0, text1, text_color = colorInputLight, text_size = textSizeInput == 'Large' ? size.large : textSizeInput == 'Small' ? size.small : size.normal)

    // Display or hide the "Trend Strength" or "Pearson's R" cell
    if showTrendStrength
        if showPearsonInput
            table.cell(t, 0, 1, 'Pearson\'s R: ' + str.tostring(detectedSlope > 0.0 ? -highestPearsonR : highestPearsonR, '#.###'), text_color = colorInput, text_size = textSizeInput == 'Large' ? size.large : textSizeInput == 'Small' ? size.small : size.normal)
        else
            table.cell(t, 0, 1, 'Trend Strength: ' + confidence(highestPearsonR), text_color = colorInput, text_size = textSizeInput == 'Large' ? size.large : textSizeInput == 'Small' ? size.small : size.normal)

    // Calculate CAGR
    float cagr = na
    if not na(detectedPeriod) and bar_index >= detectedPeriod and is_valid_timeframe()
        float num_of_periods = detectedPeriod
        float multiplier = get_tf_multiplier()
        float startClosePrice = close[detectedPeriod - 1]
        cagr := math.pow(close / startClosePrice, multiplier / num_of_periods) - 1
        cagr

    // Display or hide the "Trend Annualized Return" cell
    if showTrendAnnualizedReturn and is_valid_timeframe()
        table.cell(t, 0, 2, 'Trend Annualized Return: ' + (not na(cagr) ? str.tostring(cagr * 100, '#.#') + '%' : 'N/A'), text_color = colorInput, text_size = textSizeInput == 'Large' ? size.large : textSizeInput == 'Small' ? size.small : size.normal)

Overview

Operating on the most recent historical bar, it fits a log-transformed regression line and standard deviation bands. It evaluates linear correlation using Pearson's R to determine trend strength and select optimal period lengths automatically. The indicator draws trend channels and displays a customizable information table containing trend strength, correlation stats, and annualized return metrics.

Details

Published by

deepentropy on GitHub

Code credit

Julien_Eche, as written in the script's header

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Trend, Support & resistance, Screeners & dashboards

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

  • Signals can change on the live bar or after it closes (repainting)

Settings

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

SettingTypeDefaultVariable
SourcesourceclosesourceInput
Use Long-Term ChannelboolfalseperiodMode
Deviation Multiplier:float2.0devMultiplier
colorInputcolorcolor.graycolorInput
lineStyle1stringSolidlineStyle1
extendStylestringExtend RightextendStyle
Fill Transp:int93fillTransparency
Line Transp:int40channelTransparency
colorInputMidlinecolorcolor.bluecolorInputMidline
Transp:int100transpInput
Line Width:int1lineWidth
midLineStylestringDashedmidLineStyle
Show all 18 settings
SettingTypeDefaultVariable
Show Auto-Selected PeriodbooltrueshowAutoSelectedPeriod
Show Trend StrengthbooltrueshowTrendStrength
Show Pearson's RboolfalseshowPearsonInput
Show Trend Annualized ReturnbooltrueshowTrendAnnualizedReturn
Table PositionstringBottom RighttablePositionInput
Text SizestringNormaltextSizeInput
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

257 stars

This indicator overlays an intraday SPX and ES chart with a midnight session range, regular-trading-hours opening levels, and a VWAP projection box. It draws the midnight high, mid, and low, flags edge rejections within an absolute tolerance, and plots optiona

Indicator v6 TrendSupport & resistance
Updated 6 weeks ago · None stated GitHub repository of SPX Midnight Box + Snag Logic
53 stars

Apex Trend & Liquidity Master calculates a trend cloud using customizable moving averages expanded by a volatility multiplier directly on the main price chart. It identifies dynamic support and resistance liquidity zones from swing high and low pivots, extendi

Indicator v6 TrendSupport & resistance
Updated 2 months ago · MPL-2.0 GitHub repository of Apex Trend & Liquidity Master

Market Monitor - 1m

by chris-c-thomas
14 stars

Market Monitor - 1m is an overlay indicator designed for multi-chart monitoring and directional bias assessment. It plots a fast, mid, and slow EMA ribbon with a fill cloud, session VWAP with standard deviation bands, and previous day key levels. It evaluates

Indicator v6 TrendMoving averages Repaints
Updated 6 months ago · MIT GitHub repository of Market Monitor - 1m

5 EMAs

by hirawatt
11 stars

5 EMAs calculates and plots five Exponential Moving Averages derived from five configurable timeframes directly onto the main price chart. It also plots the high and low lines fetched from an additional custom timeframe, accompanied by a small on-screen table

Indicator v4 Moving averagesTrend Repaints
Updated 2 years ago · MIT GitHub repository of 5 EMAs
9 stars

Adaptive Supertrend is an overlay indicator that dynamically adjusts its Supertrend band width using a composite conviction score. Conviction is derived from ATR percentile ranking, trend force from EMA distance, and chop detection, narrowing the bands during

Indicator v6 TrendVolatility & bands
Updated yesterday · MIT GitHub repository of Adaptive Supertrend

Pro Pattern Al-Sat

by MeridianAlgo
9 stars

This overlay indicator marks bullish and bearish chart patterns on the price chart and can hide either side. Optional filters require agreement from fast and slow EMAs, Supertrend, and ADX, plus volume, RSI, nearby pivot support or resistance, and a user-defin

Indicator v6 Candlestick patternsTrend
Updated 4 months ago · MIT GitHub repository of Pro Pattern Al-Sat

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