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

//@version=5

// @description this library contains some utility functions that I use in my open source scripts including moving average helpers, candlstick helpers, money management, formatters, convertors, webhook integration, analysis, filters and drawing helpers
library("Antares", true)

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////      TRADINGVIEW OVERWRITTEN      ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

// @function Overwrites `ta.rma` duo to limitations of simple int length. Moving average used in RSI. It is the exponentially weighted moving average with alpha = 1 / length.
// @param length Number of bars (length).
// @param source Series of values to process.
// @returns Simple moving average of `source` for `length` bars back.
rma(int length = 14, float source = close) =>
	sma = ta.sma(source, length)
	alpha = 1/length
	sum = 0.0
	sum := na(sum[1]) ? sma : alpha * source + (1 - alpha) * nz(sum[1])

// @function Overwrites `ta.ema` duo to limitations of simple int length. The ema function returns the exponentially weighted moving average. In ema weighting factors decrease exponentially. It calculates by using a formula: EMA = alpha * source + (1 - alpha) * EMA[1], where alpha = 2 / (length + 1).
// @param length Number of bars (length).
// @param source Series of values to process.
// @returns Exponential moving average of `source` with alpha = 2 / (length + 1).
ema(int length = 14, float source = close) =>
	alpha = 2 / (length + 1)
	sum = 0.0
	sum := na(sum[1]) ? source : alpha * source + (1 - alpha) * nz(sum[1])

// @function Wraps all ma functions
// @param type Either SMA or EMA or RMA or WMA or VWMA
// @param length Number of bars (length).
// @param source Series of values to process.
// @returns Moving average of `source` for `length` bars back by the of MA.
export ma(string type = 'SMA', int length = 14, float source = close) =>
	if type != 'SMA' and type != 'EMA' and type != 'RMA' and type != 'WMA' and type != 'VWMA'
		runtime.error(type + ' is invalid. Accepted types are: ' + 'SMA, EMA, RMA, WMA, VWMA')
	_sma = ta.sma(source, length)
	_ema = ema(length, source)
	_rma = rma(length, source)
	_wma = ta.wma(source, length)
	_vwma = ta.vwma(source, length)
	switch type
		"SMA" => _sma
		"EMA" => _ema
		"RMA" => _rma
		"WMA" => _wma
		"VWMA" => _vwma

// @function Overwrites `ta.bb` duo to limitations of simple int.float mult. Bollinger Bands. A Bollinger Band is a technical analysis tool defined by a set of lines plotted two standard deviations (positively and negatively) away from a simple moving average (SMA) of the security's price, but can be adjusted to user preferences.
// @param ma Either SMA or EMA or RMA or WMA or VWMA
// @param length Number of bars (length).
// @param mult Standard deviation factor.
// @param source Series of values to process.
// @returns Bollinger Bands.
export bb(string ma = 'SMA', int length = 14, float mult = 2, float source = close) =>
    float basis = ma(ma, length, source)
    float dev = mult * ta.stdev(source, length)
    [basis, basis + dev, basis - dev]

// @function Overwrites `ta.atr` duo to limitations of simple int length. Function atr (average true range) returns the RMA of true range. True range is max(high - low, abs(high - close[1]), abs(low - close[1])).
// @param length Number of bars (length).
// @param h High price high price.
// @param l low price.
// @param c Close price close price.
// @returns Average true range.
export atr(int length = 14, float h = high, float l = low, float c = close) =>
	true_range = na(h[1]) ? h-l : math.max(math.max(h - l, math.abs(h - c[1])), math.abs(l - c[1]))
	rma(length, true_range)

// @function Overwrites `ta.rsi` duo to limitations of simple int length. Relative strength index. It is calculated using the `ta.rma()` of upward and downward changes of `source` over the last `length` bars.
// @param length Number of bars (length).
// @param source Series of values to process.
// @returns Relative strength index.
export rsi(int length = 14, float source = close) =>
	up = rma(length, math.max(ta.change(source), 0))
	down = rma(length, -math.min(ta.change(source), 0))
	down == 0 ? 100 : up == 0 ? 0 : 100 - (100 / (1 + up / down))

// @function Lowest value for a given number of bars back.
// @param length Number of bars (length).
// @param source Series of values to process.
// @param start Series number of bars that should be skipped before process.
// @returns Lowest value in the series.
export lowest(int length = 14, float source = low, int start = 0) =>
	result = source[start]
	for i = start to length + start - 1 by 1
		if source[i] < result
			result := source[i]
	result

// @function Highest value for a given number of bars back.
// @param length Number of bars (length).
// @param source Series of values to process.
// @param start Series number of bars that should be skipped before process.
// @returns Highest value in the series.
export highest(int length = 14, float source = high, int start = 0) =>
	result = source[start]
	for i = start to length + start - 1 by 1
		if source[i] > result
			result := source[i]
	result

// @function Turns source to a value between 0 and 100.
// @param source Series of values to process.
// @param length Number of bars (length).
// @returns A value between 0 and 100
export percentify(float source, int length = 200) =>
    highest = highest(length, source)
    lowest = lowest(length, source)
    (source - lowest) * 100 / (highest - lowest)

// @function Dynamic atr multiplier calculated by RSI.
// @param rsi Relative strength index.
// @param atr_min_multiplier The minimum multiplier of atr
// @param atr_max_multiplier The maximum multiplier of atr
// @returns Dynamic multiplier of ATR
export atr_multiplier(float rsi, float atr_min_multiplier = 1, float atr_max_multiplier = 3) =>
	if atr_min_multiplier > atr_max_multiplier
		runtime.error('atr_min_multiplier can not be greater than atr_max_multiplier')
	if atr_min_multiplier < 0
		runtime.error('atr_min_multiplier can not be negative')
	if atr_max_multiplier < 0
		runtime.error('atr_max_multiplier can not be negative')
	step = (atr_max_multiplier - atr_min_multiplier) / 20
	math.min(atr_min_multiplier + math.abs(50 - rsi) * step, atr_max_multiplier)

// @function Safe dynamic offset you need to use in your stoploss, stop buy/sell, etc.
// @param atr Average true range.
// @param atr_multiplier ATR multiplier got from `atr_multiplier(rsi, atr_max_multiplier)`
// @returns Dynamic offset
export offset(float atr, float atr_multiplier = 3) =>
	atr * atr_multiplier

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////              SIGNALS              ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

export type signal
    float score
    float max_score
    string status
    string name
    array<signal> subsignals

// @function Checks all required signals and calculates cumulative score and max score.
// @param signals Array of signals to be checked.
// @param status Either "REQUIRED" or "OPTIONAL" or "IGNORE".
// @param name Name of the signal
// @param side Either 1 or -1. 1 means "Long", -1 means "Short". If it was na, then side is in the same direct of first signal
// @returns A single signal calculated by all the signals passed to
export signals(array<signal> signals, string status = "Required", string name = "Final Signal", int side = na) =>
    first_signal = array.get(signals, 0)
    first_signal_score = na(first_signal) ? na : first_signal.score
    float _side = na(side) and not na(first_signal_score) ? first_signal_score : na
    _side := _side / math.abs(_side)
    if _side == 0
        runtime.error("arg side should be either 1 or -1 but got 0")
    int required = 0
    int passed_required = 0
    float score = 0
    float max_score = 0
    for i = 0 to array.size(signals) - 1 by 1
        s = array.get(signals, i)
        if na(s)
            continue
        same_direct = _side > 0 and s.score > 0 or _side < 0 and s.score < 0
        if str.upper(s.status) == "REQUIRED"
            required := required + 1
            if same_direct
                passed_required := passed_required + 1
        if str.upper(s.status) != "IGNORE"
            max_score := max_score + math.abs(s.max_score)
            if same_direct
                score := score + s.score
    if passed_required != required
        score := 0
    signal.new(score, max_score, status, name, signals)

// @function Checks usd dominance, total2btc, btcusdt, altusdt and altbtc to see if all of them are verifying the trend.
// @param symbol Ticker ID.
// @param usd Signal of USD dominance in crypto
// @param total2btc Signal of TOTAL2/BTC in crypto
// @param btcusd Signal of BTC/USD in crypto
// @param altusd Signal of XXX/USD in crypto
// @param altbtc Signal of XXX/BTC in crypto
// @returns Single trend signal
export multi_trend_signal(string symbol, signal usd, signal total2btc, signal btcusd, signal altusd, signal altbtc, float multiplier = 1) =>
    ticker = syminfo.ticker(symbol)
    is_btc = str.startswith(ticker, "BTC") and str.contains(ticker, "USD")
    is_alt = not str.startswith(ticker, "BTC") and str.contains(ticker, "USD")
    usd_score = na(usd) ? na : usd.score
    total2btc_score = na(total2btc) ? na : total2btc.score
    btcusd_score = na(btcusd) ? na : btcusd.score
    altusd_score = na(altusd) ? na : altusd.score
    altbtc_score = na(altbtc) ? na : altbtc.score
    score =
         is_btc and btcusd_score > 0 and total2btc_score < 0 and usd_score < 0 ? 3 :
         not is_alt ? 0 :
         btcusd_score > 0 and altusd_score > 0 and altbtc_score > 0 and total2btc_score > 0 ? 4 :
         btcusd_score > 0 and altusd_score > 0 and altbtc_score > 0 ? 3 :
         btcusd_score == 0 and altusd_score > 0 and altbtc_score > 0 and total2btc_score > 0 ? 2 :
         btcusd_score == 0 and altusd_score > 0 and altbtc_score > 0 ? 1 :
         btcusd_score < 0 and altusd_score < 0 and altbtc_score < 0 and total2btc_score < 0 ? -4 :
         btcusd_score < 0 and altusd_score < 0 and altbtc_score < 0 ? -3 :
         btcusd_score == 0 and altusd_score < 0 and altbtc_score < 0 and total2btc_score < 0 ? -2 :
         btcusd_score == 0 and altusd_score < 0 and altbtc_score < 0 ? -1 :
         0

    signal.new(score * multiplier, 4 * multiplier, "Required", "Multi Trend Signal")

// @function Tells you if RSI is in emotional zone.
// @param rsi Relative Strength Index
// @param bottom The zone that below it market reacts emotionally
// @param top The zone that above it market reacts emotionally
// @returns false if RSI was between `bottom` and `top` otherwise true
export rsi_emotional(float rsi, float bottom = 30, float top = 70) => rsi < bottom or top < rsi

// @function Tells you if RSI is in good point to check your other strategy conditions.
// @param rsi Relative Strength Index
// @param bottom The zone that below it market reacts emotionally
// @param top The zone that above it market reacts emotionally
// @returns 1 if RSI crossed out 30, 50 or 70. -1 if RSI crossed under 70, 50, 30. otherwise is 0
export rsi_score(float rsi, float bottom = 30, float top = 70) =>
	middle = (bottom + top) / 2
	rsi[1] < bottom and rsi > bottom or rsi[1] < middle and rsi > middle or rsi[1] < top and rsi > top ? 1 :
	 rsi[1] > bottom and rsi < bottom or rsi[1] > middle and rsi < middle or rsi[1] > top and rsi < top ? -1 :
	 0

// @function Tells you if slope of RSI is either ascending or descending
// @param rsi Relative Strength Index
// @param min_offset When RSI is either 0 or 100, offset is min_offset
// @param max_offset When RSI is 50, offset is max_offset
// @param offset_check Number of last candles that their offsets should be checked. It should be between 1 and 3
export slope_score(float rsi, float min_offset = 0.01, float max_offset = 5, int offset_check = 3) =>
    if offset_check < 1 or offset_check > 3
        runtime.error("offset_check should be either 1, 2 or 3")
    float step = (max_offset - min_offset) / 50
    float offset = math.max(max_offset - math.abs(50 - rsi) * step, min_offset)
    int offset_signal = rsi > rsi[1] + offset ? 1 : rsi < rsi[1] - offset ? -1 : 0
    switch offset_check
        1 => offset_signal
        2 => offset_signal > 0 and offset_signal[1] > 0 ? 1 :
             offset_signal < 0 and offset_signal[1] < 0 ? -1 : 0
        3 => offset_signal > 0 and offset_signal[1] > 0 and offset_signal[2] > 0 ? 1 :
             offset_signal < 0 and offset_signal[1] < 0 and offset_signal[2] < 0 ? -1 : 0

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////       CANDLESTICK PATTERNS        ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

// @function
// @param o Open price
// @param c Close price
// @returns
export body_high(float o = open, float c = close) => c >= o ? c : o

// @function
// @param o Open price
// @param c Close price
// @returns
export body_low(float o = open, float c = close) => c <= o ? c : o

// @function
// @param o Open price
// @param c Close price
// @returns
export is_green(float o = open, float c = close) => o < c

// @function
// @param o Open price
// @param c Close price
// @returns
export is_red(float o = open, float c = close) => o > c

// @function
// @param o Open price
// @param c Close price
// @param mintick Min tick value for the symbol.
// @returns
export body_size(float o = open, float c = close, float mintick = syminfo.mintick) => math.abs(c - o) / mintick

// @function
// @param o Open price
// @param h High price
// @param l Low price
// @param c Close price
// @returns
export body_percent(float o = open, float h = high, float l = low, float c = close) => math.abs(c - o) / (h - l)

// @function
// @param o Open price
// @param h High price
// @param c Close price
// @param mintick Min tick value for the symbol.
// @returns
export top_wick_size(float o = open, float h = high, float c = close, float mintick = syminfo.mintick) => math.abs(h - body_high(o, c)) / mintick

// @function
// @param o Open price
// @param h High price
// @param l Low price
// @param c Close price
// @returns
export top_wick_percent(float o = open, float h = high, float l = low, float c = close) => (h - body_high(o, c)) / (h - l)

// @function
// @param o Open price
// @param l Low price
// @param c Close price
// @param mintick Min tick value for the symbol.
// @returns
export bottom_wick_size(float o = open, float l = low, float c = close, float mintick = syminfo.mintick) => math.abs(l - body_low(o, c)) / mintick

// @function
// @param o Open price
// @param h High price
// @param l Low price
// @param c Close price
// @returns
export bottom_wick_percent(float o = open, float h = high, float l = low, float c = close) => (body_low(o, c) - l) / (h - l)

// @function
// @param o Open price
// @param h High price
// @param l Low price
// @param c Close price
// @returns
export wick_percent(float o = open, float h = high, float l = low, float c = close) => (top_wick_percent(o, h, l, c) + bottom_wick_percent(o, h, l, c)) / (h - l)

// @function
// @param o Open price
// @param h High price
// @param l Low price
// @param c Close price
// @param fib Fibonachi level
// @param c Close price
// @param color_match If true then the color of the candle will be checked
// @returns
export is_hammer(float o = open, float h = high, float l = low, float c = close, float fib = 0.382, bool color_match = false) =>
	bull_fib = (l - h) * fib + h
	body_low(o, c) >= bull_fib and (not color_match or is_green(o, c))

// @function
// @param o Open price
// @param h High price
// @param l Low price
// @param c Close price
// @param fib Fibonachi level
// @param color_match If true then the color of the candle will be checked
// @returns
export is_shooting_star(float o = open, float h = high, float l = low, float c = close, float fib = 0.382, bool color_match = false) =>
	bear_fib = (h - l) * fib + l
	body_high(o, c) <= bear_fib and (not color_match or is_red(o, c))

// @function
// @param o Open price
// @param h High price
// @param l Low price
// @param c Close price
// @param max_body_percent Max percentage of candle body
// @param wick_size Size of wick
// @returns
export is_doji(float o = open, float h = high, float l = low, float c = close, float max_body_percent = 0.05, float wick_size = 2.0) =>
	 top_wick_percent(o, h, l, c) <= bottom_wick_percent(o, h, l, c) * wick_size
	 and bottom_wick_percent(o, h, l, c) <= top_wick_percent(o, h, l, c) * wick_size
	 and body_percent(o, h, l, c) <= max_body_percent

// @function
// @param offset Safe dynamic offset you need to use in your stoploss, stop buy/sell, etc.
// @param o Open price
// @param c Close price
// @returns
export is_whalish(float offset, float o = open, float c = close) => offset < math.abs(o - c)

// @function
// @param o Open price
// @param h High price
// @param l Low price
// @param c Close price
// @param allowed_gap How much gap is allowed
// @param allowed_rejection_wick Max ratio of top wick size on body size
// @param engulf_wick Should it engulf wick of previous candle?
// @returns
export is_bullish_engulfing(float o = open, float h = high, float l = low, float c = close, float allowed_gap = 0, float allowed_rejection_wick = 0, bool engulf_wick = false) =>
	 c[1] <= o[1]
	 and c >= o[1]
	 and o <= c[1] + allowed_gap
	 and (not engulf_wick or c >= h[1])
	 and (allowed_rejection_wick == 0 or top_wick_percent(o, h, l, c) / body_percent(o, h, l, c) <= allowed_rejection_wick)

// @function
// @param o Open price
// @param h High price
// @param l Low price
// @param c Close price
// @param allowed_gap How much gap is allowed
// @param allowed_rejection_wick Max ratio of bottom wick size on body size
// @param engulf_wick Should it engulf wick of previous candle?
// @returns
export is_bearish_engulfing(float o = open, float h = high, float l = low, float c = close, float allowed_gap = 0, float allowed_rejection_wick = 0, bool engulf_wick = false) =>
	 c[1] >= o[1]
	 and c <= o[1]
	 and o >= c[1] - allowed_gap
	 and (not engulf_wick or c <= l[1])
	 and (allowed_rejection_wick == 0 or bottom_wick_percent(o, h, l, c) / body_percent(o, h, l, c) <= allowed_rejection_wick)

// @function
// @param length Number of bars (length).
// @param o Open price
// @param c Close price
// @param start Series number of bars that should be skipped before process.
// @returns
export bullish_bars(int length, float o = open, float c = close, int start = 0) =>
	counter = 0
	for i = start to length + start - 1 by 1
		if o[i] < c[i]
			counter := counter + 1
	counter

// @function
// @param length Number of bars (length).
// @param o Open price
// @param c Close price
// @param start Series number of bars that should be skipped before process.
// @returns
export bearish_bars(int length, float o = open, float c = close, int start = 0) =>
	counter = 0
	for i = start to length + start - 1 by 1
		if o[i] > c[i]
			counter := counter + 1
	counter

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////      MOVING AVERAGE HELPERS       ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

// @function
// @param light_source MA with shorter length
// @param heavy_source MA with longer length
// @param reverse If true then downtrend chart means the price is bullish
// @returns 1 if light_source crossed over heavy_source, -1 if crossed under heavy_source, otherwise 0
export trend(float light_source, float heavy_source, bool reverse = false) => (reverse ? 1 : -1) * (light_source > heavy_source ? 1 : light_source < heavy_source ? -1 : 0)

// @function
// @param length Number of bars (length).
// @param l Low price
// @param h High price
// @returns
export donchian(int length = 14, float l = low, float h = high) => math.avg(lowest(length, l), highest(length, h))

// @function
// @param l Low price
// @param h High price
// @param length Number of bars (length).
// @returns
export tenkansen(float l = low, float h = high, int length = 9) => donchian(length, l, h)

// @function
// @param l Low price
// @param h High price
// @param length Number of bars (length).
// @returns
export kijunsen(float l = low, float h = high, int length = 26) => donchian(length, l, h)

// @function
// @param tenkansen Conversion Line, is the mid-point of the highest and lowest prices of an asset over the last nine periods.
// @param kijunsen Base line, is an indicator and important component of the Ichimoku Kinko Hyo method of technical analysis
// @returns
export senkou_a(float tenkansen, float kijunsen) => math.avg(tenkansen, kijunsen)

// @function
// @param l Low price
// @param h High price
// @param length Number of bars (length).
// @returns
export senkou_b(float l = low, float h = high, int length = 52) => donchian(length, l, h)

// @function
// @param heavy_source MA with longer length
// @param light_source MA with shorter length
// @param full_enter False means that previous source can be equal or lower than current one, otherwise should be lower
// @param full_cross False means that current source can be equal or greater than previous one, otherwise should be greater
// @returns
export crossover(float heavy_source, float light_source = close, bool full_enter = true, bool full_cross = true) =>
	(full_enter ? light_source[1] < heavy_source[1] : light_source[1] <= heavy_source[1])
	 and (full_cross ? light_source > heavy_source : light_source >= heavy_source)

// @function
// @param heavy_source MA with longer length
// @param light_source MA with shorter length
// @param full_enter False means that previous source can be equal or lower than current one, otherwise should be lower
// @param full_cross False means that current source can be equal or greater than previous one, otherwise should be greater
// @returns
export crossunder(float heavy_source, float light_source = close, bool full_enter = true, bool full_cross = true) =>
	(full_enter ? light_source[1] > heavy_source[1] : light_source[1] >= heavy_source[1])
	 and (full_cross ? light_source < heavy_source : light_source <= heavy_source)

// @function
// @param heavy_source MA with longer length
// @param light_source MA with shorter length
// @param full_enter False means that previous source can be equal or lower than current one, otherwise should be lower
// @param full_cross False means that current source can be equal or greater than previous one, otherwise should be greater
// @returns
export cross(float heavy_source, float light_source = close, bool full_enter = true, bool full_cross = true) =>
	crossover(heavy_source, light_source, full_enter, full_cross)
	 or crossunder(heavy_source, light_source, full_enter, full_cross)

// @function
// @param line Horizontal line that source will be checked with
// @param length Number of bars (length).
// @param source Series of values to process
// @param start Series number of bars that should be skipped before process.
// @returns
export above_line(float line, int length = 14, float source = close, int start = 0) =>
	counter = 0
	for i = start to length + start - 1 by 1
		if source[i] > line
			counter := counter + 1
	counter

// @function
// @param line Horizontal line that source will be checked with
// @param length Number of bars (length).
// @param source Series of values to process
// @param start Series number of bars that should be skipped before process.
// @returns
export below_line(float line, int length = 14, float source = close, int start = 0) =>
	counter = 0
	for i = start to length + start - 1 by 1
		if source[i] < line
			counter := counter + 1
	counter

// @function
// @param line Horizontal line that source will be checked with
// @param length Number of bars (length).
// @param source Series of values to process
// @param start Series number of bars that should be skipped before process.
// @returns
export crossover_line(float line, int length = 14, float source = close, int start = 0) =>
	counter = 0
	for i = start to length + start - 1 by 1
		if source[i + 1] < line and source[i] > line
			counter := counter + 1
	counter

// @function
// @param line Horizontal line that source will be checked with
// @param length Number of bars (length).
// @param source Series of values to process
// @param start Series number of bars that should be skipped before process.
// @returns
export crossunder_line(float line, int length = 14, float source = close, int start = 0) =>
	counter = 0
	for i = start to length + start - 1 by 1
		if source[i + 1] > line and source[i] < line
			counter := counter + 1
	counter

// @function
// @param line Horizontal line that source will be checked with
// @param length Number of bars (length).
// @param source Series of values to process
// @param start Series number of bars that should be skipped before process.
// @returns
export cross_line(float line, int length = 14, float source = close, int start = 0) => crossover_line(line, length, source, start) + crossunder_line(line, length, source, start)

// @function
// @param line Horizontal line that source will be checked with
// @param length Number of bars (length).
// @param o Open price
// @param c Close price
// @param start Series number of bars that should be skipped before process.
// @returns
export bars_crossover_line(float line, int length = 14, float o = open, float c = close, int start = 0) =>
	counter = 0
	for i = start to length + start - 1 by 1
		if o[i] < line and c[i] > line
			counter := counter + 1
	counter

// @function
// @param line Horizontal line that source will be checked with
// @param length Number of bars (length).
// @param o Open price
// @param c Close price
// @param start Series number of bars that should be skipped before process.
// @returns
export bars_crossunder_line(float line, int length = 14, float o = open, float c = close, int start = 0) =>
	counter = 0
	for i = start to length + start - 1 by 1
		if o[i] > line and c[i] < line
			counter := counter + 1
	counter

// @function
// @param line Horizontal line that source will be checked with
// @param length Number of bars (length).
// @param o Open price
// @param c Close price
// @param start Series number of bars that should be skipped before process.
// @returns
export bars_cross_line(float line, int length = 14, float o = open, float c = close, int start = 0) => bars_crossover_line(line, length, o, c, start) + bars_crossunder_line(line, length, o, c, start)

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////              ANALAYSIS            ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

// @function
// @param source_start The value of the source in the beginning of the change
// @param source_end The value of the source in the end of the change
// @returns pecent of change
export size_percent(float source_start, float source_end) => (source_end / source_start) - 1

// @function
// @param amount The size of the asset/coin (not in base currency) for a position.
// @param leverage The proportion of your trade that will be paid for with borrowed funds. If you are using 2x leverage, you will be funding half of the trade. If you are using 25x leverage you will be funding 1/25 of the trade.
// @returns
export margin(float amount, int leverage = 1) => amount / leverage

// @function
// @param exit The price at which you exit your trade. Can be referred to as your take profit or "TP".
// @param base_qty The size, or quantity, of the asset/coin (not currency) for this position.
// @param short A selling position that enables a trader to profit if the price of an asset decreases.
// @param entry The price at which you enter the trade.
// @returns
export pnl(float exit, float base_qty, bool short, float entry = close) => base_qty * (short ? (entry - exit) : (exit - entry))

// @function
// @param risked_capital What you stand to lose if given stoploss is reached.
// @param pnl Profit and Loss. Shows what you stand to gain or lose with the inputs provided.
// @returns
export risk_reward(float risked_capital, float pnl) => pnl / risked_capital

// @function
// @param pnl Profit and Loss. Shows what you stand to gain or lose with the inputs provided.
// @param margin Margin is the portion of your own funds that you put into a trade. Keep in mind that depending on what margin mode you are using, your losses may not be limited to this margin.
// @returns
export roe(float pnl, float margin) => (pnl / margin) * 100

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////         MONEY MANAGEMENT          ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

export type mm
    float entry
    float sl
    float max_risk_percent
    float risked_capital
    float risked_percent
    float sl_size
    float sl_size_percent
    float margin
    float margin_percent
    float base_qty
    float quote_qty

export type tp
    float ror
    float limit
    float base_qty

// @function the value of `number` percentified to with precision 2 by default
// @param number The value to be percentified.
// @param precision Optional argument. Decimal places to which `number` will be rounded. When no argument is supplied, rounding is to the nearest integer.
// @returns The value of `number` percentified according to precision.
export percent(float number, int precision = 2) => math.round(number * 100, precision)

// @function
// @param sl_size Comes dynamically from offset(atr, atr_multiplier). But you can use any offset algorithm you want.
// @param o Open price
// @param c Close price
// @returns
export sl(float sl_size, float o = open, float c = close) => is_green(o, c) ? c - sl_size : c + sl_size

// @function
// @param sl A price level you can set on a position which will, once reached, close the position and prevent any further loss.
// @param entry The price at which you enter the trade.
// @returns SL size in minticks
export sl_size(float sl, float entry) => math.abs(sl - entry)

// @function
// @param risked_capital What you stand to lose if given stoploss is reached.
// @param sl_size Comes dynamically from offset(atr, atr_multiplier). But you can use any offset algorithm you want.
// @returns
export base_qty(float risked_capital, float sl_size) => math.abs(risked_capital / sl_size)

// @function
// @param base_qty The size, or quantity, of the asset/coin (not in quote currency) for a position.
// @param entry The price at which you enter the trade.
// @returns
export quote_qty(float base_qty, float entry = close) => base_qty * entry

// @function
// @param sl_size Comes dynamically from offset(atr, atr_multiplier). But you can use any offset algorithm you want.
// @param capital The total amount of capital in your trading account.
// @param leverage The proportion of your trade that will be paid for with borrowed funds. If you are using 2x leverage, you will be funding half of the trade. If you are using 25x leverage you will be funding 1/25 of the trade.
// @param max_risk_percent Max margin
// @param entry The price at which you enter the trade.
// @returns
export max_risk_percent(float sl_size, float capital, int leverage = 1, float max_risk_percent = 0.99, float entry = close) => math.abs(capital * max_risk_percent * leverage * sl_size) / (entry * capital)

// @function
// @param risk The percentage of your total capital you are willing to risk in this trade.
// @param max_risk_percent The maximum risk that your margin allows you to do.
// @param capital The total amount of capital in your trading account.
// @param dynamic If true, reduces risked capital based on max risk
// @returns
export risked_capital(float risk_percent, float max_risk_percent, float capital, bool dynamic = false) => capital * (risk_percent <= max_risk_percent ? risk_percent : dynamic ? max_risk_percent : 0)

// @function
// @param risked_capital What you stand to lose if given stoploss is reached.
// @param capital The total amount of capital in your trading account.
// @returns
export risked_percent(float risked_capital, float capital) => risked_capital / capital

// @function Calculates everything related to money management
// @param sl A price level you can set on a position which will, once reached, close the position and prevent any further loss.
// @param capital The total amount of capital in your trading account.
// @param risk_percent The percentage of your total capital you are willing to risk in this trade.
// @param entry The price at which you enter the trade.
// @param leverage The proportion of your trade that will be paid for with borrowed funds. If you are using 2x leverage, you will be funding half of the trade. If you are using 25x leverage you will be funding 1/25 of the trade.
// @param max_margin Max margin percentage
// @returns mm object
export money_management(float sl, float capital, float risk_percent, float entry = close, int leverage = 20, float max_margin = 1) =>
    float sl_size = entry - sl
    float max_risk_percent = max_risk_percent(sl_size, capital, leverage, max_margin, entry)
    float risked_capital = risked_capital(risk_percent, max_risk_percent, capital, true)
    float risked_percent = risked_percent(risked_capital, capital)
    float base_qty = base_qty(risked_capital, sl_size)
    float quote_qty = quote_qty(base_qty, entry)
    float margin = margin(quote_qty, leverage)
    float margin_percent = margin / capital
    mm.new(entry = entry,
         sl = sl,
         max_risk_percent = max_risk_percent,
         risked_capital = risked_capital,
         risked_percent = risked_percent,
         sl_size = sl_size,
         sl_size_percent = size_percent(entry, sl),
         margin = margin,
         margin_percent = margin_percent,
         base_qty = base_qty,
         quote_qty = quote_qty)

// @function
// @param mm Money Management onject
// @param limit The price that position should be closed at.
// @param percent The percentage of quantity you're going to take as profit, when the target ROR was reached.
// @param enabled True when you dont want set tp
// @returns tp object
export tp_by_limit(mm mm, float limit, float percent = 1, bool enabled = true) =>
    float tp_base_qty = enabled and not na(limit) ? percent * mm.base_qty : na
    float ror = not na(tp_base_qty) ? (limit - mm.entry) / mm.sl_size : na
    tp.new(ror = ror,
         limit = limit,
         base_qty = tp_base_qty)

// @function
// @param mm Money Management onject
// @param ror The ratio of potential profit of the trade to its potential loss.
// @param percent The percentage of quantity you're going to take as profit, when the target ROR was reached.
// @param enabled True when you dont want set tp
// @returns tp object
export tp_by_ror(mm mm, float ror, float percent = 1, bool enabled = true) =>
	float tp_limit = ror > 0 ? mm.entry + mm.sl_size * ror : na
	tp_limit := enabled and tp_limit > 0 ? tp_limit : na
    tp_by_limit(mm, tp_limit, percent, enabled)

// @function Handles risk management for your strategies
// @param signal Signal object
// @param full_risk_percent What should be risk percent at it's maximum value?
// @param full_risk_min_ratio What should be minimum of signal score ratio to use full_risk_percent
// @param half_risk_min_ratio What should be minimum of signal score ratio to use half_risk_percent
// @param quarter_risk_min_ratio What should be minimum of signal score ratio to use quarter_risk_percent
// @param one_eight_risk_min_ratio What should be minimum of signal score ratio to use one_eight_risk_percent
// @returns risk_percent arg of money_mangament function
export risk(signal signal, float full_risk_percent, float full_risk_min_ratio = 0.9, float half_risk_min_ratio = 0.7, float quarter_risk_min_ratio = 0.5, float one_eight_risk_min_ratio = 0.3) =>
    float ratio = math.abs(signal.score / signal.max_score)
    if ratio >= full_risk_min_ratio
        full_risk_percent
    else if ratio >= half_risk_min_ratio
        full_risk_percent / 2
    else if ratio >= quarter_risk_min_ratio
        full_risk_percent / 4
    else if ratio >= one_eight_risk_min_ratio
        full_risk_percent / 8
    else
        0

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////             FORMATTERS            ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

// @function
// @param number Number
// @param fallback Fallback, if the value was na
// @returns
export format_percent(float number, string fallback = '') => na(number) ? fallback : (str.tostring(percent(number)) + '%')

// @function
// @param main_value The first value
// @param second_value The second value
// @param title The title
// @param one_line_value If true wraps second value with parenthesis and concats it with the main value, otherwise moves it to another line
// @param one_line_pair If true divides the pairs with `:`, otherwise moves the values to another line
// @returns
export format_pairs(string main_value, string second_value = '', string title = '', bool one_line_value = true, bool one_line_pair = true) =>
	value = one_line_value ? main_value + ' (' + second_value + ')' : main_value + '\n' + second_value
	value := str.endswith(value, '\n') ? str.replace(value, ' \n', '') : str.replace(value, ' ()', '')
	pair = title == '' ? value : one_line_value and one_line_pair ? title + ': ' + value : title + '\n' + value

// @function
// @param signal Signal
// @param with_name If true prints name of the signal as well
// @param with_sign If true prints negative sign for short signals
// @returns
export format_signal(signal signal, bool with_name = false, bool with_sign = false) => (with_name ? signal.name + ": " : "") + str.tostring(with_sign ? signal.score : math.abs(signal.score)) + "/" + str.tostring(signal.max_score)

// @function
// @param signals Signals
// @returns
export format_signals(array<signal> signals) =>
    result = ""
    for i = 0 to array.size(signals) - 1 by 1
        s = array.get(signals, i)
        if na(s)
            continue
        result := result + (i == 0 ? "" : "\n") + format_signal(s, true, true)
    result

// @function
// @param source Series of values to process
// @param fallback Fallback, if the value was na
// @returns
export _format_quote(float source, string fallback = '') => na(source) ? fallback : (str.tostring(math.round_to_mintick(source)) + ' ' + syminfo.currency)

// @function
// @param source Series of values to process
// @param fallback Fallback, if the value was na
// @returns
export _format_base(float source = close, string fallback = '') => na(source) ? fallback : (str.tostring(source) + ' ' + syminfo.basecurrency)

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////              FILTERS              ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

// @function
// @param start_time Start time
// @param end_time End time
// @param ignore Ignores filtering and retunrs true
// @param c Close priceurrent_time
// @returns
export is_in_period(int start_time, int end_time, bool ignore = false, int current_time = time) => ignore or start_time <= current_time and current_time <= end_time

// @function
// @param source Source string
// @param substring The substring to search fo
// @param case_sensitive Is it case sensitive? default is false
// @returns true if source contains substring
export str_contains(string source, string substring, bool case_sensitive = false) =>
	str.replace_all(substring, " ", "") == "" or str.contains(case_sensitive ? source : str.upper(source), case_sensitive ? substring : str.upper(substring))

////////////////////////////////////////////
////////////////////////////////////////////
////                                    ////
////              EXCHANGE              ////
////                                    ////
////////////////////////////////////////////
////////////////////////////////////////////

// @function returns btc pair of the current chart
// @param symbol Tickerid
// @returns symbol
export get_btc_pair_symbol(simple string basecurrency) =>
    switch basecurrency
        // "BINANCE:1000LUNCUSDT.P" => "OKX:LUNCBTC"
        // "BINANCE:1000SHIBUSDT.P" => "OKX:SHIBBTC"
        // "BINANCE:1000XECUSDT.P" => "BITFINEX:XECBTC"
        // "BINANCE:BLUEBIRDUSDT.P" => ""
        // "BINANCE:BTCDOMUSDT.P" => ""
        // "BINANCE:BTCUSDT.P" => ""
        // "BINANCE:DEFIUSDT.P" => ""
        // "BINANCE:FOOTBALLUSDT.P" => ""
        // "BINANCE:SPELLUSDT.P" => ""
        // "BINANCE:USDCUSDT.P" => "BINANCE:USDCBTC"

        "CKB" => "COINEX:CKBBTC"
        "COCOS" => "MEXC:COCOSBTC"
        "DENT" => "KUCOIN:DENTBTC"
        "HNT" => "COINEX:HNTBTC"
        "HOT" => "COINEX:HOTBTC"
        "LUNA2" => "BITFINEX:LUNA2BTC"
        "MASK" => "UPBIT:MASKBTC"
        "REEF" => "KUCOIN:REEFBTC"
        "RSR" => "KUCOIN:RSRBTC"
        "T" => "UPBIT:TBTC"
        "XEM" => "COINEX:XEMBTC"

        "1INCH" => "BINANCE:1INCHBTC"
        "AAVE" => "BINANCE:AAVEBTC"
        "ACH" => "BINANCE:ACHBTC"
        "ADA" => "BINANCE:ADABTC"
        "AGIX" => "BINANCE:AGIXBTC"
        "ALGO" => "BINANCE:ALGOBTC"
        "ALICE" => "BINANCE:ALICEBTC"
        "ALPHA" => "BINANCE:ALPHABTC"
        "ANKR" => "BINANCE:ANKRBTC"
        "ANT" => "BINANCE:ANTBTC"
        "APE" => "BINANCE:APEBTC"
        "API3" => "BINANCE:API3BTC"
        "APT" => "BINANCE:APTBTC"
        "ARB" => "BINANCE:ARBBTC"
        "ARPA" => "BINANCE:ARPABTC"
        "AR" => "BINANCE:ARBTC"
        "ASTR" => "BINANCE:ASTRBTC"
        "ATA" => "BINANCE:ATABTC"
        "ATOM" => "BINANCE:ATOMBTC"
        "AUDIO" => "BINANCE:AUDIOBTC"
        "AVAX" => "BINANCE:AVAXBTC"
        "AXS" => "BINANCE:AXSBTC"
        "BAKE" => "BINANCE:BAKEBTC"
        "BAL" => "BINANCE:BALBTC"
        "BAND" => "BINANCE:BANDBTC"
        "BAT" => "BINANCE:BATBTC"
        "BCH" => "BINANCE:BCHBTC"
        "BEL" => "BINANCE:BELBTC"
        "BLZ" => "BINANCE:BLZBTC"
        "BNB" => "BINANCE:BNBBTC"
        "BNX" => "BINANCE:BNXBTC"
        "C98" => "BINANCE:C98BTC"
        "CELO" => "BINANCE:CELOBTC"
        "CELR" => "BINANCE:CELRBTC"
        "CFX" => "BINANCE:CFXBTC"
        "CHR" => "BINANCE:CHRBTC"
        "CHZ" => "BINANCE:CHZBTC"
        "COMP" => "BINANCE:COMPBTC"
        "COTI" => "BINANCE:COTIBTC"
        "CRV" => "BINANCE:CRVBTC"
        "CTK" => "BINANCE:CTKBTC"
        "CTSI" => "BINANCE:CTSIBTC"
        "CVX" => "BINANCE:CVXBTC"
        "DAR" => "BINANCE:DARBTC"
        "DASH" => "BINANCE:DASHBTC"
        "DGB" => "BINANCE:DGBBTC"
        "DOGE" => "BINANCE:DOGEBTC"
        "DOT" => "BINANCE:DOTBTC"
        "DUSK" => "BINANCE:DUSKBTC"
        "DYDX" => "BINANCE:DYDXBTC"
        "EGLD" => "BINANCE:EGLDBTC"
        "ENJ" => "BINANCE:ENJBTC"
        "ENS" => "BINANCE:ENSBTC"
        "EOS" => "BINANCE:EOSBTC"
        "ETC" => "BINANCE:ETCBTC"
        "ETH" => "BINANCE:ETHBTC"
        "FET" => "BINANCE:FETBTC"
        "FIL" => "BINANCE:FILBTC"
        "FLM" => "BINANCE:FLMBTC"
        "FLOW" => "BINANCE:FLOWBTC"
        "FTM" => "BINANCE:FTMBTC"
        "FXS" => "BINANCE:FXSBTC"
        "GALA" => "BINANCE:GALABTC"
        "GAL" => "BINANCE:GALBTC"
        "GMT" => "BINANCE:GMTBTC"
        "GMX" => "BINANCE:GMXBTC"
        "GRT" => "BINANCE:GRTBTC"
        "GTC" => "BINANCE:GTCBTC"
        "HBAR" => "BINANCE:HBARBTC"
        "HIGH" => "BINANCE:HIGHBTC"
        "HOOK" => "BINANCE:HOOKBTC"
        "ICP" => "BINANCE:ICPBTC"
        "ICX" => "BINANCE:ICXBTC"
        "ID" => "BINANCE:IDBTC"
        "IMX" => "BINANCE:IMXBTC"
        "INJ" => "BINANCE:INJBTC"
        "IOST" => "BINANCE:IOSTBTC"
        "IOTA" => "BINANCE:IOTABTC"
        "IOTX" => "BINANCE:IOTXBTC"
        "JASMY" => "BINANCE:JASMYBTC"
        "KAVA" => "BINANCE:KAVABTC"
        "KLAY" => "BINANCE:KLAYBTC"
        "KNC" => "BINANCE:KNCBTC"
        "KSM" => "BINANCE:KSMBTC"
        "LDO" => "BINANCE:LDOBTC"
        "LINA" => "BINANCE:LINABTC"
        "LINK" => "BINANCE:LINKBTC"
        "LIT" => "BINANCE:LITBTC"
        "LPT" => "BINANCE:LPTBTC"
        "LQTY" => "BINANCE:LQTYBTC"
        "LRC" => "BINANCE:LRCBTC"
        "LTC" => "BINANCE:LTCBTC"
        "MAGIC" => "BINANCE:MAGICBTC"
        "MANA" => "BINANCE:MANABTC"
        "MATIC" => "BINANCE:MATICBTC"
        "MINA" => "BINANCE:MINABTC"
        "MKR" => "BINANCE:MKRBTC"
        "MTL" => "BINANCE:MTLBTC"
        "NEAR" => "BINANCE:NEARBTC"
        "NEO" => "BINANCE:NEOBTC"
        "NKN" => "BINANCE:NKNBTC"
        "OCEAN" => "BINANCE:OCEANBTC"
        "OGN" => "BINANCE:OGNBTC"
        "OMG" => "BINANCE:OMGBTC"
        "ONE" => "BINANCE:ONEBTC"
        "ONT" => "BINANCE:ONTBTC"
        "OP" => "BINANCE:OPBTC"
        "PEOPLE" => "BINANCE:PEOPLEBTC"
        "PERP" => "BINANCE:PERPBTC"
        "PHB" => "BINANCE:PHBBTC"
        "QNT" => "BINANCE:QNTBTC"
        "QTUM" => "BINANCE:QTUMBTC"
        "REN" => "BINANCE:RENBTC"
        "RLC" => "BINANCE:RLCBTC"
        "RNDR" => "BINANCE:RNDRBTC"
        "ROSE" => "BINANCE:ROSEBTC"
        "RUNE" => "BINANCE:RUNEBTC"
        "RVN" => "BINANCE:RVNBTC"
        "SAND" => "BINANCE:SANDBTC"
        "SFP" => "BINANCE:SFPBTC"
        "SKL" => "BINANCE:SKLBTC"
        "SNX" => "BINANCE:SNXBTC"
        "SOL" => "BINANCE:SOLBTC"
        "SSV" => "BINANCE:SSVBTC"
        "STG" => "BINANCE:STGBTC"
        "STMX" => "BINANCE:STMXBTC"
        "STORJ" => "BINANCE:STORJBTC"
        "STX" => "BINANCE:STXBTC"
        "SUSHI" => "BINANCE:SUSHIBTC"
        "SXP" => "BINANCE:SXPBTC"
        "THETA" => "BINANCE:THETABTC"
        "TOMO" => "BINANCE:TOMOBTC"
        "TRB" => "BINANCE:TRBBTC"
        "TRU" => "BINANCE:TRUBTC"
        "TRX" => "BINANCE:TRXBTC"
        "UNFI" => "BINANCE:UNFIBTC"
        "UNI" => "BINANCE:UNIBTC"
        "VET" => "BINANCE:VETBTC"
        "WAVES" => "BINANCE:WAVESBTC"
        "WOO" => "BINANCE:WOOBTC"
        "XLM" => "BINANCE:XLMBTC"
        "XMR" => "BINANCE:XMRBTC"
        "XRP" => "BINANCE:XRPBTC"
        "XTZ" => "BINANCE:XTZBTC"
        "YFI" => "BINANCE:YFIBTC"
        "ZEC" => "BINANCE:ZECBTC"
        "ZEN" => "BINANCE:ZENBTC"
        "ZIL" => "BINANCE:ZILBTC"
        "ZRX" => "BINANCE:ZRXBTC"
        "RDNT" => "BINANCE:RDNTBTC"
        "HFT" => "BINANCE:HFTBTC"
        "AMB" => "BINANCE:AMBBTC"
        "LUNC" => "OKX:LUNCBTC"
        "SHIB" => "OKX:SHIBBTC"
        "XEC" => "BITFINEX:XECBTC"
        "TLM" => "BINANCE:TLMBTC"
        "JOE" => "BINANCE:JOEBTC"
        => na

// @function pagination for binance perpetual symbols.
// @param group Group
// @param item Item in each group
// @param group_items Length of group
// @returns symbol
export get_alt_coin_symbol(simple int group, simple int item, simple int group_items = 30) =>
    switch (group - 1) * group_items + item
        // => ["BINANCE:1000LUNCUSDT.P", get_brc_pair_symbol("LUNC")]
        // => ["BINANCE:1000SHIBUSDT.P", get_brc_pair_symbol("SHIB")]
        // => ["BINANCE:1000XECUSDT.P", get_brc_pair_symbol("XEC")]
        // => ["BINANCE:BLUEBIRDUSDT.P", get_brc_pair_symbol("BLUEBIRD")] // not exist
        // => ["BINANCE:BTCDOMUSDT.P", get_brc_pair_symbol("BTCDOM")] // not exist
        // => ["BINANCE:BTCUSDT.P", get_brc_pair_symbol("BTC")] // not exist
        // => ["BINANCE:DEFIUSDT.P", get_brc_pair_symbol("DEFI")] // not exist
        // => ["BINANCE:FOOTBALLUSDT.P", get_brc_pair_symbol("FOOTBALL")] // not exist
        // => ["BINANCE:SPELLUSDT.P", get_brc_pair_symbol("SPELL")] // not exist
        // => ["BINANCE:LEVERUSDT.P", get_btc_pair_symbol("LEVER")] // not exist
        // => ["BINANCE:USDCUSDT.P", na]

        1 => ["BINANCE:CKBUSDT.P", get_btc_pair_symbol("CKB")]
        2 => ["BINANCE:COCOSUSDT.P", get_btc_pair_symbol("COCOS")]
        3 => ["BINANCE:DENTUSDT.P", get_btc_pair_symbol("DENT")]
        4 => ["BINANCE:HNTUSDT.P", get_btc_pair_symbol("HNT")]
        5 => ["BINANCE:HOTUSDT.P", get_btc_pair_symbol("HOT")]
        6 => ["BINANCE:LUNA2USDT.P", get_btc_pair_symbol("LUNA2")]
        7 => ["BINANCE:MASKUSDT.P", get_btc_pair_symbol("MASK")]
        8 => ["BINANCE:REEFUSDT.P", get_btc_pair_symbol("REEF")]
        9 => ["BINANCE:RSRUSDT.P", get_btc_pair_symbol("RSR")]
        10 => ["BINANCE:TUSDT.P", get_btc_pair_symbol("T")]
        11 => ["BINANCE:XEMUSDT.P", get_btc_pair_symbol("XEM")]

        12 => ["BINANCE:1INCHUSDT.P", get_btc_pair_symbol("1INCH")]
        13 => ["BINANCE:AAVEUSDT.P", get_btc_pair_symbol("AAVE")]
        14 => ["BINANCE:ACHUSDT.P", get_btc_pair_symbol("ACH")]
        15 => ["BINANCE:ADAUSDT.P", get_btc_pair_symbol("ADA")]
        16 => ["BINANCE:AGIXUSDT.P", get_btc_pair_symbol("AGIX")]
        17 => ["BINANCE:ALGOUSDT.P", get_btc_pair_symbol("ALGO")]
        18 => ["BINANCE:ALICEUSDT.P", get_btc_pair_symbol("ALICE")]
        19 => ["BINANCE:ALPHAUSDT.P", get_btc_pair_symbol("ALPHA")]
        20 => ["BINANCE:ANKRUSDT.P", get_btc_pair_symbol("ANKR")]
        21 => ["BINANCE:ANTUSDT.P", get_btc_pair_symbol("ANT")]
        22 => ["BINANCE:APEUSDT.P", get_btc_pair_symbol("APE")]
        23 => ["BINANCE:API3USDT.P", get_btc_pair_symbol("API3")]
        24 => ["BINANCE:APTUSDT.P", get_btc_pair_symbol("APT")]
        25 => ["BINANCE:ARBUSDT.P", get_btc_pair_symbol("ARB")]
        26 => ["BINANCE:ARPAUSDT.P", get_btc_pair_symbol("ARPA")]
        27 => ["BINANCE:ARUSDT.P", get_btc_pair_symbol("AR")]
        28 => ["BINANCE:ASTRUSDT.P", get_btc_pair_symbol("ASTR")]
        29 => ["BINANCE:ATAUSDT.P", get_btc_pair_symbol("ATA")]
        30 => ["BINANCE:ATOMUSDT.P", get_btc_pair_symbol("ATOM")]
        31 => ["BINANCE:AUDIOUSDT.P", get_btc_pair_symbol("AUDIO")]
        32 => ["BINANCE:AVAXUSDT.P", get_btc_pair_symbol("AVAX")]
        33 => ["BINANCE:AXSUSDT.P", get_btc_pair_symbol("AXS")]
        34 => ["BINANCE:BAKEUSDT.P", get_btc_pair_symbol("BAKE")]
        35 => ["BINANCE:BALUSDT.P", get_btc_pair_symbol("BAL")]
        36 => ["BINANCE:BANDUSDT.P", get_btc_pair_symbol("BAND")]
        37 => ["BINANCE:BATUSDT.P", get_btc_pair_symbol("BAT")]
        38 => ["BINANCE:BCHUSDT.P", get_btc_pair_symbol("BCH")]
        39 => ["BINANCE:BELUSDT.P", get_btc_pair_symbol("BEL")]
        40 => ["BINANCE:BLZUSDT.P", get_btc_pair_symbol("BLZ")]
        41 => ["BINANCE:BNBUSDT.P", get_btc_pair_symbol("BNB")]
        42 => ["BINANCE:BNXUSDT.P", get_btc_pair_symbol("BNX")]
        43 => ["BINANCE:C98USDT.P", get_btc_pair_symbol("C98")]
        44 => ["BINANCE:CELOUSDT.P", get_btc_pair_symbol("CELO")]
        45 => ["BINANCE:CELRUSDT.P", get_btc_pair_symbol("CELR")]
        46 => ["BINANCE:CFXUSDT.P", get_btc_pair_symbol("CFX")]
        47 => ["BINANCE:CHRUSDT.P", get_btc_pair_symbol("CHR")]
        48 => ["BINANCE:CHZUSDT.P", get_btc_pair_symbol("CHZ")]
        49 => ["BINANCE:COMPUSDT.P", get_btc_pair_symbol("COMP")]
        50 => ["BINANCE:COTIUSDT.P", get_btc_pair_symbol("COTI")]
        51 => ["BINANCE:CRVUSDT.P", get_btc_pair_symbol("CRV")]
        52 => ["BINANCE:CTKUSDT.P", get_btc_pair_symbol("CTK")]
        53 => ["BINANCE:CTSIUSDT.P", get_btc_pair_symbol("CTSI")]
        54 => ["BINANCE:CVXUSDT.P", get_btc_pair_symbol("CVX")]
        55 => ["BINANCE:DARUSDT.P", get_btc_pair_symbol("DAR")]
        56 => ["BINANCE:DASHUSDT.P", get_btc_pair_symbol("DASH")]
        57 => ["BINANCE:DGBUSDT.P", get_btc_pair_symbol("DGB")]
        58 => ["BINANCE:DOGEUSDT.P", get_btc_pair_symbol("DOGE")]
        59 => ["BINANCE:DOTUSDT.P", get_btc_pair_symbol("DOT")]
        60 => ["BINANCE:DUSKUSDT.P", get_btc_pair_symbol("DUSK")]
        61 => ["BINANCE:DYDXUSDT.P", get_btc_pair_symbol("DYDX")]
        62 => ["BINANCE:EGLDUSDT.P", get_btc_pair_symbol("EGLD")]
        63 => ["BINANCE:ENJUSDT.P", get_btc_pair_symbol("ENJ")]
        64 => ["BINANCE:ENSUSDT.P", get_btc_pair_symbol("ENS")]
        65 => ["BINANCE:EOSUSDT.P", get_btc_pair_symbol("EOS")]
        66 => ["BINANCE:ETCUSDT.P", get_btc_pair_symbol("ETC")]
        67 => ["BINANCE:ETHUSDT.P", get_btc_pair_symbol("ETH")]
        68 => ["BINANCE:FETUSDT.P", get_btc_pair_symbol("FET")]
        69 => ["BINANCE:FILUSDT.P", get_btc_pair_symbol("FIL")]
        70 => ["BINANCE:FLMUSDT.P", get_btc_pair_symbol("FLM")]
        71 => ["BINANCE:FLOWUSDT.P", get_btc_pair_symbol("FLOW")]
        72 => ["BINANCE:FTMUSDT.P", get_btc_pair_symbol("FTM")]
        73 => ["BINANCE:FXSUSDT.P", get_btc_pair_symbol("FXS")]
        74 => ["BINANCE:GALAUSDT.P", get_btc_pair_symbol("GALA")]
        75 => ["BINANCE:GALUSDT.P", get_btc_pair_symbol("GAL")]
        76 => ["BINANCE:GMTUSDT.P", get_btc_pair_symbol("GMT")]
        77 => ["BINANCE:GMXUSDT.P", get_btc_pair_symbol("GMX")]
        78 => ["BINANCE:GRTUSDT.P", get_btc_pair_symbol("GRT")]
        79 => ["BINANCE:GTCUSDT.P", get_btc_pair_symbol("GTC")]
        80 => ["BINANCE:HBARUSDT.P", get_btc_pair_symbol("HBAR")]
        81 => ["BINANCE:HIGHUSDT.P", get_btc_pair_symbol("HIGH")]
        82 => ["BINANCE:HOOKUSDT.P", get_btc_pair_symbol("HOOK")]
        83 => ["BINANCE:ICPUSDT.P", get_btc_pair_symbol("ICP")]
        84 => ["BINANCE:ICXUSDT.P", get_btc_pair_symbol("ICX")]
        85 => ["BINANCE:IDUSDT.P", get_btc_pair_symbol("ID")]
        86 => ["BINANCE:IMXUSDT.P", get_btc_pair_symbol("IMX")]
        87 => ["BINANCE:INJUSDT.P", get_btc_pair_symbol("INJ")]
        88 => ["BINANCE:IOSTUSDT.P", get_btc_pair_symbol("IOST")]
        89 => ["BINANCE:IOTAUSDT.P", get_btc_pair_symbol("IOTA")]
        90 => ["BINANCE:IOTXUSDT.P", get_btc_pair_symbol("IOTX")]
        91 => ["BINANCE:JASMYUSDT.P", get_btc_pair_symbol("JASMY")]
        92 => ["BINANCE:KAVAUSDT.P", get_btc_pair_symbol("KAVA")]
        93 => ["BINANCE:KLAYUSDT.P", get_btc_pair_symbol("KLAY")]
        94 => ["BINANCE:KNCUSDT.P", get_btc_pair_symbol("KNC")]
        95 => ["BINANCE:KSMUSDT.P", get_btc_pair_symbol("KSM")]
        96 => ["BINANCE:LDOUSDT.P", get_btc_pair_symbol("LDO")]
        97 => ["BINANCE:LINAUSDT.P", get_btc_pair_symbol("LINA")]
        98 => ["BINANCE:LINKUSDT.P", get_btc_pair_symbol("LINK")]
        99 => ["BINANCE:LITUSDT.P", get_btc_pair_symbol("LIT")]
        100 => ["BINANCE:LPTUSDT.P", get_btc_pair_symbol("LPT")]
        101 => ["BINANCE:LQTYUSDT.P", get_btc_pair_symbol("LQTY")]
        102 => ["BINANCE:LRCUSDT.P", get_btc_pair_symbol("LRC")]
        103 => ["BINANCE:LTCUSDT.P", get_btc_pair_symbol("LTC")]
        104 => ["BINANCE:MAGICUSDT.P", get_btc_pair_symbol("MAGIC")]
        105 => ["BINANCE:MANAUSDT.P", get_btc_pair_symbol("MANA")]
        106 => ["BINANCE:MATICUSDT.P", get_btc_pair_symbol("MATIC")]
        107 => ["BINANCE:MINAUSDT.P", get_btc_pair_symbol("MINA")]
        108 => ["BINANCE:MKRUSDT.P", get_btc_pair_symbol("MKR")]
        109 => ["BINANCE:MTLUSDT.P", get_btc_pair_symbol("MTL")]
        110 => ["BINANCE:NEARUSDT.P", get_btc_pair_symbol("NEAR")]
        111 => ["BINANCE:NEOUSDT.P", get_btc_pair_symbol("NEO")]
        112 => ["BINANCE:NKNUSDT.P", get_btc_pair_symbol("NKN")]
        113 => ["BINANCE:OCEANUSDT.P", get_btc_pair_symbol("OCEAN")]
        114 => ["BINANCE:OGNUSDT.P", get_btc_pair_symbol("OGN")]
        115 => ["BINANCE:OMGUSDT.P", get_btc_pair_symbol("OMG")]
        116 => ["BINANCE:ONEUSDT.P", get_btc_pair_symbol("ONE")]
        117 => ["BINANCE:ONTUSDT.P", get_btc_pair_symbol("ONT")]
        118 => ["BINANCE:OPUSDT.P", get_btc_pair_symbol("OP")]
        119 => ["BINANCE:PEOPLEUSDT.P", get_btc_pair_symbol("PEOPLE")]
        120 => ["BINANCE:PERPUSDT.P", get_btc_pair_symbol("PERP")]
        121 => ["BINANCE:PHBUSDT.P", get_btc_pair_symbol("PHB")]
        122 => ["BINANCE:QNTUSDT.P", get_btc_pair_symbol("QNT")]
        123 => ["BINANCE:QTUMUSDT.P", get_btc_pair_symbol("QTUM")]
        124 => ["BINANCE:RENUSDT.P", get_btc_pair_symbol("REN")]
        125 => ["BINANCE:RLCUSDT.P", get_btc_pair_symbol("RLC")]
        126 => ["BINANCE:RNDRUSDT.P", get_btc_pair_symbol("RNDR")]
        127 => ["BINANCE:ROSEUSDT.P", get_btc_pair_symbol("ROSE")]
        128 => ["BINANCE:RUNEUSDT.P", get_btc_pair_symbol("RUNE")]
        129 => ["BINANCE:RVNUSDT.P", get_btc_pair_symbol("RVN")]
        130 => ["BINANCE:SANDUSDT.P", get_btc_pair_symbol("SAND")]
        131 => ["BINANCE:SFPUSDT.P", get_btc_pair_symbol("SFP")]
        132 => ["BINANCE:SKLUSDT.P", get_btc_pair_symbol("SKL")]
        133 => ["BINANCE:SNXUSDT.P", get_btc_pair_symbol("SNX")]
        134 => ["BINANCE:SOLUSDT.P", get_btc_pair_symbol("SOL")]
        135 => ["BINANCE:SSVUSDT.P", get_btc_pair_symbol("SSV")]
        136 => ["BINANCE:STGUSDT.P", get_btc_pair_symbol("STG")]
        137 => ["BINANCE:STMXUSDT.P", get_btc_pair_symbol("STMX")]
        138 => ["BINANCE:STORJUSDT.P", get_btc_pair_symbol("STORJ")]
        139 => ["BINANCE:STXUSDT.P", get_btc_pair_symbol("STX")]
        140 => ["BINANCE:SUSHIUSDT.P", get_btc_pair_symbol("SUSHI")]
        141 => ["BINANCE:SXPUSDT.P", get_btc_pair_symbol("SXP")]
        142 => ["BINANCE:THETAUSDT.P", get_btc_pair_symbol("THETA")]
        143 => ["BINANCE:TOMOUSDT.P", get_btc_pair_symbol("TOMO")]
        144 => ["BINANCE:TRBUSDT.P", get_btc_pair_symbol("TRB")]
        145 => ["BINANCE:TRUUSDT.P", get_btc_pair_symbol("TRU")]
        146 => ["BINANCE:TRXUSDT.P", get_btc_pair_symbol("TRX")]
        147 => ["BINANCE:UNFIUSDT.P", get_btc_pair_symbol("UNFI")]
        148 => ["BINANCE:UNIUSDT.P", get_btc_pair_symbol("UNI")]
        149 => ["BINANCE:VETUSDT.P", get_btc_pair_symbol("VET")]
        150 => ["BINANCE:WAVESUSDT.P", get_btc_pair_symbol("WAVES")]
        151 => ["BINANCE:WOOUSDT.P", get_btc_pair_symbol("WOO")]
        152 => ["BINANCE:XLMUSDT.P", get_btc_pair_symbol("XLM")]
        153 => ["BINANCE:XMRUSDT.P", get_btc_pair_symbol("XMR")]
        154 => ["BINANCE:XRPUSDT.P", get_btc_pair_symbol("XRP")]
        155 => ["BINANCE:XTZUSDT.P", get_btc_pair_symbol("XTZ")]
        156 => ["BINANCE:YFIUSDT.P", get_btc_pair_symbol("YFI")]
        157 => ["BINANCE:ZECUSDT.P", get_btc_pair_symbol("ZEC")]
        158 => ["BINANCE:ZENUSDT.P", get_btc_pair_symbol("ZEN")]
        159 => ["BINANCE:ZILUSDT.P", get_btc_pair_symbol("ZIL")]
        160 => ["BINANCE:ZRXUSDT.P", get_btc_pair_symbol("ZRX")]
        161 => ["BINANCE:RDNTUSDT.P", get_btc_pair_symbol("RDNT")]
        162 => ["BINANCE:HFTUSDT.P", get_btc_pair_symbol("HFT")]
        163 => ["BINANCE:AMBUSDT.P", get_btc_pair_symbol("AMB")]
        164 => ["BINANCE:TLMUSDT.P", get_btc_pair_symbol("TLM")]
        165 => ["BINANCE:JOEUSDT.P", get_btc_pair_symbol("JOE")]
        => [na, na]

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////       WEHBOOK INTEGRATION         ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

// @function
// @param fields Other properties
// @param api_key ApiKey
// @param chart_id Chart ID. You can find it in URL: www.tradingview.com/chart/{{chartId}}
// @param trade_type Type of the current symbol. Possible values are stock, futures, index, forex, crypto, fund, dr.
// @param symbol Symbol name without exchange prefix, e.g. 'MSFT'.
// @param exchange Prefix of current symbol name (i.e. for 'CME_EOD:TICKER' prefix is 'CME_EOD').
// @param base Base currency for the symbol. For the symbol 'BTCUSD' returns 'BTC'.
// @param currency Currency for the current symbol. Returns currency code: 'USD', 'EUR', etc.
// @param timeframe A string representation of the chart's timeframe. The returned string's format is "[<quantity>][<units>]", where <quantity> and <units> are in some cases absent. <quantity> is the number of units, but it is absent if that number is 1. <unit> is "S" for seconds, "D" for days, "W" for weeks, "M" for months, but it is absent for minutes. No <unit> exists for hours. The variable will return: "10S" for 10 seconds, "60" for 60 minutes, "D" for one day, "2W" for two weeks, "3M" for one quarter. Can be used as an argument with any function containing a `timeframe` parameter.
// @returns
export json(string fields, string api_key, string chart_id = '', string trade_type = syminfo.type, string tickerid = syminfo.tickerid, string exchange = syminfo.prefix, string base = syminfo.basecurrency, string currency = syminfo.currency, string timeframe = timeframe.period) => '{' +
	 '"exchange":"' + exchange + '",' +
	 '"base":"' + base + '",' +
	 '"quote":"' + currency + '",' +
	 '"tickerid":"' + tickerid + '",' +
	 '"timeframe":"' + timeframe + '",' +
	 '"type":"' + trade_type + '",' +
	 '"apiKey":"' + api_key + '",' +
	 (chart_id != "" ? '"chartUrl":"https://www.tradingview.com/chart/' + chart_id + '/?symbol=' + tickerid + '",' : '') +
	 fields +
	 '}'

///////////////////////////////////////////
///////////////////////////////////////////
////                                   ////
////           PLOT HELPERS            ////
////                                   ////
///////////////////////////////////////////
///////////////////////////////////////////

// @function
// @param tbl The ID of a table object that can be passed to other table.*() functions.
// @param row The index of the cell's row. Numbering starts at 0.
// @param col The index of the cell's column. Numbering starts at 0.
// @param txt The text to be displayed inside the cell. Optional. The default is empty string.
// @param bgcolor The background color of the text. Optional. The default is color.white
// @param txtcolor The color of the text. Optional. The default is color.gray
// @param tooltip The tooltip to be displayed inside the cell. Optional.
// @param haligh The horizontal alignment of the cell's text. Optional. The default value is text.align_center. Possible values: text.align_left, text.align_center, text.align_right
// @param valign The vertical alignment of the cell's text. Optional. The default value is text.align_center. Possible values: text.align_top, text.align_center, text.align_bottom
// @param size Size of texts
// @returns
export _table_cel(table tbl, int row, int col, string txt, color bgcolor = na, color txtcolor = na, string tooltip = na, string halign = text.align_center, string valign = text.align_center, string size = size.small) =>
	table.cell(tbl, col, row, txt,
		 text_color=na(txtcolor) ? color.white : txtcolor,
		 bgcolor=na(bgcolor) ? color.gray : bgcolor,
		 text_halign=halign,
		 text_valign=valign,
		 text_size=size,
		 tooltip=tooltip)

// @function
// @param signal 1 is bullish, greater than 1 is strongly bullish, -1 is bearish, lower than -1 is strongly bearish, 0 is neutral
// @param ncolor Neutral color.
// @returns
export _signal_color(int signal = 0, color ncolor = na) =>
	signal == 1 ? color.new(color.green, 50) :
	 signal > 1 ? color.green :
	 signal == -1 ? color.new(color.red, 50) :
	 signal < -1 ? color.red :
	 ncolor

// @function Creates new label object for easier debug
// @param debug_bar_index Debug bar index
// @param tooltip Hover to see tooltip label.
// @returns new label object
export _debug(int debug_bar_index, string title, string tooltip = na) =>
	if debug_bar_index == bar_index
		label.new(
			 debug_bar_index,
			 high + high/5,
			 text=title,
			 color=color.gray,
			 textcolor=color.white,
			 yloc=yloc.abovebar,
			 style=label.style_label_down,
			 textalign=text.align_left,
			 tooltip=tooltip)

