Files

90 lines
4.9 KiB
Plaintext

// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Stochastic Momentum Index (SMI)", "SMI", overlay=false)
//@function Calculates Stochastic Momentum Index oscillator
//@param source Source series to calculate SMI for
//@param k_period Lookback period for high/low range calculation
//@param k_smooth First smoothing period for raw SMI values
//@param d_smooth Second smoothing period for signal line
//@param blau Use Blau method (true) or Chande/Kroll method (false)
//@returns [%K, %D] values of Stochastic Momentum Index
smi(series float source, simple int k_period, simple int k_smooth, simple int d_smooth, simple bool blau) =>
if k_period <= 0 or k_smooth <= 0 or d_smooth <= 0
runtime.error("All periods must be positive")
float src_clean = na(source) ? 0 : source
if na(source)
[na, na]
else
var array<float> high_buffer = array.new_float(0), var array<float> low_buffer = array.new_float(0)
array.push(high_buffer, nz(high)), array.push(low_buffer, nz(low))
if array.size(high_buffer) > k_period
array.shift(high_buffer)
if array.size(low_buffer) > k_period
array.shift(low_buffer)
highest_high = array.max(high_buffer), lowest_low = array.min(low_buffer)
midpoint = (highest_high + lowest_low) / 2, range_half = (highest_high - lowest_low) / 2
float a1 = 2.0 / (k_smooth + 1), float a2 = 2.0 / (k_smooth + 1), float a3 = 2.0 / (d_smooth + 1)
var float e1 = 1.0, var float e2 = 1.0, var float e3 = 1.0, var bool warmup = true
var float ema1_raw = 0.0, var float ema2_raw = 0.0, var float ema3_raw = 0.0
var float first_ema = 0.0, var float k_value = 0.0, var float d_value = 0.0
if blau
raw_smi = range_half > 0 ? 100 * (src_clean - midpoint) / range_half : 0
ema1_raw := a1 * (raw_smi - ema1_raw) + ema1_raw
if warmup
e1 *= (1 - a1), e2 *= (1 - a2), e3 *= (1 - a3)
float c1 = 1.0 / (1.0 - e1), float c2 = 1.0 / (1.0 - e2), float c3 = 1.0 / (1.0 - e3)
first_ema := ema1_raw * c1
ema2_raw := a2 * (first_ema - ema2_raw) + ema2_raw
k_value := ema2_raw * c2
ema3_raw := a3 * (k_value - ema3_raw) + ema3_raw
d_value := ema3_raw * c3
warmup := math.max(math.max(e1, e2), e3) > 1e-10
else
first_ema := ema1_raw
ema2_raw := a2 * (first_ema - ema2_raw) + ema2_raw
k_value := ema2_raw
ema3_raw := a3 * (k_value - ema3_raw) + ema3_raw
d_value := ema3_raw
else
var float num_ema1 = 0.0, var float num_ema2 = 0.0, var float den_ema1 = 0.0, var float den_ema2 = 0.0
var float num_first = 0.0, var float den_first = 0.0
numerator = src_clean - midpoint, denominator = range_half
ema1_raw := a1 * (numerator - ema1_raw) + ema1_raw
num_ema1 := a1 * (denominator - num_ema1) + num_ema1
if warmup
e1 *= (1 - a1), e2 *= (1 - a2), e3 *= (1 - a3)
float c1 = 1.0 / (1.0 - e1), float c2 = 1.0 / (1.0 - e2), float c3 = 1.0 / (1.0 - e3)
num_first := ema1_raw * c1, den_first := num_ema1 * c1
num_ema2 := a2 * (num_first - num_ema2) + num_ema2
den_ema2 := a2 * (den_first - den_ema2) + den_ema2
k_value := den_ema2 > 0 ? 100 * (num_ema2 * c2) / (den_ema2 * c2) : 0
ema3_raw := a3 * (k_value - ema3_raw) + ema3_raw
d_value := ema3_raw * c3
warmup := math.max(math.max(e1, e2), e3) > 1e-10
else
num_first := ema1_raw, den_first := num_ema1
num_ema2 := a2 * (num_first - num_ema2) + num_ema2
den_ema2 := a2 * (den_first - den_ema2) + den_ema2
k_value := den_ema2 > 0 ? 100 * num_ema2 / den_ema2 : 0
ema3_raw := a3 * (k_value - ema3_raw) + ema3_raw
d_value := ema3_raw
[k_value, d_value]
// ---------- Main loop ----------
// Inputs
i_k_period = input.int(10, "%K Period", minval=1, maxval=100, tooltip="Lookback period for high/low range calculation")
i_k_smooth = input.int(3, "%K Smooth", minval=1, maxval=20, tooltip="First smoothing period for raw SMI values")
i_d_smooth = input.int(3, "%D Smooth", minval=1, maxval=20, tooltip="Second smoothing period for signal line")
i_source = input.source(close, "Source", tooltip="Price series to analyze")
i_blau = input.bool(true, "Blau Method", tooltip="True: Blau (smooth raw SMI ratio), False: Chande/Kroll (smooth numerator & denominator first)")
// Calculation
[k_value, d_value] = smi(i_source, i_k_period, i_k_smooth, i_d_smooth, i_blau)
// Plots
plot(k_value, "SMI %K", color=color.yellow, linewidth=2)
plot(d_value, "SMI %D", color=color.blue, linewidth=2)