feat: add new indicators (Decay, Edecay, MinusDi, MinusDm, PlusDi, PlusDm, Maxindex, Minindex, Sarext) and update pine scripts, core libs, validation tests, and python bindings
2026-03-09 13:45:46 -07:00
|
|
|
|
// Licensed under the Apache License, Version 2.0
|
2026-01-18 19:02:03 -08:00
|
|
|
|
// © mihakralj
|
|
|
|
|
|
//@version=6
|
|
|
|
|
|
indicator("Price Momentum Oscillator (PMO)", "PMO", overlay=false)
|
|
|
|
|
|
|
2026-02-11 14:46:56 -08:00
|
|
|
|
//@function Calculates Price Momentum Oscillator (DecisionPoint algorithm)
|
2026-01-18 19:02:03 -08:00
|
|
|
|
//@param src Source series to calculate PMO for
|
2026-02-11 14:46:56 -08:00
|
|
|
|
//@param time_periods First EMA smoothing period for 1-bar ROC (default 35)
|
|
|
|
|
|
//@param smooth_periods Second EMA smoothing period for PMO (default 20)
|
|
|
|
|
|
//@param signal_periods Signal line EMA period (default 10)
|
|
|
|
|
|
//@returns PMO value measuring double-smoothed momentum
|
|
|
|
|
|
pmo(series float src, simple int time_periods=35, simple int smooth_periods=20, simple int signal_periods=10)=>
|
|
|
|
|
|
if time_periods<2 or smooth_periods<=0 or signal_periods<=0
|
|
|
|
|
|
runtime.error("Periods must be greater than 0 (time_periods >= 2)")
|
|
|
|
|
|
// Step 1: Always 1-bar ROC (percentage)
|
|
|
|
|
|
float roc = bar_index > 0 and not na(src[1]) and src[1] != 0.0 ? (src / src[1] - 1.0) * 100.0 : 0.0
|
|
|
|
|
|
// Step 2: First Custom EMA of ROC (alpha = 2/time_periods), then ×10
|
|
|
|
|
|
float alpha1 = 2.0 / time_periods
|
|
|
|
|
|
var float roc_ema = na
|
|
|
|
|
|
roc_ema := na(roc_ema) ? roc : roc_ema + alpha1 * (roc - roc_ema)
|
|
|
|
|
|
float roc_ema_scaled = roc_ema * 10.0
|
|
|
|
|
|
// Step 3: Second Custom EMA of scaled RocEma (alpha = 2/smooth_periods) → PMO
|
|
|
|
|
|
float alpha2 = 2.0 / smooth_periods
|
|
|
|
|
|
var float pmo_val = na
|
|
|
|
|
|
pmo_val := na(pmo_val) ? roc_ema_scaled : pmo_val + alpha2 * (roc_ema_scaled - pmo_val)
|
|
|
|
|
|
pmo_val
|
2026-01-18 19:02:03 -08:00
|
|
|
|
|
|
|
|
|
|
// ---------- Main loop ----------
|
|
|
|
|
|
|
|
|
|
|
|
// Inputs
|
|
|
|
|
|
i_source = input.source(close, "Source")
|
2026-02-11 14:46:56 -08:00
|
|
|
|
i_time_periods = input.int(35, "Time Periods (1st EMA)", minval=2)
|
|
|
|
|
|
i_smooth_periods = input.int(20, "Smooth Periods (2nd EMA)", minval=1)
|
|
|
|
|
|
i_signal_periods = input.int(10, "Signal Line Period", minval=1)
|
2026-01-18 19:02:03 -08:00
|
|
|
|
|
|
|
|
|
|
// Calculation
|
2026-02-11 14:46:56 -08:00
|
|
|
|
pmo_value = pmo(i_source, i_time_periods, i_smooth_periods, i_signal_periods)
|
|
|
|
|
|
|
|
|
|
|
|
// Signal line uses standard EMA: alpha = 2/(N+1)
|
|
|
|
|
|
float alpha_signal = 2.0 / (i_signal_periods + 1)
|
2026-01-18 19:02:03 -08:00
|
|
|
|
var float signal_line = na
|
2026-02-11 14:46:56 -08:00
|
|
|
|
signal_line := na(signal_line) ? pmo_value : signal_line + alpha_signal * (pmo_value - signal_line)
|
2026-01-18 19:02:03 -08:00
|
|
|
|
|
|
|
|
|
|
// Plot
|
|
|
|
|
|
plot(pmo_value, "PMO", color=color.blue, linewidth=2)
|
|
|
|
|
|
plot(signal_line, "Signal", color=color.red, linewidth=2)
|
2026-02-11 14:46:56 -08:00
|
|
|
|
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)
|