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

//@version=5
indicator('Extended Parallel Channels', overlay = true, max_lines_count = 500)

const string ANCHORS_GROUP = 'Anchors'
int T1 = input.time(0, 'X1', confirm = true, inline = 'First Anchor', group = ANCHORS_GROUP, display = display.none)
float Y1 = input.price(0, 'Y1', confirm = true, inline = 'First Anchor', group = ANCHORS_GROUP, display = display.none)

int T2 = input.time(0, 'X2', confirm = true, inline = 'Second Anchor', group = ANCHORS_GROUP, display = display.none)
float Y2 = input.price(0, 'Y2', confirm = true, inline = 'Second Anchor', group = ANCHORS_GROUP, display = display.none)

int T3 = input.time(0, 'X3', confirm = true, inline = 'Third Anchor', group = ANCHORS_GROUP, display = display.none)
float Y3 = input.price(0, 'Y3', confirm = true, inline = 'Third Anchor', group = ANCHORS_GROUP, display = display.none)

const string QUANTITIES_GROUP = 'Quantities'
int NUM_LINES = input.int(10, 'Number of Lines to Extend in Each Direction', group = QUANTITIES_GROUP, display = display.none)
int DIVISION_FACTOR = input.int(1, 'Sub-Channel Division Factor',
  minval = 1,
  tooltip = 'Number of sub-channels to divide each channel into.',
  group = QUANTITIES_GROUP,
  display = display.none)

const string THEME_GROUP = 'Theme'
string STYLE_SOLID  = '⎯⎯'
string STYLE_DASHED = '- - -'
string STYLE_DOTTED = '·······'
string STYLE_STRING = input.string(STYLE_DASHED, 'Line Style',
  options = [
      STYLE_SOLID,
      STYLE_DASHED,
      STYLE_DOTTED
  ],
  group = THEME_GROUP,
  display = display.none)
string STYLE = switch STYLE_STRING
    STYLE_SOLID => line.style_solid
    STYLE_DASHED => line.style_dashed
    STYLE_DOTTED => line.style_dotted
color LINE_COLOR = input.color(#5d606b, title = 'Line Color', group = THEME_GROUP, display = display.none)
color ANCHOR_COLOR = input.color(#2962ffaa, 'Anchor Color', group = THEME_GROUP, display = display.none)
color BG_COLOR = input.color(#5d606b33, 'Background Color', group = THEME_GROUP, display = display.none)

// @function Highlights the anchor points so that it's easier to spot them in the chart.
// @param x The x coordinate (UNIX timestamp).
// @param y The y coordinate (price).
highlight(int x, float y) =>
    label.new(x, y, xloc = xloc.bar_time, style = label.style_circle, size = size.tiny, color = ANCHOR_COLOR)

// Converts the X coordinates from UNIX timestamp into bar_index.
// Using bar_index is necessary here since the index delta between adjacent bars is a constant (1), whereas the UNIX
// timestamp delta between adjacent bars can be variable, which can cause troubles in the slope calculation later.
var int x1 = na
var int x2 = na
var int x3 = na
if na(x1) and time >= T1
    x1 := bar_index
if na(x2) and time >= T2
    x2 := bar_index
if na(x3) and time >= T3
    x3 := bar_index

var plotted = false
if barstate.islast and not plotted
    plotted := true

    highlight(T1, Y1)
    highlight(T2, Y2)
    highlight(T3, Y3)

    int dx = x2 - x1
    float dy = Y2 - Y1

    if dx != 0
        // Calculate the slope and intercept using the line equation: y = kx + b.
        float k = dy / dx
        float b1 = Y1 - k * x1
        float b3 = Y3 - k * x3

        // Plot the channels
        float db = (b3 - b1) / DIVISION_FACTOR
        int num = NUM_LINES * DIVISION_FACTOR
        for i = -num to num + 1
            line.new(x1, Y1 + db * i, x2, Y2 + db * i, xloc.bar_index, extend.both, LINE_COLOR, STYLE)

        // Fill the background for the main channel.
        line a = line.new(x1, Y1, x2, Y2, xloc.bar_index, extend.both, #00000000)
        line b = line.new(x1, k * x1 + b3, x2, k * x2 + b3, xloc.bar_index, extend.both, #00000000)
        linefill.new(a, b, BG_COLOR)
