Normal Distribution Asymmetry & Volatility Zones
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tkarolak

//@version=5
indicator("Normal Distribution Asymmetry & Volatility Zones", "ND-Asymetry & Volatility", overlay=false, precision = 8)

// ====================
// ==== Background ====
// ====================

// This script calculates and visualizes normal distribution asymmetry and volatility zones
// which are useful in identifying potential buying and selling opportunities in the market.

// Skewness calculation leverages the third central moment and variance of the price distribution 
// within a specified period to ascertain the asymmetry in the price distribution.

// The Z-score coefficient (multiplier) aids in defining the volatility zones by categorizing
// data points that are a certain number of standard deviations away from the mean.

// By analyzing the skewness coefficient, traders can gain insights into the distribution of recent prices.
// A positive skewness indicates that recent prices are skewed to the right of the average price, suggesting potential for upward movement.
// Conversely, a negative skewness indicates a potential downward trend.

// The normalized skewness (skNorm) helps in understanding how the current skewness value deviates from 
// a moving average of the recent "x-bars" skewness values.

// The script generates buy and sell signals based on the skewness value and the specified Z-score multiplier.
// The buy and sell zones are determined based on whether the current price is in the tails of the distribution.
// - Buy zone: Identified when skewness is negative and the current price is significantly below the mean price.
// - Sell zone: Highlighted when skewness is positive and the current price is notably above the mean price.

// The script further identifies 'superbuy' and 'supersell' zones that are extreme representations of the buy and sell zones.
// These zones are recognized as areas with even higher potential for profit, albeit with higher risk.

// The plot presents the skewness coefficient, enabling traders to visualize the price distribution asymmetry at a glance.
// The background color dynamically changes to visually represent the current market condition based on the calculated zones.

// For optimal results, users are advised to experiment with different settings for the source, length, 
// multiplier, and normalization length parameters to find the setup that best suits their trading strategy.

// The horizontal zero line serves as a reference point, delineating positive and negative skewness values 
// and thereby aiding in the swift interpretation of the market condition.

// Users can toggle between the raw and normalized skewness values through the 'Plot normalized' setting,
// facilitating a more flexible approach to market analysis based on individual preferences.


// Custom types
type SettingsSk
    float   source
    int     lenght
    float   multiplier
    bool    selector
    int     lenghtNormalize

// Normal Distribution Asymmetry
string gNda                 = "Normal Distribution Asymmetry"
string ttSourceSk           = "Select the price source indicator calculations"
string ttLengthSk           = "Specify the distribution length (between 50 and 2000)"
string ttMultiplierSk       = "Set a volatility threshold (1 to 3) to determine when to take long (buy) or short (sell) positions based on positive (above mean) and negative (below mean) Z-scores. The Z-score measures how many standard deviations a data point is from the mean."
string ttSelector           = "Switch to normalized values. The normalized indicator reflects its deviation from the x-bars moving average."
string ttLengthNormSk       = "Length for normalization"
SettingsSk settingsSk       = 
 SettingsSk.new(
   input.source (close,     "Source",                      group = gNda, tooltip = ttSourceSk),
   input.int    (180,       "Distribution Length",         group = gNda, minval = 50, maxval = 2000, tooltip = ttLengthSk),
   input.float  (2.0,       "Define Z-score coefficient",  group = gNda, minval = 1, maxval = 3, step = 0.1, tooltip = ttMultiplierSk),
   input.bool   (false,     "Plot normalized",             group = gNda, tooltip = ttSelector),
   input.int    (14,        "Length for normalization",    group = gNda, minval = 7, maxval = 50, tooltip = ttLengthNormSk)
 )

skewness(float _source, int _length, int _lenghtNorm) =>
    // Calculate the mean (average)
    float ma = ta.sma(_source, _length)
    // Calculate the standard deviation of price signals
    float stddev = ta.stdev(_source, settingsSk.lenght)
    // Calculate variance
    float variance = ta.variance(_source, _length)
    // Calculate the third central moment
    float moment3 = ta.sma(math.pow(_source - ma, 3), _length)
    // Calculate the skewness coefficient
    float skewness = moment3 / math.pow(variance, 3)
    float skNorm = skewness / ta.sma(skewness,_lenghtNorm)-1
    // The 'sk' variable has been transformed into a normalized value, reflecting its deviation from the "_lenghtNorm" bars long average.
    // If the result is greater than 0, it signifies that the current value exceeds the average,
    // while if it's less than 0, it suggests that the current value is below the average.
    [skewness, skNorm, ma, stddev]

[sk, skNorm, ma, stddev]  = skewness(settingsSk.source, settingsSk.lenght, settingsSk.lenghtNormalize)

// Generate buy/sell zones based on skewness and volatility
buyZone = sk < 0 and settingsSk.source < ma - settingsSk.multiplier * stddev
sellZone = sk > 0 and settingsSk.source > ma + settingsSk.multiplier * stddev

buySrc = array.new_float(0)
sellSrc = array.new_float(0)

for i = 0 to settingsSk.lenght-1 by 1
    if buyZone[i] 
        array.push(buySrc, settingsSk.source[i])
    if sellZone[i]
        array.push(sellSrc, settingsSk.source[i])

maBuySrc    = array.avg(buySrc)
maSellSrc   = array.avg(sellSrc)
sdBuySrc    = array.stdev(buySrc)
sdSellSrc   = array.stdev(sellSrc)

bool superbuyZone       = sk < 0 and settingsSk.source < maBuySrc - settingsSk.multiplier * sdBuySrc
bool supersellZone      = sk > 0 and settingsSk.source > maSellSrc + settingsSk.multiplier * sdSellSrc

////////////////////////////////////////////////////////////////////////////////
//                     ====== DRAWING and PLOTTING ======                     //
////////////////////////////////////////////////////////////////////////////////

// Define colors for skewness and signals
skColor = sk > 0 ? color.green : color.red
signalColor = buyZone ? color.new(color.red, 90) : sellZone ? color.new(color.green, 90) : na
signalColor := superbuyZone ? color.new(color.red, 10) : supersellZone ? color.new(color.green, 10) : signalColor

// Plot the skewness coefficient
plot(settingsSk.selector ? skNorm : sk, title="Skewness Coefficient", color=skColor, linewidth=2)

// Add horizontal reference lines
hline(0, "Zero Line", color = color.gray)

// Set background color based on signals
bgcolor(signalColor)
//easy peasy