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

//@version=5

// @description Library of functions that manipulate a set of 5 MAs created within user-supplied maximum and minimum lengths. 
// The MAs are spaced out (within the range) in a way that approximates how Fibonnaci numbers are spaced.
// Using MA flow, as opposed to simple crosses of the minimum and maximum lengths, gives more detail, and can result in faster changes and more resistance to chop, depending how you use it.


library("EMAFlow", overlay=true)

in_maMinLength = input.int(defval=8, minval=4, maxval=50, title='Shortest MA Length', tooltip='The length of the shortest MA in the set. The middle three MAs are generated automatically. Must be 15 less than the maximum in order to generate a meaningful spread. If too close, the background is coloured red and a label indicates the error.')
in_maMaxLength = input.int(defval=34, minval=20, maxval=250, title='Longest MA Length', tooltip='The length of the longest MA in the set. The middle three MAs are generated automatically. Must be 15 more than the minimum in order to generate a meaningful spread. If too close, the background is coloured red and a label indicates the error.')

var string errorTextLibraryName = "EMAFlow"


// === Helper functions ===

f_ema(_source,_length) => _alpha = (2 / (_length + 1)), var float _ema = 0.0, _ema := na(_ema[1]) ? _source : _alpha * _source + (1 - _alpha) * nz(_ema[1]) 

f_source(_source) =>
    _output = switch _source
        "ohlc4" => ohlc4
        "open" => open
        "high" => high
        "low" => low
        "close" => close
        "hl2" => hl2
        "hlc3" => hlc3
        => na

// === Exported functions ===

// @function f_emaFlowBias: Gives a bullish or bearish bias reading based on the EMA flow from the user-supplied range.
// @param int _min: The minimum length of the EMA set.
// @param int _max: The maximum length of the EMA set.
// @param: float _source: The source for the EMA set. Supply the actual floating-point number.
// @returns: An integer, representing the bias: 1 is bearish, 2 is slightly bearish, 3 is neutral, 4 is slightly bullish, 5 is bullish.

export f_emaFlowBias(int _min=8, int _max=34, float _source=ohlc4) =>
    // Error-checking
    var string errorTextFunctionName = "f_emaFlowBias"
    if (_max - _min) < 15
        runtime.error("Max MA length must be at least 15 greater than min length [Error 001:" + " from function " + errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if _min < 4
        runtime.error("Min MA length cannot be less than 4. [Error 002:" + " from function " + errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if _min > 50
        runtime.error("Min MA length cannot be more than 50. [Error 003:" + " from function " + errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if _max > 200
        runtime.error("Max MA length cannot be more than 200. [Error 004:" + " from function " + errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    
    float _emaAdjustedRange = (_max - _min) / 55  // Because 8+13+21+34+55 = 5 Fib MAs
    float _ema1 = f_ema(_source, _min)
    float _ema2 = f_ema(_source, _min + int(9 * _emaAdjustedRange))
    float _ema3 = f_ema(_source, _min + int(21 * _emaAdjustedRange))
    float _ema4 = f_ema(_source, _min + int(34 * _emaAdjustedRange))
    float _ema5 = f_ema(_source, _max)
    float _rma = ta.rma(_source,2)
    
    if barstate.isfirst  // A way of ignoring the first bar (which can be useful to exclude strangeness on some charts) that doesn't give compilation warnings
        _ema1 := na, _ema2 := na, _ema3 := na, _ema4 := na, _ema5 := na, _rma := na

    bool _allEmasBearish = _ema1 < math.min(_ema2, _ema3, _ema4, _ema5) and _ema2 < math.min(_ema3, _ema4, _ema5) and _ema3 < math.min(_ema4, _ema5) and _ema4 < _ema5
    bool _allEmasBullish = _ema1 > math.max(_ema2, _ema3, _ema4, _ema5) and _ema2 > math.max(_ema3, _ema4, _ema5) and _ema3 > math.max(_ema4, _ema5) and _ema4 > _ema5

    _emasHowManyBearish = 0
    _emasHowManyBearish := _ema1 < math.min(_ema2, _ema3, _ema4, _ema5) ? _emasHowManyBearish + 1 : _emasHowManyBearish
    _emasHowManyBearish := _ema2 < math.min(_ema3, _ema4, _ema5) ? _emasHowManyBearish + 1 : _emasHowManyBearish
    _emasHowManyBearish := _ema3 < math.min(_ema4, _ema5) ? _emasHowManyBearish + 1 : _emasHowManyBearish
    _emasHowManyBearish := _ema4 < _ema5 ? _emasHowManyBearish + 1 : _emasHowManyBearish

    _emasHowManyBullish = 0
    _emasHowManyBullish := _ema1 > math.max(_ema2, _ema3, _ema4, _ema5) ? _emasHowManyBullish + 1 : _emasHowManyBullish
    _emasHowManyBullish := _ema2 > math.max(_ema3, _ema4, _ema5) ? _emasHowManyBullish + 1 : _emasHowManyBullish
    _emasHowManyBullish := _ema3 > math.max(_ema4, _ema5) ? _emasHowManyBullish + 1 : _emasHowManyBullish
    _emasHowManyBullish := _ema4 > _ema5 ? _emasHowManyBullish + 1 : _emasHowManyBullish

    bool _moreEmasBearish = _emasHowManyBearish > _emasHowManyBullish
    bool _moreEmasBullish = _emasHowManyBullish > _emasHowManyBearish

    int _bias = _allEmasBearish ? 1 : _allEmasBullish ? 5 : _moreEmasBearish and (_rma < _ema5) ? 2 : _moreEmasBullish and (_rma > _ema5) ? 4 : 3



// @function f_emaFlowBiasSource: Same as f_emaFlowBias except you can supply the source as a string.
// @param int _min: The minimum length of the EMA set.
// @param int _max: The maximum length of the EMA set.
// @param: string _source: The source for the EMA set. Must be in standard format (open, close, ohlc4, etc.)
// @returns: An integer, representing the bias: 1 is bearish, 2 is slightly bearish, 3 is neutral, 4 is slightly bullish, 5 is bullish.

export f_emaFlowBiasSource(int _min=8, int _max=34, string _source="ohlc4") =>
    // Error-checking
    var string errorTextFunctionName = "f_emaFlowBias"
    if (_max - _min) < 15
        runtime.error("Max MA length must be at least 15 greater than min length [Error 001:" + " from function " + errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if _min < 4
        runtime.error("Min MA length cannot be less than 4. [Error 002:" + " from function " + errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if _min > 50
        runtime.error("Min MA length cannot be more than 50. [Error 003:" + " from function " + errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if _max > 200
        runtime.error("Max MA length cannot be more than 200. [Error 004:" + " from function " + errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    
    _sourceFixed = f_source(_source)  // Needed because Pine only accepts typed inputs to library functions and string doesn't work as an EMA input and source isn't a type, so we're stuck with string and if anyone knows a better fix feel free to comment.
    if na(_sourceFixed)
        runtime.error("Source must be in standard format")

    float _emaAdjustedRange = (_max - _min) / 55  // Because 8+13+21+34+55 = 5 Fib MAs
    float _ema1 = f_ema(_sourceFixed, _min)
    float _ema2 = f_ema(_sourceFixed, _min + int(9 * _emaAdjustedRange))
    float _ema3 = f_ema(_sourceFixed, _min + int(21 * _emaAdjustedRange))
    float _ema4 = f_ema(_sourceFixed, _min + int(34 * _emaAdjustedRange))
    float _ema5 = f_ema(_sourceFixed, _max)
    float _rma = ta.rma(_sourceFixed,2)
    
    if barstate.isfirst  // A way of ignoring the first bar (which can be useful to exclude strangeness on some charts) that doesn't give compilation warnings
        _ema1 := na, _ema2 := na, _ema3 := na, _ema4 := na, _ema5 := na, _rma := na

    bool _allEmasBearish = _ema1 < math.min(_ema2, _ema3, _ema4, _ema5) and _ema2 < math.min(_ema3, _ema4, _ema5) and _ema3 < math.min(_ema4, _ema5) and _ema4 < _ema5
    bool _allEmasBullish = _ema1 > math.max(_ema2, _ema3, _ema4, _ema5) and _ema2 > math.max(_ema3, _ema4, _ema5) and _ema3 > math.max(_ema4, _ema5) and _ema4 > _ema5

    _emasHowManyBearish = 0
    _emasHowManyBearish := _ema1 < math.min(_ema2, _ema3, _ema4, _ema5) ? _emasHowManyBearish + 1 : _emasHowManyBearish
    _emasHowManyBearish := _ema2 < math.min(_ema3, _ema4, _ema5) ? _emasHowManyBearish + 1 : _emasHowManyBearish
    _emasHowManyBearish := _ema3 < math.min(_ema4, _ema5) ? _emasHowManyBearish + 1 : _emasHowManyBearish
    _emasHowManyBearish := _ema4 < _ema5 ? _emasHowManyBearish + 1 : _emasHowManyBearish

    _emasHowManyBullish = 0
    _emasHowManyBullish := _ema1 > math.max(_ema2, _ema3, _ema4, _ema5) ? _emasHowManyBullish + 1 : _emasHowManyBullish
    _emasHowManyBullish := _ema2 > math.max(_ema3, _ema4, _ema5) ? _emasHowManyBullish + 1 : _emasHowManyBullish
    _emasHowManyBullish := _ema3 > math.max(_ema4, _ema5) ? _emasHowManyBullish + 1 : _emasHowManyBullish
    _emasHowManyBullish := _ema4 > _ema5 ? _emasHowManyBullish + 1 : _emasHowManyBullish

    bool _moreEmasBearish = _emasHowManyBearish > _emasHowManyBullish
    bool _moreEmasBullish = _emasHowManyBullish > _emasHowManyBearish

    int _bias = _allEmasBearish ? 1 : _allEmasBullish ? 5 : _moreEmasBearish and (_rma < _ema5) ? 2 : _moreEmasBullish and (_rma > _ema5) ? 4 : 3


// ==============================  EXAMPLE USAGE  ==================================

int bias= f_emaFlowBias(_min=in_maMinLength, _max=in_maMaxLength, _source=ohlc4)

color bgColour = switch bias
    1 => color.new(color.red, 50)
    2 => color.new(color.red, 80)
    3 => na
    4 => color.new(color.green,80)
    5 => color.new(color.green,50)
bgcolor(bgColour, title="Background for bias", editable=false)

// END