'Possible to call the "Regression Trend" tool in pine script?

TradingView has this convenient Regression Trend tool in its UI, which can generate the trend channel for a specified period of time.

I'm trying to create a pine script to use this Regression Trends and auto generate trend channels for the past 1 month, 3 months, 6 months, then analyze their trend strength.

However, I have checked their Pine Script manualn and it doesn't seem like there is an API for me to call the tool in pine script.

My questions are,

  • Is it possible to call this "Regression Trend" tool using pine script?
  • If not possible, is there a way to generate trend channels and calculate the trend strength in Pine Script?

Please see the picture below for what I'm trying to accomplish. enter image description here



Solution 1:[1]

Answering my own question. The following script creates trend lines that are identical to the Regression Tool. Please note that the script works in Pine Script v5 env.

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

//@version=5
indicator("Linear regression trend lines", overlay=true)

// Config group 1 - Controls # of days to use for trendline calc and which data to use (open or close).
group1 = "LINEAR REGRESSION SETTING"
lengthInput = input.int(30, title="Length", minval = 1, maxval = 5000, group=group1)  // By default use 30 bars. Use 1 bar = 1 day then it's roughly 1 month ago.
sourceInput = input.source(close, title="Source", group=group1)

// Config groups 2 - Controls if a line is visible.
group2 = "LINEAR REGRESSION SWITCH"
useDev1Input = input.bool(true, title="Large LinReg On / Off", group=group2)
useDev2Input = input.bool(true, title="Medium LinReg On / Off", group=group2)
useDev3Input = input.bool(true, title="Small LinReg On / Off", group=group2)

useUpperDevInput = true
upperMultInput = 3.0
useLowerDevInput = true
lowerMultInput = 3.0
useUpperDevInput2 = true
upperMultInput2 = 2.0
useLowerDevInput2 = true
lowerMultInput2 = 2.0
useUpperDevInput3 = true
upperMultInput3 = 1.0
useLowerDevInput3 = true
lowerMultInput3 = 1.0

// Config group 3 - Controls if lines are extended longer on the left / right.
group3 = "LINEAR REGRESSION EXTEND LINES"
extendLeftInput = input.bool(false, "Extend Lines Left", group=group3)
extendRightInput = input.bool(true, "Extend Lines Right", group=group3)
extendStyle = switch
    extendLeftInput and extendRightInput => extend.both
    extendLeftInput => extend.left
    extendRightInput => extend.right
    => extend.none

// Config group 4 - Controls line width.
group4 = "LINEAR REGRESSION WIDTH"
a_lwidth = input.int(2, title='Large LinReg Width', minval=0, maxval=10, group=group4)
b_lwidth = input.int(1, title='Medium LinReg Width', minval=0, maxval=10, group=group4)
c_lwidth = input.int(1, title='Small LinReg Width', minval=0, maxval=10, group=group4)
d_lwidth = input.int(2, title='Base Line LinReg Width', minval=0, maxval=10, group=group4)

// Config group 5 - Controls line solid, dotted, dashed, etc.
group5 = "LINEAR REGRESSION STYLE"
A_LineStyle_Type = input.string(title='Large LinReg Type', defval='Dotted', options=['Dashed', 'Dotted', 'Solid'], group=group5)
A_Line_Type = A_LineStyle_Type == 'Dashed' ? line.style_dashed : A_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid
B_LineStyle_Type = input.string(title='Medium LinReg Type', defval='Solid', options=['Dashed', 'Dotted', 'Solid'], group=group5)
B_Line_Type = B_LineStyle_Type == 'Dashed' ? line.style_dashed : B_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid
C_LineStyle_Type = input.string(title='Small LinReg Type', defval='Dashed', options=['Dashed', 'Dotted', 'Solid'], group=group5)
C_Line_Type = C_LineStyle_Type == 'Dashed' ? line.style_dashed : C_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid
D_LineStyle_Type = input.string(title='Base Line LinReg Type', defval='Solid', options=['Dashed', 'Dotted', 'Solid'], group=group5)
D_Line_Type = D_LineStyle_Type == 'Dashed' ? line.style_dashed : D_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid

// Config group 6 - Controls line colors.
group6 = "LINEAR REGRESSION COLOR"
colorLarge = input.color(color.new(#ffffff, 0), "Large Linreg", inline=group6, group=group6)  // Outer line color default: white
colorMedium = input.color(color.new(#ff0000, 0), "Medium Linreg", inline=group6, group=group6)  // Middle line color defaul: red
colorSmall = input.color(color.new(#ffa500, 0), "Small Linreg", inline=group6, group=group6)  // Narror line color default: orange
colorBase = input.color(color.new(#0c3299, 0), "Base Line", inline=group6, group=group6)  // Baseline color default: blue

// Calculate slope based on input parameter & # of bars to use.
// (This slope calculation is genius.)
calcSlope(source, length) =>
    max_bars_back(source, 5000)
    // Check if current bar is last or we are only using 1 bar to calculate slope.
    if not barstate.islast or length <= 1
        [float(na), float(na), float(na)]
    else
        sumX = 0.0
        sumY = 0.0
        sumXSqr = 0.0
        sumXY = 0.0
        for i = 0 to length - 1 by 1
            val = source[i]
            per = i + 1.0
            sumX += per  // Sum of 1, 2, 3, ... until length
            sumY += val  // Sum of price value (default: close)
            sumXSqr += per * per
            sumXY += val * per
        slope = (length * sumXY - sumX * sumY) / (length * sumXSqr - sumX * sumX)
        average = sumY / length
        intercept = average - slope * sumX / length + slope
        [slope, average, intercept]
        
// Slope, average, intercept.
[s, a, i] = calcSlope(sourceInput, lengthInput)
    
// Calculate standard deviation.
calcDev(source, length, slope, average, intercept) =>
    upDev = 0.0
    dnDev = 0.0
    stdDevAcc = 0.0
    dsxx = 0.0
    dsyy = 0.0
    dsxy = 0.0
    periods = length - 1
    daY = intercept + slope * periods / 2
    val = intercept
    for j = 0 to periods by 1
        price = high[j] - val
        if price > upDev
            upDev := price
        price := val - low[j]
        if price > dnDev
            dnDev := price
        price := source[j]
        dxt = price - average
        dyt = val - daY
        price -= val
        stdDevAcc += price * price
        dsxx += dxt * dxt
        dsyy += dyt * dyt
        dsxy += dxt * dyt
        val += slope
    stdDev = math.sqrt(stdDevAcc / (periods == 0 ? 1 : periods))
    pearsonR = dsxx == 0 or dsyy == 0 ? 0 : dsxy / math.sqrt(dsxx * dsyy)
    [stdDev, pearsonR, upDev, dnDev]
    
[stdDev, pearsonR, upDev, dnDev] = calcDev(sourceInput, lengthInput, s, a, i)

// Draw trendlines based on returns of calcSlope and calcDev.
drawTrendlines(lengthInput, s, a, i, stdDev, pearsonR, upDev, dnDev) =>
    // linreg base function
    startPrice = i + s * (lengthInput - 1)
    endPrice = i
    var line baseLine = na
    if na(baseLine) and not na(startPrice)
        baseLine := line.new(bar_index - lengthInput + 1, startPrice, bar_index, endPrice, width=d_lwidth, style=D_Line_Type, extend=extendStyle, color=colorBase)
    else
        line.set_xy1(baseLine, bar_index - lengthInput + 1, startPrice)
        line.set_xy2(baseLine, bar_index, endPrice)
        na

    // large linreg function
    upperStartPrice = startPrice + (useUpperDevInput ? upperMultInput * stdDev : upDev)
    upperEndPrice = endPrice + (useUpperDevInput ? upperMultInput * stdDev : upDev)
    var line upper = na
    lowerStartPrice = startPrice + (useLowerDevInput ? -lowerMultInput * stdDev : -dnDev)
    lowerEndPrice = endPrice + (useLowerDevInput ? -lowerMultInput * stdDev : -dnDev)
    var line lower = na
    if na(upper) and not na(upperStartPrice)
        upper := useDev1Input ? line.new(bar_index - lengthInput + 1, upperStartPrice, bar_index, upperEndPrice, width=a_lwidth, style=A_Line_Type, extend=extendStyle, color=colorLarge) : na
    else
        line.set_xy1(upper, bar_index - lengthInput + 1, upperStartPrice)
        line.set_xy2(upper, bar_index, upperEndPrice)
        na
    if na(lower) and not na(lowerStartPrice)
        lower := useDev1Input ? line.new(bar_index - lengthInput + 1, lowerStartPrice, bar_index, lowerEndPrice, width=a_lwidth, style=A_Line_Type, extend=extendStyle, color=colorLarge) : na
    else
        line.set_xy1(lower, bar_index - lengthInput + 1, lowerStartPrice)
        line.set_xy2(lower, bar_index, lowerEndPrice)
        na

    // medium linreg function
    upperStartPrice2 = startPrice + (useUpperDevInput2 ? upperMultInput2 * stdDev : upDev)
    upperEndPrice2 = endPrice + (useUpperDevInput2 ? upperMultInput2 * stdDev : upDev)
    var line upper2 = na
    lowerStartPrice2 = startPrice + (useLowerDevInput2 ? -lowerMultInput2 * stdDev : -dnDev)
    lowerEndPrice2 = endPrice + (useLowerDevInput2 ? -lowerMultInput2 * stdDev : -dnDev)
    var line lower2 = na
    if na(upper2) and not na(upperStartPrice2)
        upper2 := useDev2Input ? line.new(bar_index - lengthInput + 1, upperStartPrice2, bar_index, upperEndPrice2, width=b_lwidth, style=B_Line_Type, extend=extendStyle, color=colorMedium) : na
    else
        line.set_xy1(upper2, bar_index - lengthInput + 1, upperStartPrice2)
        line.set_xy2(upper2, bar_index, upperEndPrice2)
        na
    if na(lower2) and not na(lowerStartPrice2)
        lower2 := useDev2Input ? line.new(bar_index - lengthInput + 1, lowerStartPrice2, bar_index, lowerEndPrice2, width=b_lwidth, style=B_Line_Type, extend=extendStyle, color=colorMedium) : na
    else
        line.set_xy1(lower2, bar_index - lengthInput + 1, lowerStartPrice2)
        line.set_xy2(lower2, bar_index, lowerEndPrice2)
        na

    // small linreg fuction
    upperStartPrice3 = startPrice + (useUpperDevInput3 ? upperMultInput3 * stdDev : upDev)
    upperEndPrice3 = endPrice + (useUpperDevInput3 ? upperMultInput3 * stdDev : upDev)
    var line upper3 = na
    lowerStartPrice3 = startPrice + (useLowerDevInput3 ? -lowerMultInput3 * stdDev : -dnDev)
    lowerEndPrice3 = endPrice + (useLowerDevInput3 ? -lowerMultInput3 * stdDev : -dnDev)
    var line lower3 = na
    if na(upper3) and not na(upperStartPrice3)
        upper3 := useDev3Input ? line.new(bar_index - lengthInput + 1, upperStartPrice3, bar_index, upperEndPrice3, width=c_lwidth, style=C_Line_Type, extend=extendStyle, color=colorSmall) : na
    else
        line.set_xy1(upper3, bar_index - lengthInput + 1, upperStartPrice3)
        line.set_xy2(upper3, bar_index, upperEndPrice3)
        na
    if na(lower3) and not na(lowerStartPrice3)
        lower3 := useDev3Input ? line.new(bar_index - lengthInput + 1, lowerStartPrice3, bar_index, lowerEndPrice3, width=c_lwidth, style=C_Line_Type, extend=extendStyle, color=colorSmall) : na
    else
        line.set_xy1(lower3, bar_index - lengthInput + 1, lowerStartPrice3)
        line.set_xy2(lower3, bar_index, lowerEndPrice3)
        na

    // Add tag to show slope to indicate strength of the trend.
    slopeLabel = label.new(bar_index - lengthInput + 1, upperStartPrice)
    label.set_text(slopeLabel, str.tostring(pearsonR))  // pearsonR is what is used in the official regression tool.

drawTrendlines(lengthInput, s, a, i, stdDev, pearsonR, upDev, dnDev)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ema