Psychological levels (Bank levels) by tartigradia
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tartigradia
//
// Description:
// Psychological levels (Bank levels) plots the price levels by truncating after the nth leftmost digits, as it appears the humain brain tends to intuitively calculate in log/zipf.
// Contrary to other similar scripts, this one uses a mathematical calculation that extracts the 1, 2 or 3 leftmost digits and calculate the previous and next level by incrementing/decrementing these digits. This means it works for any asset at any price range.
// For example, if price is at 0.0421, the next major price level is 0.05 and medium one is 0.043. For another asset currently priced at 19354, the next and previous major price levels are 20000 and 10000 respectively, and the next/previous medium levels are 20000 and 19000, and the next/previous weak levels are 19400 and 19300.
// By default, strong upper level is in green, strong lower level is in red, medium upper level is in blue, medium lower level is in yellow, and weak levels aren't displayed but can be. Strong levels are increments of the first leftmost digit (eg, 10000 to 20000), medium levels are increments of the second leftmost digit (eg, 19000 to 20000), and weak levels of the third leftmost digit (eg, 19100 to 19200). Instead of plotting all the psychological levels all at once as a grid, which makes the chart unintelligible, here the levels adapt dynamically around the current price, so that they show the upper/lower levels relatively to the current price.
// A simple moving average is implemented, so that "half-levels" are also displayed when relevant (eg, medium level can also display 19500 instead of only 19000 or 20000). This can be disabled by setting smoothing to 1.
// I made this script mainly to train with PineScript, but I guess it can be useful for new traders, as it's easy to forget that psychological levels can often be as strong if not stronger than technical levels.

//@version=5
indicator(title='Psychological levels (Bank levels) by tartigradia', shorttitle='PsychoLevels', format=format.price, timeframe="D", timeframe_gaps=false, overlay=true)

//
// Aux functions

// Detect the n left-most digits
f_nleftmostdigits(_num, _n, _increment) =>
    // If input price number is smaller than 1.0, then we add 1 to n because 0 counts as a significant number, eg, 0.0456 with n=2 would return 0.04 instead of 0.045
    _n2 = (_num < 1.0) ? _n + 1 : _n
    // Get the n leftmost digits, by using log10 to remove lower order digits
    nleftmostdigits_nb = int(_num / math.pow(10, (int(math.log10(_num)) - _n2 + 1)))
    // Get the smallest unit at exact the n leftmost digit, this eases calculation of next or previous value
    nleftmostdigits_unit = math.pow(10, (int(math.log10(_num)) - _n2 + 1))
    // Truncate input price after the n leftmost digits
    nleftmostdigits_truncated = nleftmostdigits_nb * nleftmostdigits_unit
    // Return the next or previous (above or below) truncated price at the n leftmost digits
    nleftmostdigits_truncated + nleftmostdigits_unit * _increment

// Rounding function, from TradingView PineScript v5 manual, deprecated, not used
f_round(_val, _decimals) =>
    // Rounds _val to _decimals places.
    _p = math.pow(10, _decimals)
    math.round(math.abs(_val) * _p) / _p * math.sign(_val)

//
// Inputs

src = input(close, title='Data source')
smooth = input.int(2, 'Smoothing (SMA)', minval=1, tooltip='Smooth out transitions by calculating the average between the previous and next psychological level, this allows to display "half-levels" (eg, medium levels can display 19500 instead of only 19000 or 20000). Set smoothing to 1 to disable. Values above 2 are disadvised as the levels will not be psychologically meaningful anymore (eg, 19667).')
cgr = color.green
cre = color.red
cbl = color.teal
cye = color.yellow

//
// Plot

plot(ta.sma(f_nleftmostdigits(src, 3, 1), smooth), 'Weak level above', color=color.new(cbl, 30), linewidth=1, style=plot.style_circles, display=display.none)
plot(ta.sma(f_nleftmostdigits(src, 3, 0), smooth), 'Weak level below', color=color.new(cye, 30), linewidth=1, style=plot.style_circles, display=display.none)
plot(ta.sma(f_nleftmostdigits(src, 2, 1), smooth), 'Medium level above', color=color.new(cbl, 30), linewidth=2, style=plot.style_circles)
plot(ta.sma(f_nleftmostdigits(src, 2, 0), smooth), 'Medium level below', color=color.new(cye, 30), linewidth=2, style=plot.style_circles)
// Plot strongest last so that it overlaps lower strength levels
plot(ta.sma(f_nleftmostdigits(src, 1, 1), smooth), 'Strong level above', color=color.new(cgr, 30), linewidth=3, style=plot.style_circles)
plot(ta.sma(f_nleftmostdigits(src, 1, 0), smooth), 'Strong level below', color=color.new(cre, 30), linewidth=3, style=plot.style_circles)

