Linear Regression Channel Breakout Strategy
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © LonesomeTheBlue © chanu_lev10k

//@version=5
strategy('Linear Regression Channel Breakout Strategy', overlay=true)
src = input(defval=close, title='Source')
len = input.int(defval=20, title='Length', minval=10)
devlen = input.float(defval=2.3, title='Deviation', minval=0.1, step=0.1)
extendit = input(defval=true, title='Extend Lines')
showfibo = input(defval=false, title='Show Fibonacci Levels')
showbroken = input.bool(defval=true, title='Show Broken Channel', inline='brk')
brokencol = input.color(defval=color.blue, title='', inline='brk')
upcol = input.color(defval=color.lime, title='Up/Down Trend Colors', inline='trcols')
dncol = input.color(defval=color.red, title='', inline='trcols')
widt = input(defval=2, title='Line Width')
issl = input.bool(title='SL', inline='linesl1', group='Stop Loss / Take Profit:', defval=false)
slpercent = input.float(title=', %', inline='linesl1', group='Stop Loss / Take Profit:', defval=18.0, minval=0.0, step=0.1)
istp = input.bool(title='TP', inline='linetp1', group='Stop Loss / Take Profit:', defval=false)
tppercent = input.float(title=', %', inline='linetp1', group='Stop Loss / Take Profit:', defval=18.0, minval=0.0, step=0.1)

var fibo_ratios = array.new_float(0)
var colors = array.new_color(2)
if barstate.isfirst
    array.unshift(colors, upcol)
    array.unshift(colors, dncol)
    array.push(fibo_ratios, 0.236)
    array.push(fibo_ratios, 0.382)
    array.push(fibo_ratios, 0.618)
    array.push(fibo_ratios, 0.786)

get_channel(src, len) =>
    mid = math.sum(src, len) / len
    slope = ta.linreg(src, len, 0) - ta.linreg(src, len, 1)
    intercept = mid - slope * math.floor(len / 2) + (1 - len % 2) / 2 * slope
    endy = intercept + slope * (len - 1)
    dev = 0.0
    for x = 0 to len - 1 by 1
        dev += math.pow(src[x] - (slope * (len - x) + intercept), 2)
        dev
    dev := math.sqrt(dev / len)
    [intercept, endy, dev, slope]

[y1_, y2_, dev, slope] = get_channel(src, len)

outofchannel = slope > 0 and close < y2_ - dev * devlen ? 0 : slope < 0 and close > y2_ + dev * devlen ? 2 : -1

var reglines = array.new_line(3)
var fibolines = array.new_line(4)
for x = 0 to 2 by 1
    if not showbroken or outofchannel != x or nz(outofchannel[1], -1) != -1
        line.delete(array.get(reglines, x))
    else
        line.set_color(array.get(reglines, x), color=brokencol)
        line.set_width(array.get(reglines, x), width=2)
        line.set_style(array.get(reglines, x), style=line.style_dotted)
        line.set_extend(array.get(reglines, x), extend=extend.none)

    array.set(reglines, x, line.new(x1=bar_index - (len - 1), y1=y1_ + dev * devlen * (x - 1), x2=bar_index, y2=y2_ + dev * devlen * (x - 1), color=array.get(colors, math.round(math.max(math.sign(slope), 0))), style=x % 2 == 1 ? line.style_solid : line.style_dashed, width=widt, extend=extendit ? extend.right : extend.none))
if showfibo
    for x = 0 to 3 by 1
        line.delete(array.get(fibolines, x))
        array.set(fibolines, x, line.new(x1=bar_index - (len - 1), y1=y1_ - dev * devlen + dev * devlen * 2 * array.get(fibo_ratios, x), x2=bar_index, y2=y2_ - dev * devlen + dev * devlen * 2 * array.get(fibo_ratios, x), color=array.get(colors, math.round(math.max(math.sign(slope), 0))), style=line.style_dotted, width=widt, extend=extendit ? extend.right : extend.none))

var label sidelab = label.new(x=bar_index - (len - 1), y=y1_, text='S', size=size.large)
txt = slope > 0 ? slope > slope[1] ? '⇑' : '⇗' : slope < 0 ? slope < slope[1] ? '⇓' : '⇘' : '⇒'
stl = slope > 0 ? slope > slope[1] ? label.style_label_up : label.style_label_upper_right : slope < 0 ? slope < slope[1] ? label.style_label_down : label.style_label_lower_right : label.style_label_right
label.set_style(sidelab, stl)
label.set_text(sidelab, txt)
label.set_x(sidelab, bar_index - (len - 1))
label.set_y(sidelab, slope > 0 ? y1_ - dev * devlen : slope < 0 ? y1_ + dev * devlen : y1_)
label.set_color(sidelab, slope > 0 ? upcol : slope < 0 ? dncol : color.blue)

// Long Short conditions
longCondition = (slope < 0 and close > y2_ + dev * devlen) or (slope > 0 and close > y2_ + dev * devlen)
if longCondition
    strategy.entry('Long', strategy.long)
shortCondition = (slope > 0 and close < y2_ - dev * devlen) or (slope < 0 and close < y2_ - dev * devlen)
if shortCondition
    strategy.entry('Short', strategy.short)

// Entry price / Take Profit / Stop Loss
//entryprice = strategy.position_avg_price
entryprice = ta.valuewhen(condition=longCondition or shortCondition, source=close, occurrence=0)
pm = longCondition ? 1 : shortCondition ? -1 : 1 / math.sign(strategy.position_size)
takeprofit = entryprice * (1 + pm * tppercent * 0.01)
stoploss = entryprice * (1 - pm * slpercent * 0.01)
strategy.exit(id='Exit Long', from_entry='Long', stop=issl ? stoploss : na, limit=istp ? takeprofit : na, alert_message='Exit Long')
strategy.exit(id='Exit Short', from_entry='Short', stop=issl ? stoploss : na, limit=istp ? takeprofit : na, alert_message='Exit Short')