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

//@version=5
indicator("Sequence Distribution Report", max_lines_count=100)

import RicardoSantos/DebugConsole/8 as console

// @function generates a string with xyz spaces.
sp(int em=0, int en=0, int six_per_em=0) => //{
    string _s6_ = ' ' // six per em
    string _en_ = ' ' // en space
    string _em_ = ' ' // em space
    string _str = ''
    if em > 0
        for _i = 1 to em
            _str += _em_
    if en > 0
        for _i = 1 to en
            _str += _en_
    if six_per_em > 0
        for _i = 1 to six_per_em
            _str += _s6_
    _str
//}

var string _D01_ = 'CUM', var string _D02_ = 'ATR', var string _D03_ = 'STD'
string deviation_method = input.string(defval=_D01_, options=[_D01_, _D02_, _D03_], tooltip='Method to pool average deviation, cumulative, atr or stdev.')
int    deviation_length = input.int(defval=100, minval=1, tooltip='atr or stdev function length.')
// unit deviation, uses cumulative deviation average as base deviation unit, to
//  measure how many deviations the price changes on a sequence.
int unit_dev = 
  switch deviation_method
    _D01_ => math.round(math.abs(close - open) / (ta.cum(ta.tr) / (bar_index + 1)))
    _D02_ => math.round(math.abs(close - open) / ta.atr(deviation_length))
    _D03_ => math.round(math.abs(close - open) / ta.stdev(close, deviation_length))
    => math.round(math.abs(close - open) / ta.tr)
// plot(unit_dev)

int max_count = 1 + input.int(defval=20, maxval=50, tooltip='Limit of counted sequences.')
var int nsequences = 0
var array<int> upseq = array.new<int>(max_count, 0)
var array<int>doseq = array.new<int>(max_count, 0)

bool upbar = close > open
bool dobar = close < open

// up sequence counter
var int ucount = 0
if upbar 
    ucount += unit_dev
else if ucount > 0 and ucount < max_count
    array.set(upseq, ucount, array.get(upseq, ucount) + 1)
    nsequences += 1
    ucount := 0
else
    ucount := 0

// down sequence counter
var int dcount = 0
if dobar 
    dcount += unit_dev
else if dcount > 0 and dcount < max_count
    array.set(doseq, dcount, array.get(doseq, dcount) + 1)
    nsequences += 1
    dcount := 0
else
    dcount := 0

var array<line> ulines = array.new<line>(max_count)
var array<line> dlines = array.new<line>(max_count)
if barstate.isfirst
    
    for _i = 0  to max_count - 1
        array.set(ulines, _i, line.new(bar_index, 0.0, bar_index, 1.0, xloc.bar_index, extend.none, color.lime, width=2))
        array.set(dlines, _i, line.new(bar_index, 0.0, bar_index, 1.0, xloc.bar_index, extend.none, color.red, width=2))

_lvline(idx, uvalue, dvalue) =>
    int _idx1 = bar_index + idx * 3, _idx2 = _idx1 + 1
    line.set_x1(array.get(ulines, idx), _idx1)
    line.set_xy2(array.get(ulines, idx), _idx1, uvalue)
    line.set_x1(array.get(dlines, idx), _idx2)
    line.set_xy2(array.get(dlines, idx), _idx2, dvalue)

string date = str.format('{0, number, 0000} / {1, number, 00} / {2, number, 00}', year, month, dayofmonth)
var string start_date = date

int utotal = array.sum(upseq)
int dtotal = array.sum(doseq)

str = 'Distribution report:\n' + 
 str.format('Ticker : {0}, {1}\n', syminfo.tickerid, timeframe.period) + 
 str.format('Deviation method: {0} {1}\n', deviation_method, deviation_method != _D01_ ? str.tostring(deviation_length) : '') + 
 str.format('Start date: {0}\n', start_date) + 
 str.format('End date: {0}\n', date) + 
 str.format('Totals: {0} sequences in {1} bars\n', nsequences, bar_index) + 
 str.format('Deviation{0}|{1}UP{2}|{3}DOWN\n', sp(0,1), sp(6), sp(0,1,1), sp(2,1))

for _i = 1 to max_count-1
    int _u = array.get(upseq, _i), _d = array.get(doseq, _i)
    _lvline(_i, _u, _d)
    str += str.format('   {0, number, 00} |  {1, number, 000}({2, number, 00.00}%)  | {3, number, 000}({4, number, 00.00}%)\n', _i, _u, (_u/utotal)*100, _d, (_d/dtotal)*100)

if barstate.islastconfirmedhistory
    console.log(str)
// var _l = label.new(bar_index, 0.0, '', color=color.silver, style=label.style_diamond, tooltip=str)
// label.set_x(_l, bar_index)
// label.set_tooltip(_l, str)