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

//@version=5

// @description This library calculates "Heikinashi".
library("Heikinashi", overlay=true)

export f_heikinashi_close (string _type, float _o, float _h, float _l, float _c) =>
    switch _type
        'close'     => _c
        'oc2'       => math.avg(_o, _c)
        'hl2'       => math.avg(_l, _l)
        'hlc3'      => math.avg(_h, _l, _c)
        'ohlc4'     => math.avg(_o, _h, _l, _c)
        'ohlc4-s2'  => math.avg(_o, ta.sma(_h, 2), ta.sma(_l, 2), _c)
        'ohlc4-s3'  => math.avg(_o, ta.sma(_h, 3), ta.sma(_l, 3), _c)
        'ohlc4-s4'  => math.avg(_o, ta.sma(_h, 4), ta.sma(_l, 4), _c)
        'ohlc4-s5'  => math.avg(_o, ta.sma(_h, 5), ta.sma(_l, 5), _c)
        'hlcc4'     => math.avg(_h, _l, _c, _c)
        'ohlcc5'    => math.avg(_h, _l, _c, _c, _o)
        => math.avg(_o, _h, _l, _c)

heikinashi (float _o, float _h, float _l, float _c, string _close_type = 'ohlc4') =>
    _hclose = f_heikinashi_close(_close_type, _o, _h, _l, _c)

    // Day1
    _hopen = ohlc4[1]

    // Day2
    if (not na(_hopen[1]))
        _hopen := math.avg(nz(_hopen[1]), nz(_hclose[1]))
    else if (not na(_hopen))
        _hopen := ohlc4[1]
    else
        _hopen := open

    [_hopen, _h, _l, _hclose]

heikinashi_for (float _o, float _h, float _l, float _c, string _close_type = 'ohlc4', int _times = 2) =>
    times = math.max(_times, 2)

    [_hopen, _, _, _hclose] = heikinashi(_o, _h, _l, _c, _close_type)

    if (not na(open[times-1])) and times > 1
        for i = 1 to times-1
            _hclose := f_heikinashi_close(_close_type, _hopen, _h, _l, _hclose)
            _hopen  := math.avg(_hopen[1], _hclose[1])

    [_hopen, _h, _l, _hclose]

heikinashi_for_v1 (float _o, float _h, float _l, float _c, string _close_type = 'ohlc4', int _times = 2) =>
    times = math.min(math.max(_times, 1), 10)

    [_open1 , _, _, _close1 ] = heikinashi(_o, _h, _l, _c, _close_type)
    [_open2 , _, _, _close2 ] = heikinashi(_open1, _h, _l, _close1, _close_type)
    [_open3 , _, _, _close3 ] = heikinashi(_open2, _h, _l, _close2, _close_type)
    [_open4 , _, _, _close4 ] = heikinashi(_open3, _h, _l, _close3, _close_type)
    [_open5 , _, _, _close5 ] = heikinashi(_open4, _h, _l, _close4, _close_type)
    [_open6 , _, _, _close6 ] = heikinashi(_open5, _h, _l, _close5, _close_type)
    [_open7 , _, _, _close7 ] = heikinashi(_open6, _h, _l, _close6, _close_type)
    [_open8 , _, _, _close8 ] = heikinashi(_open7, _h, _l, _close7, _close_type)
    [_open9 , _, _, _close9 ] = heikinashi(_open8, _h, _l, _close8, _close_type)
    [_open10, _, _, _close10] = heikinashi(_open9, _h, _l, _close9, _close_type)

    switch times
        1 => [_open1, high, low, _close1]
        2 => [_open2, high, low, _close2]
        3 => [_open3, high, low, _close3]
        4 => [_open4, high, low, _close4]
        5 => [_open5, high, low, _close5]
        6 => [_open6, high, low, _close6]
        7 => [_open7, high, low, _close7]
        8 => [_open8, high, low, _close8]
        9 => [_open9, high, low, _close9]
        10 => [_open10, high, low, _close10]

// @function This function calculates "Heikinashi".
// @param _o open
// @param _h high
// @param _l low
// @param _c close
// @param _close_type ['ohlc4', 'hlc3', 'oc2', 'hl2', 'close'] Sets the calculation method to be used for the closing price.
// @returns TODO: add what function returns
export calc (float _o, float _h, float _l, float _c, string _close_type = 'ohlc4') =>
	heikinashi(_o, _h, _l, _c, _close_type)

// @function This function calculates "Heikinashi".
// @param _o open
// @param _h high
// @param _l low
// @param _c close
// @param _close_type ['ohlc4', 'hlc3', 'oc2', 'hl2', 'close'] Sets the calculation method to be used for the closing price.
// @param _times Sets how many times to average.(min:1, max:10)
// @returns TODO: add what function returns
export calcFor (float _o, float _h, float _l, float _c, string _close_type = 'ohlc4', int _times = 1) =>
    heikinashi_for(_o, _h, _l, _c, _close_type, _times)

// @function This function calculates "Heikinashi".
// @param _o open
// @param _h high
// @param _l low
// @param _c close
// @param _close_type ['ohlc4', 'hlc3', 'oc2', 'hl2', 'close'] Sets the calculation method to be used for the closing price.
// @param _times Sets how many times to average.(min:1, max:10)
// @returns TODO: add what function returns
export calcFor_v1 (float _o, float _h, float _l, float _c, string _close_type = 'ohlc4', int _times = 1) =>
    heikinashi_for_v1(_o, _h, _l, _c, _close_type, _times)

///////////////
// Test suit //
length = input.int(2, minval=1, maxval=10)
close_type = input.string('ohlc4', options=['close', 'oc2', 'hl2', 'hlc3', 'ohlc4', 'ohlc4-s2', 'ohlc4-s3', 'ohlc4-s4', 'ohlc4-s5', 'hlcc4', 'ohlcc5'])
i_algo = input.string('v1', options=['v1', 'for'])
i_ma_length = input.int(30, 'MA period')

[plot_open, _, _, plot_close] = switch i_algo
    'v1'  => heikinashi_for_v1(open, high, low, close, close_type, length)
    'for' => heikinashi_for   (open, high, low, close, close_type, length)

c = plot_open > plot_close ? color.red : color.green

plotcandle(plot_open, high, low, plot_close, 'Heikinashi 2', color=plot_open > plot_close ? c : color.new(color.black, 100), wickcolor=c, bordercolor=c)
plot(ta.ema(close, i_ma_length), display=display.none)
