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

//@version=5

// @description General statistics library.
library("statistics")

// @function The "error function" encountered in integrating the normal
//           distribution (which is a normalized form of the Gaussian function).
//
// @param x The input series.
//
// @returns The Error Function evaluated for each element of x.
export erf(float x) =>
    a1 =  0.254829592
    a2 = -0.284496736
    a3 =  1.421413741
    a4 = -1.453152027
    a5 =  1.031405429
    p =   0.3275911
    sign = (x < 0 ? -1 : 1)
    ax = math.abs(x)/math.sqrt(2)
    t = 1/(1 + (p*ax))
    y = 1 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t * math.exp(-ax*ax)
    (sign*y)
    
// @fucntion The 'complementary error function'.
//
// @param x The input series
//
// @returns The Complementary Error Function evaluated for each alement of x.
export erfc(float x) =>
    1 - erf(x)

// @function Calculates the sum of the reciprocals of the series.
//           For each element 'elem' in the series:
//              sum += 1/elem
//           Should the element be 0, the reciprocal value of 0 is used instead
//           of NA.
//
// @param src The input series.
// @param len The length for the sum.
//
// @returns The sum of the resciprocals of 'src' for 'len' bars back.
export sumOfReciprocals(float src, int len) =>
    math.sum((src != 0 ? 1.0/src : 0), len)

// @function The mean of the series.
//           (wrapper around ta.sma).
//
// @param src The input series.
// @param len The length for the mean.
//
// @returns The mean of 'src' for 'len' bars back.
export mean(float src, int len) =>
    ta.sma(src, len)

// @function The mean of the series.
//           (wrapper around ta.sma).
//
// @param src The input series.
// @param len The length for the average.
//
// @returns The average of 'src' for 'len' bars back.
export average(float src, int len) =>
    ta.sma(src, len)

// @function The Geometric Mean of the series.
//
//           The geometric mean is most important when using data representing
//           percentages, ratios, or rates of change. It cannot be used for
//           negative numbers
//
//           Since the pure mathematical implementation generates a very large
//           intermediate result, we performed the calculation in log space:
//           https://buzzardsbay.org/special-topics/calculating-geometric-mean/
//
// @param src The input series.
// @param len The length for the geometricMean.
//
// @returns The geometric mean of 'src' for 'len' bars back.
export geometricMean(float src, int len) =>
    math.exp(1/len * math.sum(math.log(src), len))

// @function The Harmonic Mean of the series.
//
//           The harmonic mean is most applicable to time changes and, along
//           with the geometric mean, has been used in economics for price
//           analysis. It is more difficult to calculate; therefore, it is less
//           popular than eiter of the other averages.
//
//           0 values are ignored in the calculation.
//
// @param src The input series.
// @param len The length for the harmonicMean.
//
// @returns The harmonic mean of 'src' for 'len' bars back.
export harmonicMean(float src, int len) =>
    1/len * sumOfReciprocals(src, len)

// @function The median of the series.
//           (a wrapper around ta.median)
//
// @param src The input series.
// @param len The length for the median.
//
// @returns The median of 'src' for 'len' bars back.
export median(float src, int len) =>
    ta.median(src, len)

// @function The variance of the series.
//
// @param src The input series.
// @param len The length for the variance.
// @param biased Wether to use the biased calculation (for a population), or the
//               unbiased calculation (for a sample set). [Default = true].
//
// @returns The variance of 'src' for 'len' bars back.
export variance(float src, int len, bool biased = true) =>
    ta.variance(src, len, biased)

// @function The standard deviation of the series.
//
// @param src The input series.
// @param len The length for the stdev.
// @param biased Wether to use the biased calculation (for a population), or the
//               unbiased calculation (for a sample set). [Default = true].
//
// @returns The standard deviation of 'src' for 'len' bars back.
export stdev(float src, int len, bool biased = true) =>
    ta.stdev(src, len, biased)
    
// @function The skew of the series.
//
//           Skewness measures the amount of distortion from a symmetric
//           distribution, making the curve appear to be short on the left
//           (lower prices) and extended to the right (higher prices). The
//           extended side, either left or right is called the tail, and a
//           longer tail to the right is called positive skewness. Negative
//           skewness has the tail extending towards the left.
//
// @param src The input series.
// @param len The length for the skewness.
//
// @returns The skewness of 'src' for 'len' bars back.
export skewness(float src, int len) =>
    r = math.log(src/src[1])
    m = mean(r, len)
    (1/len * math.sum(math.pow(r-m, 3), len)) / math.pow(1/len * math.sum(math.pow(r-m, 2), len), 3/2)
    
// @function The kurtosis of the series.
//
//           Kurtosis describes the peakedness or flatness of a distribution.
//           This can be used as an unbiased assessment of whether prices are
//           trending or moving sideways. Trending prices will ocver a wider
//           range and thus a flatter distribution (kurtosis < 3; negative
//           kurtosis). If prices are range-bound, there will be a clustering
//           around the mean and we have positive kurtosis (kurtosis > 3)
//
// @param src The input series.
// @param len The length for the kurtosis.
//
// @returns The kurtosis of 'src' for 'len' bars back.
export kurtosis(float src, int len) =>
    r = math.log(src/src[1])
    m = mean(r, len)
    ((1/len * math.sum(math.pow(r-m, 4), len)) / math.pow(1/len * math.sum(math.pow(r-m, 2), len), 2))

// @function The normalized kurtosis of the series.
//           kurtosis > 0 --> positive kurtosis --> trending
//           kurtosis < 0 --> negative krutosis --> range-bound
//
// @param src The input series.
// @param len The length for the excessKurtosis.
//
// @returns The excessKurtosis of 'src' for 'len' bars back.
export excessKurtosis(float src, int len) =>
    kurtosis(src, len) - 3

// @function Calculates the probability mass for the value according to the
//           src and length. It calculates the probability for value to be 
//           present in the normal distribution calculated for src and length.
//
// @param src The input series.
// @param len The length for the normDist.
// @param value The series of values to calculate the normal distance for
//
// @returns The normal distance of 'value' to 'src' for 'len' bars back.
export normDist(float src, int len, float value) =>
    m = mean(src, len)
    v = variance(src, len)
    div = math.sqrt(math.pi * 2) * math.sqrt(v)
    pwr = math.pow(value - m, 2) / (2 * v) * -1
    ePwr = math.pow(math.e, pwr)
    (1 / div) * ePwr

// @function Calculates the cumulative probability mass for the value according
//           to the src and length. It calculates the cumulative probability for
//           value to  be present in the normal distribution calculated for src
//           and length.
//
// @param src The input series.
// @param len The length for the normDistCumulative.
// @param value The series of values to calculate the cumulative normal distance
//              for
//
// @returns The cumulative normal distance of 'value' to 'src' for 'len' bars
//          back.
export normDistCumulative(float src, int len, float value) =>
    m = median(src, len)
    v = stdev(src, len)
    sqrt2 = math.sqrt(2)
    (0.5) * (1 + erf((value - m) / (v * sqrt2)))

// @function Returns then z-score of objective to the series src.
//           It returns the number of stdev's the objective is away from the
//           mean(src, len)
//
// @param src The input series.
// @param len The length for the zScore.
// @param value The series of values to calculate the cumulative normal distance
//              for
//
// @returns The z-score of objectiv with respect to src and len.
export zScore(float src, int len, float objective) =>
    (objective - mean(src, len)) / stdev(src, len)

// @function Calculates the efficiency ratio of the series.
//
//           It measures the noise of the series. The lower the number, the
//           higher the noise.
//
// @param src The input series.
// @param len The length for the efficiency ratio.
//
// @returns The efficiency ratio of 'src' for 'len' bars back.    
export er(float src, int len) =>
    spc = math.sum(math.abs(src[0] - src[1]), len)
    math.abs(src[len-1]-src[0])/spc
  
// @function Calculates the efficiency ratio of the series.
//
//           It measures the noise of the series. The lower the number, the
//           higher the noise.
//
// @param src The input series.
// @param len The length for the efficiency ratio.
//
// @returns The efficiency ratio of 'src' for 'len' bars back.
export efficiencyRatio(float src, int len) =>
    er(src, len)
    
// @function Calculates the efficiency ratio of the series.
//
//           It measures the noise of the series. The lower the number, the
//           higher the noise.
//
// @param src The input series.
// @param len The length for the efficiency ratio.
//
// @returns The efficiency ratio of 'src' for 'len' bars back.
export fractalEfficiency(float src, int len) =>
    er(src, len)
  
// @function Calculates the Mean Squared Error of the series.
//
// @param src The input series.
// @param len The length for the mean squared error.
//
// @returns The mean squared error of 'src' for 'len' bars back.
export mse(float src, int len, float other) =>
    mean(math.pow((src - other), 2), len)

// @function Calculates the Mean Squared Error of the series.
//
// @param src The input series.
// @param len The length for the mean squared error.
//
// @returns The mean squared error of 'src' for 'len' bars back.
export meanSquaredError(float src, int len, float other) =>
    mse(src, len, other)

// @function Calculates the Root Mean Squared Error of the series.
//
// @param src The input series.
// @param len The length for the root mean squared error.
//
// @returns The root mean squared error of 'src' for 'len' bars back.
export rmse(float src, int len, float other) =>
    math.sqrt(mse(src, len, other))

// @function Calculates the Root Mean Squared Error of the series.
//
// @param src The input series.
// @param len The length for the root mean squared error.
//
// @returns The root mean squared error of 'src' for 'len' bars back.
export rootMeanSquaredError(float src, int len, float other) =>
    rmse(src, len, other)
    
// @function Calculates the Mean Absolute Error of the series.
//
// @param src The input series.
// @param len The length for the mean absolute error.
//
// @returns The mean absolute error of 'src' for 'len' bars back.
export mae(float src, int len, float other) =>
    mean(math.abs((src - other)), len)

// @function Calculates the Mean Absolute Error of the series.
//
// @param src The input series.
// @param len The length for the mean absolute error.
//
// @returns The mean absolute error of 'src' for 'len' bars back.
export meanAbsoluteError(float src, int len, float other) =>
    mae(src, len, other)
    
    
//plot(close)
plot(er(close, 100))

