feat: Dpo, Tsi, Vortex, Bpp, Cci, Cfo, Tr, Ui, Vc, Vov, Vr, Vs, Mfi, Nvi, Obv, Pvi, Pvo, Pvol, Pvr, Pvt, Tvi

This commit is contained in:
Miha
2024-10-30 13:45:36 -07:00
parent 06c6875970
commit 6231bab9e5
34 changed files with 3151 additions and 254 deletions
+46
View File
@@ -105,6 +105,21 @@ public class MomentumUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Dpo_Update()
{
var indicator = new Dpo(period: 20);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Pmo_Update()
{
@@ -214,6 +229,21 @@ public class MomentumUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Tsi_Update()
{
var indicator = new Tsi(firstPeriod: 25, secondPeriod: 13);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble() + 100, IsNew: false)); // Ensure positive prices
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Vel_Update()
{
@@ -228,4 +258,20 @@ public class MomentumUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Vortex_Update()
{
var indicator = new Vortex(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
}
+47
View File
@@ -109,4 +109,51 @@ public class OscillatorsUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Bop_Update()
{
var indicator = new Bop();
TBar r = new(DateTime.Now, ReferenceValue, ReferenceValue, ReferenceValue, ReferenceValue, 1000, IsNew: true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TBar(DateTime.Now, GetRandomDouble(), GetRandomDouble(), GetRandomDouble(), GetRandomDouble(), 1000, IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Cci_Update()
{
var indicator = new Cci(period: 20);
TBar r = new(DateTime.Now, ReferenceValue, ReferenceValue, ReferenceValue, ReferenceValue, 1000, IsNew: true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TBar(DateTime.Now, GetRandomDouble(), GetRandomDouble(), GetRandomDouble(), GetRandomDouble(), 1000, IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Cfo_Update()
{
var indicator = new Cfo(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
}
+96
View File
@@ -101,4 +101,100 @@ public class VolatilityUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Tr_Update()
{
var indicator = new Tr();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Ui_Update()
{
var indicator = new Ui(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Vc_Update()
{
var indicator = new Vc(period: 20, deviations: 2.0);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Vov_Update()
{
var indicator = new Vov(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Vr_Update()
{
var indicator = new Vr(shortPeriod: 10, longPeriod: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Vs_Update()
{
var indicator = new Vs(period: 14, multiplier: 2.0);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
}
+163
View File
@@ -156,4 +156,167 @@ public class VolumeUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Mfi_Update()
{
var indicator = new Mfi(period: 14);
TBar r = GetRandomBar(true);
// Generate a sequence of bars for warmup
var warmupBars = new List<TBar>();
for (int i = 0; i < indicator.WarmupPeriod; i++)
{
var bar = GetRandomBar(IsNew: true);
warmupBars.Add(bar);
indicator.Calc(bar);
}
// Calculate initial value after warmup
double initialValue = indicator.Calc(r);
// Apply random updates
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
// Reset and replay the same sequence
indicator.Init();
foreach (var bar in warmupBars)
{
indicator.Calc(bar);
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Nvi_Update()
{
var indicator = new Nvi();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Obv_Update()
{
var indicator = new Obv();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Pvi_Update()
{
var indicator = new Pvi();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Pvol_Update()
{
var indicator = new Pvol();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Pvo_Update()
{
var indicator = new Pvo(shortPeriod: 12, longPeriod: 26);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Pvr_Update()
{
var indicator = new Pvr();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Pvt_Update()
{
var indicator = new Pvt();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Tvi_Update()
{
var indicator = new Tvi(minTick: 0.5);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
}
+210 -180
View File
@@ -1,183 +1,213 @@
# Indicators in QuanTAlib
⭐= Validation against several TA libraries<br>
✔️= Validation tests passed<br>
❌= Issue
\* = Returns multiple values
|**MOMENTUM INDICATORS**|**Class Name**|Skender.Stock|TALib.NETCore|
|--|:--:|:--:|:--:|
|DMI - Directional Movement Index|`?`|GetDmi||
|DMX - Jurik Directional Movement Index|`?`|||
|MOM - Momentum|`?`|||
|VEL - Jurik Signal Velocity|`?`|||
|ADX - Average Directional Movement Index|`?`|GetAdx|Adx|
|ADXR - Average Directional Movement Index|`?`|Rating|Adxr|
|APO - Absolute Price Oscillator|`?`|Apo||
|DPO - Detrended Price Oscillator|`?`|GetDpo||
|MACD - Moving Average Convergence/Divergence|`?`|||
|PO - Price Oscillator|`?`|||
|PPO - Percentage Price Oscillator|`?`|||
|PMO - Price Momentum Oscillator|`?`|GetPmo||
|PRS - Price Relative Strength|`?`|GetPrs||
|ROC - Rate of Change|`?`|GetRoc||
|TRIX - 1-day ROC of TEMA|`?`|GetTrix||
|VORTEX - Vortex Indicator|`?`|||
<br>
|**VOLATILITY INDICATORS**|**Class Name**|Skender.Stock|TALib.NETCore|
|ADR - Average Daily Range|`?`|||
|ANDREW - Andrew's Pitchfork|`?`|||
|ATR - Average True Range|`Atr`|GetAtr|Atr|
|ATRP - Average True Range Percent|`?`|||
|ATRSTOP - ATR Trailing Stop|`?`|GetAtrStop||
|BBANDS - Bollinger Bands®|`?`|BollingerBands||
|CHAND - Chandelier Exit|`?`|GetChandelier||
|CVI - Chaikins Volatility|`?`|||
|DON - Donchian Channels|`?`|GetDonchian||
|FCB - Fractal Chaos Bands|`?`|GetFcb||
|HV - Historical Volatility|`Hv`|||
|ICH - Ichimoku Cloud|`?`|GetIchimoku||
|KEL - Keltner Channels|`?`|GetKeltner||
|NATR - Normalized Average True Range|`?`|GetAtr||
|CHN - Price Channel Indicator|`?`|||
|SAR - Parabolic Stop and Reverse|`?`|GetParabolicSar||
|STARC - Starc Bands|`?`|GetStarcBands||
|TR - True Range|`?`|||
|UI - Ulcer Index|`?`|GetUlcerIndex||
|VSTOP - Volatility Stop|`?`|GetVolatilityStop||
<br>
|**OSCILLATORS**|**Class Name**|Skender.Stock|TALib.NETCore|
|RSI - Relative Strength Index|`Rsi`|GetRsi||
|RSX - Jurik Trend Strength Index|`Rsx`|||
|AC - Acceleration Oscillator|`?`|||
|AO - Awesome Oscillator|`?`|GetAwesome||
|AROON - Aroon oscillator|`?`|GetAroon|Aroon|
|BOP - Balance of Power|`?`|GetBop|Bop|
|CCI - Commodity Channel Index|`?`|GetCci|Cci|
|CFO - Chande Forcast Oscillator|`?`|||
|CMO - Chande Momentum Oscillator|`Cmo`|GetCmo|Cmo|
|CHOP - Choppiness Index|`?`|GetChop||
|COG - Ehler's Center of Gravity|`?`|||
|COPPOCK - Coppock Curve|`?`|||
|CRSI - Connor RSI|`?`|GetConnorsRsi||
|CTI - Ehler's Correlation Trend Indicator|`?`|||
|DOSC - Derivative Oscillator|`?`|||
|EFI - Elder Ray's Force Index|`?`|GetElderRay||
|FISHER - Fisher Transform|`?`|||
|FOSC - Forecast Oscillator|`?`|||
|GATOR - Williams Alliator Oscillator|`?`|GetGator||
|KDJ - KDJ Indicator (trend reversal)|`?`|||
|KRI - Kairi Relative Index|`?`|||
|RVGI - Relative Vigor Index|`?`|||
|SMI - Stochastic Momentum Index|`?`|GetSmi||
|SRSI - Stochastic RSI|`?`|GetStochRsi||
|STC - Schaff Trend Cycle|`?`|GetStc||
|STOCH - Stochastic Oscillator|`?`|GetStoch||
|TSI - True Strength Index|`?`|GetTsi||
|UO - Ultimate Oscillator|`?`|GetUltimate||
|WILLR - Larry Williams' %R|`?`|GetWilliamsR||
<br>
|**VOLUME INDICATORS**|**Class Name**|Skender.Stock|TALib.NETCore|
|ADL - Chaikin Accumulation Distribution Line|`?`|GetAdl|Ad|
|ADOSC - Chaikin Accumulation Distribution Oscillator|`?`|GetChaikinOsc|AdOsc|
|AOBV - Archer On-Balance Volume|`?`|||
|CMF - Chaikin Money Flow|`?`|GetCmf||
|EOM - Ease of Movement|`?`|||
|KVO - Klinger Volume Oscillator|`?`|GetKvo||
|MFI - Money Flow Index|`?`|GetMfi||
|NVI - Negative Volume Index|`?`|||
|OBV - On-Balance Volume|`?`|GetObv||
|PVI - Positive Volume Index|`?`|||
|PVOL - Price-Volume|`?`|||
|PVO - Percentage Volume Oscillator|`?`|GetPvo||
|PVR - Price Volume Rank|`?`|||
|PVT - Price Volume Trend|`?`|||
|TVI - Trade Volume Index|`?`|||
|VP - Volume Profile|`?`|||
|VWAP - Volume Weighted Average Price|`?`|GetVwap||
|VWMA - Volume Weighted Moving Average|`?`|GetVwma||
<br>
|**NUMERICAL ANALYSIS**|**Class Name**|Skender.Stock|TALib.NETCore|
|BETA - Beta coefficient|`?`|||
|CORR - Correlation Coefficient|`?`|||
|CURVATURE - Rate of Change in Direction or Slope|`Curvature`|||
|ENTROPY - Measure of Uncertainty or Disorder|`Entropy`|||
|KURTOSIS - Measure of Tails/Peakedness|`Kurtosis`|||
|HUBER - Huber Loss|`Huber`|||
|HURST - Hurst Exponent|`?`|GetHurst||
|MAX - Maximum with exponential decay|`Max`|||
|MEDIAN - Middle value|`Median`|||
|MIN - Minimum with exponential decay|`Min`|||
|MODE - Most Frequent Value|`Mode`|||
|PERCENTILE - Rank Order|`Percentile`|||
|RSQUARED - Coefficient of Determination R-Squared|`?`|||
|SKEW - Skewness, asymmetry of distribution|`Skew`|||
|SLOPE - Rate of Change, Linear Regression|`Slope`|||
|STDDEV - Standard Deviation, Measure of Spread|`Stddev`|||
|THEIL - Theil's U Statistics|`?`|||
|TSF - Time Series Forecast|`?`|✔️|✔️|
|VARIANCE - Average of Squared Deviations|`Variance`|||
|ZSCORE - Standardized Score|`Zscore`|||
<br>
|**ERRORS**|**Class Name**|Skender.Stock|TALib.NETCore|
|MAE - Mean Absolute Error|`Mae`|||
|MAPD - Mean Absolute Percentage Deviation|`Mapd`|||
|MAPE - Mean Absolute Percentage Error|`Mape`|||
|MASE - Mean Absolute Scaled Error|`Mase`|||
|MDA - Mean Directional Accuracy|`Mda`|||
|ME - Mean Error|`Me`|||
|MPE - Mean Percentage Error|`Mpe`|||
|MSE - Mean Squared Error|`Mse`|||
|MSLE - Mean Squared Logarithmic Error|`Msle`|||
|RAE - Relative Absolute Error|`Rae`|||
|RMSE - Root Mean Squared Error|`Rmse`|||
|RSE - Relative Squared Error|`Rse`|||
|RMSLE - Root Mean Squared Logarithmic Error|`Rmsle`|||
|SMAPE - Symmetric Mean Absolute Percentage Error|`Smape`|||
<br>
|**AVERAGES & TRENDS**|**Class Name**|Skender.Stock|TALib.NETCore|
|AFIRMA - Autoregressive Finite Impulse Response Moving Average|`Afirma`|||
|ALMA - Arnaud Legoux Moving Average|`Alma`|✔️||
|DEMA - Double EMA Average|`Dema`|✔️|✔️|
|DSMA - Deviation Scaled Moving Average|`Dsma`|||
|DWMA - Double WMA Average|`Dwma`|||
|EMA - Exponential Moving Average|`Ema`|⭐|⭐|
|EPMA - Endpoint Moving Average|`Epma`|✔️||
|FRAMA - Fractal Adaptive Moving Average|`Frama`|||
|FWMA - Fibonacci Weighted Moving Average|`Fwma`|||
|HILO - Gann High-Low Activator|`?`|||
|HTIT - Hilbert Transform Instantaneous Trendline|`Htit`|✔️|✔️|
|GMA - Gaussian-Weighted Moving Average|`Gma`|||
|HMA - Hull Moving Average|`Hma`|✔️|✔️|
|HWMA - Holt-Winter Moving Average|`Hwma`|||
|JMA - Jurik Moving Average|`Jma`|||
|JORDAN - Jordan Moving Average|`?`|||
|KAMA - Kaufman's Adaptive Moving Average|`Kama`|✔️|✔️|
|LTMA - Laguerre Transform Moving Average|`Ltma`|||
|MAAF - Median-Average Adaptive Filter|`Maaf`|||
|MAMA - MESA Adaptive Moving Average|`Mama`|✔️|✔️|
|MGDI - McGinley Dynamic Indicator|`Mgdi`|✔️||
|MLMA - Minimal Lag Moving Average|`?`|||
|MMA - Modified Moving Average|`Mma`|||
|PPMA - Pivot Point Moving Average|`?`|||
|PWMA - Pascal's Weighted Moving Average|`Pwma`|||
|QEMA - Quad Exponential Moving Average|`Qema`|||
|RMA - WildeR's Moving Average|`Rma`|||
|SINEMA - Sine Weighted Moving Average|`Sinema`|||
|SMA - Simple Moving Average|`Sma`|||
|SMMA - Smoothed Moving Average|`Smma`|✔️||
|SSF - Ehler's Super Smoother Filter|`?`|||
|SUPERTREND - Supertrend|`?`|✔️||
|T3 - Tillson T3 Moving Average|`T3`|✔️|✔️|
|TEMA - Triple EMA Average|`Tema`|✔️|✔️|
|TRIMA - Triangular Moving Average|`Trima`|✔️||
|VIDYA - Variable Index Dynamic Average|`Vidya`|||
|WMA - Weighted Moving Average|`Wma`|✔️||
|ZLEMA - Zero Lag EMA Average|`Zlema`|||
<br>
|**BASIC TRANSFORMS**|**Class Name**|Skender.Stock|TALib.NETCore|
|OC2 - Midpoint price|`.OC2`|CandlePart.OC2|MidPoint|
|HL2 - Median Price|`.HL2`|CandlePart.HL2|MedPrice|
|HLC3 - Typical Price|`.HLC3`|CandlePart.HLC3|TypPrice|
|OHL3 - Mean Price|`.OHL3`|CandlePart.OHL3||
|OHLC4 - Average Price|`.OHLC4`|CandlePart.OHLC4|AvgPrice|
|HLCC4 - Weighted Price|`.HLCC4`||WclPrice|
**Implementation Status:**
- Basic Transforms: 6 of 6 complete
- Averages & Trends: 33 of 33 complete
- Momentum: 16 of 17 complete
- Oscillators: 6 of 29 complete
- Volatility: 11 of 35 complete
- Volume: 15 of 19 complete
- Numerical Analysis: 13 of 20 complete
- Errors: 16 of 16 complete
- **Total: 116 of 175 indicators implemented (66%)**
|**BASIC TRANSFORMS**|**Class Name**|
|--|:--:|
|OC2 - Midpoint price|`.OC2`|
|HL2 - Median Price|`.HL2`|
|HLC3 - Typical Price|`.HLC3`|
|OHL3 - Mean Price|`.OHL3`|
|OHLC4 - Average Price|`.OHLC4`|
|HLCC4 - Weighted Price|`.HLCC4`|
|**AVERAGES & TRENDS**|**Class Name**|
|--|:--:|
|AFIRMA - Adaptive FIR Moving Average|`Afirma`|
|ALMA - Arnaud Legoux Moving Average|`Alma`|
|DEMA - Double Exponential Moving Average|`Dema`|
|DSMA - Dynamic Simple Moving Average|`Dsma`|
|DWMA - Dynamic Weighted Moving Average|`Dwma`|
|EMA - Exponential Moving Average|`Ema`|
|EPMA - Endpoint Moving Average|`Epma`|
|FRAMA - Fractal Adaptive Moving Average|`Frama`|
|FWMA - Forward Weighted Moving Average|`Fwma`|
|GMA - Gaussian Moving Average|`Gma`|
|HMA - Hull Moving Average|`Hma`|
|HTIT - Hilbert Transform Instantaneous Trendline|`Htit`|
|HWMA - Hann Weighted Moving Average|`Hwma`|
|JMA - Jurik Moving Average|`Jma`|
|KAMA - Kaufman Adaptive Moving Average|`Kama`|
|LTMA - Linear Time Moving Average|`Ltma`|
|MAAF - Moving Average Adaptive Filter|`Maaf`|
|MAMA* - MESA Adaptive Moving Average (MAMA, FAMA)|`Mama`|
|MGDI - McGinley Dynamic Indicator|`Mgdi`|
|MMA - Modified Moving Average|`Mma`|
|PWMA - Parabolic Weighted Moving Average|`Pwma`|
|QEMA - Quick Exponential Moving Average|`Qema`|
|REMA - Regularized Exponential Moving Average|`Rema`|
|RMA - Running Moving Average|`Rma`|
|SINEMA - Sine-weighted Moving Average|`Sinema`|
|SMA - Simple Moving Average|`Sma`|
|SMMA - Smoothed Moving Average|`Smma`|
|T3 - Triple Exponential Moving Average (T3)|`T3`|
|TEMA - Triple Exponential Moving Average|`Tema`|
|TRIMA - Triangular Moving Average|`Trima`|
|VIDYA - Variable Index Dynamic Average|`Vidya`|
|WMA - Weighted Moving Average|`Wma`|
|ZLEMA - Zero-Lag Exponential Moving Average|`Zlema`|
|**MOMENTUM INDICATORS**|**Class Name**|
|--|:--:|
|ADX - Average Directional Movement Index|`Adx`|
|ADXR - Average Directional Movement Index Rating|`Adxr`|
|APO - Absolute Price Oscillator|`Apo`|
|DMI* - Directional Movement Index (DI+, DI-)|`Dmi`|
|DMX - Jurik Directional Movement Index|`Dmx`|
|DPO - Detrended Price Oscillator|`Dpo`|
|🚧 MACD* - Moving Average Convergence/Divergence|`Macd`|
|MOM - Momentum|`Mom`|
|PMO - Price Momentum Oscillator|`Pmo`|
|PO - Price Oscillator|`Po`|
|PPO - Percentage Price Oscillator|`Ppo`|
|PRS - Price Relative Strength|`Prs`|
|ROC - Rate of Change|`Roc`|
|TSI - True Strength Index|`Tsi`|
|TRIX - 1-day ROC of TEMA|`Trix`|
|VEL - Jurik Signal Velocity|`Vel`|
|VORTEX* - Vortex Indicator (VI+, VI-)|`Vortex`|
|**OSCILLATORS**|**Class Name**|
|--|:--:|
|AC - Acceleration Oscillator|`Ac`|
|AO - Awesome Oscillator|`Ao`|
|AROON* - Aroon oscillator (Up, Down)|`Aroon`|
|🚧 BOP - Balance of Power|`Bop`|
|🚧 CCI - Commodity Channel Index|`Cci`|
|🚧 CFO - Chande Forcast Oscillator|`Cfo`|
|CMO - Chande Momentum Oscillator|`Cmo`|
|🚧 CHOP - Choppiness Index|`Chop`|
|🚧 COG - Ehler's Center of Gravity|`Cog`|
|🚧 COPPOCK - Coppock Curve|`Coppock`|
|🚧 CRSI - Connor RSI|`Crsi`|
|🚧 CTI - Ehler's Correlation Trend Indicator|`Cti`|
|🚧 DOSC - Derivative Oscillator|`Dosc`|
|🚧 EFI - Elder Ray's Force Index|`Efi`|
|🚧 FISHER - Fisher Transform|`Fisher`|
|🚧 FOSC - Forecast Oscillator|`Fosc`|
|🚧 GATOR* - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)|`Gator`|
|🚧 KDJ* - KDJ Indicator (K, D, J lines)|`Kdj`|
|🚧 KRI - Kairi Relative Index|`Kri`|
|RSI - Relative Strength Index|`Rsi`|
|RSX - Jurik Trend Strength Index|`Rsx`|
|🚧 RVGI* - Relative Vigor Index (RVGI, Signal)|`Rvgi`|
|🚧 SMI - Stochastic Momentum Index|`Smi`|
|🚧 SRSI* - Stochastic RSI (SRSI, Signal)|`Srsi`|
|🚧 STC - Schaff Trend Cycle|`Stc`|
|🚧 STOCH* - Stochastic Oscillator (%K, %D)|`Stoch`|
|🚧 TSI - True Strength Index|`Tsi`|
|🚧 UO - Ultimate Oscillator|`Uo`|
|🚧 WILLR - Larry Williams' %R|`Willr`|
|**VOLATILITY INDICATORS**|**Class Name**|
|--|:--:|
|🚧 ADR - Average Daily Range|`Adr`|
|🚧 AP - Andrew's Pitchfork|`Ap`|
|ATR - Average True Range|`Atr`|
|🚧 ATRP - Average True Range Percent|`Atrp`|
|🚧 ATRS - ATR Trailing Stop|`Atrs`|
|🚧 BB* - Bollinger Bands® (Upper, Middle, Lower)|`Bb`|
|🚧 CCV - Close-to-Close Volatility|`Ccv`|
|🚧 CE - Chandelier Exit|`Ce`|
|🚧 CV - Conditional Volatility (ARCH/GARCH)|`Cv`|
|🚧 CVI - Chaikin's Volatility|`Cvi`|
|🚧 DC* - Donchian Channels (Upper, Middle, Lower)|`Dc`|
|🚧 EWMA - Exponential Weighted Moving Average Volatility|`Ewma`|
|🚧 FCB - Fractal Chaos Bands|`Fcb`|
|🚧 GKV - Garman-Klass Volatility|`Gkv`|
|🚧 HLV - High-Low Volatility|`Hlv`|
|HV - Historical Volatility|`Hv`|
|🚧 ICH* - Ichimoku Cloud (Conversion, Base, Leading Span A, Leading Span B, Lagging Span)|`Ich`|
|JVOLTY - Jurik Volatility|`Jvolty`|
|🚧 KC* - Keltner Channels (Upper, Middle, Lower)|`Kc`|
|🚧 NATR - Normalized Average True Range|`Natr`|
|🚧 PCH - Price Channel Indicator|`Pch`|
|🚧 PSAR* - Parabolic Stop and Reverse (Value, Trend)|`Psar`|
|🚧 PV - Parkinson Volatility|`Pv`|
|🚧 RSV - Rogers-Satchell Volatility|`Rsv`|
|RV - Realized Volatility|`Rv`|
|RVI - Relative Volatility Index|`Rvi`|
|🚧 STARC* - Starc Bands (Upper, Middle, Lower)|`Starc`|
|🚧 SV - Stochastic Volatility|`Sv`|
|TR - True Range|`Tr`|
|UI - Ulcer Index|`Ui`|
|VC* - Volatility Cone (Mean, Upper Bound, Lower Bound)|`Vc`|
|VOV - Volatility of Volatility|`Vov`|
|VR - Volatility Ratio|`Vr`|
|VS* - Volatility Stop (Long Stop, Short Stop)|`Vs`|
|🚧 YZV - Yang-Zhang Volatility|`Yzv`|
|**VOLUME INDICATORS**|**Class Name**|
|--|:--:|
|ADL - Chaikin Accumulation Distribution Line|`Adl`|
|ADOSC - Chaikin Accumulation Distribution Oscillator|`Adosc`|
|AOBV - Archer On-Balance Volume|`Aobv`|
|CMF - Chaikin Money Flow|`Cmf`|
|EOM - Ease of Movement|`Eom`|
|KVO - Klinger Volume Oscillator|`Kvo`|
|MFI - Money Flow Index|`Mfi`|
|NVI - Negative Volume Index|`Nvi`|
|OBV - On-Balance Volume|`Obv`|
|PVI - Positive Volume Index|`Pvi`|
|PVOL - Price-Volume|`Pvol`|
|PVO - Percentage Volume Oscillator|`Pvo`|
|PVR - Price Volume Rank|`Pvr`|
|PVT - Price Volume Trend|`Pvt`|
|TVI - Trade Volume Index|`Tvi`|
|🚧 VF - Volume Force|`Vf`|
|🚧 VP - Volume Profile|`Vp`|
|🚧 VWAP - Volume Weighted Average Price|`Vwap`|
|🚧 VWMA - Volume Weighted Moving Average|`Vwma`|
|**NUMERICAL ANALYSIS**|**Class Name**|
|--|:--:|
|🚧 BETA* - Beta coefficient (Beta, R-squared)|`Beta`|
|🚧 CORR* - Correlation Coefficient (Correlation, P-value)|`Corr`|
|CURVATURE - Rate of Change in Direction or Slope|`Curvature`|
|ENTROPY - Measure of Uncertainty or Disorder|`Entropy`|
|🚧 HUBER - Huber Loss|`Huber`|
|🚧 HURST - Hurst Exponent|`Hurst`|
|KURTOSIS - Measure of Tails/Peakedness|`Kurtosis`|
|MAX - Maximum with exponential decay|`Max`|
|MEDIAN - Middle value|`Median`|
|MIN - Minimum with exponential decay|`Min`|
|MODE - Most Frequent Value|`Mode`|
|PERCENTILE - Rank Order|`Percentile`|
|🚧 RSQUARED* - Coefficient of Determination (R-squared, Adjusted R-squared)|`Rsquared`|
|SKEW - Skewness, asymmetry of distribution|`Skew`|
|SLOPE - Rate of Change, Linear Regression|`Slope`|
|STDDEV - Standard Deviation, Measure of Spread|`Stddev`|
|🚧 THEIL* - Theil's U Statistics (U1, U2)|`Theil`|
|🚧 TSF* - Time Series Forecast (Forecast, Confidence Interval)|`Tsf`|
|VARIANCE - Average of Squared Deviations|`Variance`|
|ZSCORE - Standardized Score|`Zscore`|
|**ERRORS**|**Class Name**|
|--|:--:|
|HUBER - Huber Loss|`Huber`|
|MAE - Mean Absolute Error|`Mae`|
|MAPD - Mean Absolute Percentage Deviation|`Mapd`|
|MAPE - Mean Absolute Percentage Error|`Mape`|
|MASE - Mean Absolute Scaled Error|`Mase`|
|MDA - Mean Directional Accuracy|`Mda`|
|ME - Mean Error|`Me`|
|MPE - Mean Percentage Error|`Mpe`|
|MSE - Mean Squared Error|`Mse`|
|MSLE - Mean Squared Logarithmic Error|`Msle`|
|RAE - Relative Absolute Error|`Rae`|
|RMSE - Root Mean Squared Error|`Rmse`|
|RMSLE - Root Mean Squared Logarithmic Error|`Rmsle`|
|RSE - Relative Squared Error|`Rse`|
|RSQUARED - R-Squared (Coefficient of Determination)|`Rsquared`|
|SMAPE - Symmetric Mean Absolute Percentage Error|`Smape`|
+3 -1
View File
@@ -1,3 +1,5 @@
# Averages indicators
✔️ AFIRMA - Adaptive FIR Moving Average
✔️ ALMA - Arnaud Legoux Moving Average
✔️ DEMA - Double Exponential Moving Average
@@ -15,7 +17,7 @@
✔️ KAMA - Kaufman Adaptive Moving Average
✔️ LTMA - Linear Time Moving Average
✔️ MAAF - Moving Average Adaptive Filter
✔️ MAMA - MESA Adaptive Moving Average
✔️ *MAMA - MESA Adaptive Moving Average (MAMA, FAMA)
✔️ MGDI - McGinley Dynamic Indicator
✔️ MMA - Modified Moving Average
✔️ PWMA - Parabolic Weighted Moving Average
+112
View File
@@ -0,0 +1,112 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// DPO: Detrended Price Oscillator
/// A momentum indicator that removes the trend from price by comparing the current price
/// to a past moving average, helping to identify cycles in the price.
/// </summary>
/// <remarks>
/// The DPO calculation process:
/// 1. Calculate the period shifted back by (period / 2 + 1) days
/// 2. Calculate SMA for the shifted period
/// 3. DPO = Price - SMA(Price, period) shifted back
///
/// Key characteristics:
/// - Removes long-term trends
/// - Helps identify cycles
/// - Oscillates above and below zero
/// - Default period is 20 days
/// - Uses price displacement
///
/// Formula:
/// DPO = Price - SMA(Price, period) shifted (period/2 + 1) bars back
///
/// Market Applications:
/// - Cycle identification
/// - Overbought/Oversold conditions
/// - Price momentum
/// - Trading signals
/// - Market timing
///
/// Sources:
/// Donald Dorsey - Original development
/// https://www.investopedia.com/terms/d/detrended-price-oscillator-dpo.asp
///
/// Note: DPO helps identify cycles by removing the trend component from the price data
/// </remarks>
[SkipLocalsInit]
public sealed class Dpo : AbstractBase
{
private readonly int _shift;
private readonly CircularBuffer _prices;
private readonly CircularBuffer _sma;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dpo(int period = 20)
{
_shift = period / 2 + 1;
WarmupPeriod = period + _shift;
Name = $"DPO({period})";
_prices = new CircularBuffer(WarmupPeriod);
_sma = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dpo(object source, int period = 20) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prices.Clear();
_sma.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Add current price to buffer
_prices.Add(BarInput.Close);
// Need enough prices for the shifted SMA calculation
if (_index <= _shift)
{
return 0;
}
// Add price from shift periods ago to SMA buffer
_sma.Add(_prices[_shift]);
// Need enough prices for full calculation
if (_index <= WarmupPeriod)
{
return 0;
}
// Calculate DPO
double dpo = BarInput.Close - _sma.Average();
IsHot = _index >= WarmupPeriod;
return dpo;
}
}
+151
View File
@@ -0,0 +1,151 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// TSI: True Strength Index
/// A momentum indicator that shows both trend direction and overbought/oversold conditions
/// by using two smoothing steps on price changes.
/// </summary>
/// <remarks>
/// The TSI calculation process:
/// 1. Calculate price change (PC):
/// PC = Close - Previous Close
/// 2. Calculate absolute price change (APC):
/// APC = |PC|
/// 3. Double smooth both PC and APC using EMA:
/// First PC EMA = EMA(PC, firstPeriod)
/// Second PC EMA = EMA(First PC EMA, secondPeriod)
/// First APC EMA = EMA(APC, firstPeriod)
/// Second APC EMA = EMA(First APC EMA, secondPeriod)
/// 4. Calculate TSI:
/// TSI = (Second PC EMA / Second APC EMA) * 100
///
/// Key characteristics:
/// - Double smoothed momentum indicator
/// - Oscillates between +100 and -100
/// - Default periods are 25 and 13
/// - Shows trend direction
/// - Identifies overbought/oversold
///
/// Formula:
/// TSI = (EMA(EMA(PC, r), s) / EMA(EMA(|PC|, r), s)) * 100
/// where:
/// PC = Close - Previous Close
/// r = first period (default 25)
/// s = second period (default 13)
///
/// Market Applications:
/// - Trend direction
/// - Overbought/Oversold levels
/// - Centerline crossovers
/// - Divergence analysis
/// - Signal line crossovers
///
/// Sources:
/// William Blau - Original development (1991)
/// https://www.investopedia.com/terms/t/tsi.asp
///
/// Note: Values above +25 indicate overbought conditions, while values below -25 indicate oversold conditions
/// </remarks>
[SkipLocalsInit]
public sealed class Tsi : AbstractBase
{
private readonly int _firstPeriod;
private double _prevClose;
private double _pcFirstEma;
private double _pcSecondEma;
private double _apcFirstEma;
private double _apcSecondEma;
private readonly double _firstAlpha;
private readonly double _secondAlpha;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tsi(int firstPeriod = 25, int secondPeriod = 13)
{
_firstPeriod = firstPeriod;
WarmupPeriod = firstPeriod + secondPeriod;
Name = $"TSI({_firstPeriod},{secondPeriod})";
_firstAlpha = 2.0 / (firstPeriod + 1);
_secondAlpha = 2.0 / (secondPeriod + 1);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tsi(object source, int firstPeriod = 25, int secondPeriod = 13) : this(firstPeriod, secondPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_pcFirstEma = 0;
_pcSecondEma = 0;
_apcFirstEma = 0;
_apcSecondEma = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate price changes
double pc = BarInput.Close - _prevClose;
double apc = Math.Abs(pc);
// Initialize or update EMAs
if (_index <= _firstPeriod)
{
_pcFirstEma = pc;
_apcFirstEma = apc;
}
else
{
_pcFirstEma = (_firstAlpha * pc) + ((1 - _firstAlpha) * _pcFirstEma);
_apcFirstEma = (_firstAlpha * apc) + ((1 - _firstAlpha) * _apcFirstEma);
}
if (_index <= WarmupPeriod)
{
_pcSecondEma = _pcFirstEma;
_apcSecondEma = _apcFirstEma;
}
else
{
_pcSecondEma = (_secondAlpha * _pcFirstEma) + ((1 - _secondAlpha) * _pcSecondEma);
_apcSecondEma = (_secondAlpha * _apcFirstEma) + ((1 - _secondAlpha) * _apcSecondEma);
}
// Store current close for next calculation
_prevClose = BarInput.Close;
// Calculate TSI
double tsi = Math.Abs(_apcSecondEma) > double.Epsilon ? (_pcSecondEma / _apcSecondEma) * 100 : 0;
IsHot = _index >= WarmupPeriod;
return tsi;
}
}
+162
View File
@@ -0,0 +1,162 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VORTEX: Vortex Indicator
/// A technical indicator consisting of two oscillating lines that identify trend reversals
/// and confirm current trends based on the highs and lows of the previous period.
/// </summary>
/// <remarks>
/// The Vortex calculation process:
/// 1. Calculate True Range (TR):
/// TR = max(High - Low, |High - Previous Close|, |Low - Previous Close|)
/// 2. Calculate +VM (Positive Movement):
/// +VM = |Current High - Previous Low|
/// 3. Calculate -VM (Negative Movement):
/// -VM = |Current Low - Previous High|
/// 4. Calculate period sums:
/// TR Period Sum = Sum(TR, period)
/// +VM Period Sum = Sum(+VM, period)
/// -VM Period Sum = Sum(-VM, period)
/// 5. Calculate +VI and -VI:
/// +VI = +VM Period Sum / TR Period Sum
/// -VI = -VM Period Sum / TR Period Sum
///
/// Key characteristics:
/// - Two oscillating lines (+VI and -VI)
/// - No upper or lower bounds
/// - Default period is 14 days
/// - Crossovers signal trend changes
/// - Uses true range normalization
///
/// Formula:
/// +VI = Sum(+VM, period) / Sum(TR, period)
/// -VI = Sum(-VM, period) / Sum(TR, period)
///
/// Market Applications:
/// - Trend identification
/// - Trend reversals
/// - Trend confirmation
/// - Trading signals
/// - Market momentum
///
/// Sources:
/// Etienne Botes and Douglas Siepman - Original development (2010)
/// https://www.investopedia.com/terms/v/vortex-indicator-vi.asp
///
/// Note: When +VI crosses above -VI, it signals a potential uptrend, and vice versa
/// </remarks>
[SkipLocalsInit]
public sealed class Vortex : AbstractBase
{
private readonly CircularBuffer _tr;
private readonly CircularBuffer _vmPlus;
private readonly CircularBuffer _vmMinus;
private double _prevHigh;
private double _prevLow;
private double _prevClose;
public double _viPlus { get; set; }
public double _viMinus { get; set; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vortex(int period = 14)
{
WarmupPeriod = period + 1; // Need one extra period for previous values
Name = $"VORTEX({period})";
_tr = new CircularBuffer(period);
_vmPlus = new CircularBuffer(period);
_vmMinus = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vortex(object source, int period = 14) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevHigh = 0;
_prevLow = 0;
_prevClose = 0;
_viPlus = 0;
_viMinus = 0;
_tr.Clear();
_vmPlus.Clear();
_vmMinus.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous values
if (_index == 1)
{
_prevHigh = BarInput.High;
_prevLow = BarInput.Low;
_prevClose = BarInput.Close;
return 0;
}
// Calculate True Range
double tr = Math.Max(BarInput.High - BarInput.Low,
Math.Max(Math.Abs(BarInput.High - _prevClose),
Math.Abs(BarInput.Low - _prevClose)));
// Calculate VM+ and VM-
double vmPlus = Math.Abs(BarInput.High - _prevLow);
double vmMinus = Math.Abs(BarInput.Low - _prevHigh);
// Add values to buffers
_tr.Add(tr);
_vmPlus.Add(vmPlus);
_vmMinus.Add(vmMinus);
// Calculate VI+ and VI-
double trSum = _tr.Sum();
if (Math.Abs(trSum) > double.Epsilon)
{
_viPlus = _vmPlus.Sum() / trSum;
_viMinus = _vmMinus.Sum() / trSum;
}
// Store current values for next calculation
_prevHigh = BarInput.High;
_prevLow = BarInput.Low;
_prevClose = BarInput.Close;
// Return the difference between VI+ and VI-
double vortex = _viPlus - _viMinus;
IsHot = _index >= WarmupPeriod;
return vortex;
}
/// <summary>
/// Gets the positive Vortex line (VI+)
/// </summary>
public double ViPlus => _viPlus;
/// <summary>
/// Gets the negative Vortex line (VI-)
/// </summary>
public double ViMinus => _viMinus;
}
+6 -6
View File
@@ -1,20 +1,20 @@
# Momentum indicators
Done: 12, Todo: 5
Done: 15, Todo: 2
✔️ ADX - Average Directional Movement Index
✔️ ADXR - Average Directional Movement Index Rating
✔️ APO - Absolute Price Oscillator
✔️ DMI - Directional Movement Index
✔️ *DMI - Directional Movement Index (DI+, DI-)
✔️ DMX - Jurik Directional Movement Index
DPO - Detrended Price Oscillator
MACD - Moving Average Convergence/Divergence
✔️ DPO - Detrended Price Oscillator
*MACD - Moving Average Convergence/Divergence (MACD, Signal, Histogram)
✔️ MOM - Momentum
✔️ PMO - Price Momentum Oscillator
✔️ PO - Price Oscillator
✔️ PPO - Percentage Price Oscillator
✔️ PRS - Price Relative Strength
✔️ ROC - Rate of Change
TSI - True Strength Index
✔️ TSI - True Strength Index
✔️ TRIX - 1-day ROC of TEMA
✔️ VEL - Jurik Signal Velocity
VORTEX - Vortex Indicator
✔️ *VORTEX - Vortex Indicator (VI+, VI-)
+66
View File
@@ -0,0 +1,66 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// BOP: Balance of Power
/// A momentum oscillator that measures the strength of buying and selling pressure by comparing
/// closing prices to their corresponding opening prices.
/// </summary>
/// <remarks>
/// The BOP calculation process:
/// 1. Calculate (Close - Open) / (High - Low) for each period
/// 2. A positive BOP indicates buying pressure (bullish)
/// 3. A negative BOP indicates selling pressure (bearish)
///
/// Key characteristics:
/// - Oscillates above and below zero
/// - No upper or lower bounds
/// - Zero line acts as equilibrium between buying and selling pressure
/// - Can be used to identify potential trend reversals and divergences
///
/// Formula:
/// BOP = (Close - Open) / (High - Low)
///
/// Sources:
/// Igor Livshin (1990s)
/// https://www.investopedia.com/terms/b/bop.asp
/// </remarks>
[SkipLocalsInit]
public sealed class Bop : AbstractBase
{
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bop(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bop()
{
WarmupPeriod = 1;
Name = "BOP";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
var range = BarInput.High - BarInput.Low;
if (range <= double.Epsilon) return 0;
return (BarInput.Close - BarInput.Open) / range;
}
}
+101
View File
@@ -0,0 +1,101 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CCI: Commodity Channel Index
/// A momentum oscillator used to identify cyclical trends and measure the deviation of price
/// from its statistical mean.
/// </summary>
/// <remarks>
/// The CCI calculation process:
/// 1. Calculate Typical Price (TP) = (High + Low + Close) / 3
/// 2. Calculate Simple Moving Average of TP
/// 3. Calculate Mean Deviation
/// 4. CCI = (TP - SMA(TP)) / (0.015 * Mean Deviation)
///
/// Key characteristics:
/// - Oscillates above and below zero
/// - Typically ranges between +100 and -100
/// - Values above +100 indicate overbought conditions
/// - Values below -100 indicate oversold conditions
/// - Can identify trend strength and reversals
///
/// Formula:
/// CCI = (TypicalPrice - SMA(TypicalPrice, period)) / (0.015 * MeanDeviation)
/// where:
/// - TypicalPrice = (High + Low + Close) / 3
/// - MeanDeviation = Mean(|TP - SMA(TP)|)
///
/// Sources:
/// Donald Lambert (1980)
/// https://www.investopedia.com/terms/c/commoditychannelindex.asp
/// </remarks>
[SkipLocalsInit]
public sealed class Cci : AbstractBase
{
private readonly int _period;
private readonly Sma _sma;
private readonly double[] _typicalPrices;
private readonly double _constant = 0.015;
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The calculation period (default: 20)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cci(object source, int period = 20) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cci(int period = 20)
{
_period = period;
_sma = new Sma(period);
_typicalPrices = new double[period];
WarmupPeriod = period;
Name = "CCI";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private double CalculateMeanDeviation(double typicalPrice, double smaValue)
{
var sum = 0.0;
var count = System.Math.Min(_period, _index + 1);
for (var i = 0; i < count; i++)
{
sum += System.Math.Abs(_typicalPrices[i] - smaValue);
}
return sum / count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
var typicalPrice = (BarInput.High + BarInput.Low + BarInput.Close) / 3.0;
var idx = _index % _period;
_typicalPrices[idx] = typicalPrice;
var smaValue = _sma.Calc(typicalPrice, BarInput.IsNew);
if (_index < _period - 1) return double.NaN;
var meanDeviation = CalculateMeanDeviation(typicalPrice, smaValue);
if (meanDeviation <= double.Epsilon) return 0;
return (typicalPrice - smaValue) / (_constant * meanDeviation);
}
}
+117
View File
@@ -0,0 +1,117 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CFO: Chande Forecast Oscillator
/// A momentum oscillator that measures the percentage difference between the actual price
/// and its linear regression forecast value.
/// </summary>
/// <remarks>
/// The CFO calculation process:
/// 1. Calculate linear regression forecast value for the current period
/// 2. Calculate percentage difference between actual price and forecast
///
/// Key characteristics:
/// - Oscillates above and below zero
/// - Measures deviation of price from its forecasted value
/// - Positive values indicate price is above forecast (bullish)
/// - Negative values indicate price is below forecast (bearish)
/// - Can identify potential trend reversals and price divergences
///
/// Formula:
/// CFO = ((Price - Forecast) / Price) * 100
/// where:
/// - Price is typically the closing price
/// - Forecast is the linear regression forecast value
///
/// Sources:
/// Tushar Chande (1990s)
/// Technical Analysis of Stocks and Commodities magazine
/// </remarks>
[SkipLocalsInit]
public sealed class Cfo : AbstractBase
{
private readonly int _period;
private readonly double[] _prices;
private double _sumX;
private double _sumY;
private double _sumXY;
private double _sumX2;
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The calculation period (default: 14)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cfo(object source, int period = 14) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cfo(int period = 14)
{
_period = period;
_prices = new double[period];
WarmupPeriod = period;
Name = "CFO";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateSums(double oldPrice, double newPrice, int oldX, int newX)
{
_sumY -= oldPrice;
_sumY += newPrice;
_sumXY -= oldPrice * oldX;
_sumXY += newPrice * newX;
_sumX -= oldX;
_sumX += newX;
_sumX2 -= oldX * oldX;
_sumX2 += newX * newX;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private double CalculateForecast()
{
var count = System.Math.Min(_period, _index + 1);
var n = (double)count;
// Calculate linear regression coefficients
var slope = (n * _sumXY - _sumX * _sumY) / (n * _sumX2 - _sumX * _sumX);
var intercept = (_sumY - slope * _sumX) / n;
// Calculate forecast for next period
return intercept + slope * count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override double Calculation()
{
ManageState(Input.IsNew);
var price = Input.Value;
var idx = _index % _period;
var oldPrice = _prices[idx];
_prices[idx] = price;
var oldX = idx + 1;
var newX = _index < _period ? idx + 1 : _period;
UpdateSums(oldPrice, price, oldX, newX);
if (_index < _period - 1) return double.NaN;
var forecast = CalculateForecast();
if (price <= double.Epsilon) return 0;
return ((price - forecast) / price) * 100;
}
}
+6 -6
View File
@@ -3,7 +3,7 @@ Done: 6, Todo: 23
✔️ AC - Acceleration Oscillator
✔️ AO - Awesome Oscillator
✔️ AROON - Aroon oscillator
✔️ *AROON - Aroon oscillator (Up, Down)
BOP - Balance of Power
CCI - Commodity Channel Index
CFO - Chande Forcast Oscillator
@@ -17,16 +17,16 @@ DOSC - Derivative Oscillator
EFI - Elder Ray's Force Index
FISHER - Fisher Transform
FOSC - Forecast Oscillator
GATOR - Williams Alliator Oscillator
KDJ - KDJ Indicator (trend reversal)
*GATOR - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)
*KDJ - KDJ Indicator (K, D, J lines)
KRI - Kairi Relative Index
✔️ RSI - Relative Strength Index
✔️ RSX - Jurik Trend Strength Index
RVGI - Relative Vigor Index
*RVGI - Relative Vigor Index (RVGI, Signal)
SMI - Stochastic Momentum Index
SRSI - Stochastic RSI
*SRSI - Stochastic RSI (SRSI, Signal)
STC - Schaff Trend Cycle
STOCH - Stochastic Oscillator
*STOCH - Stochastic Oscillator (%K, %D)
TSI - True Strength Index
UO - Ultimate Oscillator
WILLR - Larry Williams' %R
+4 -4
View File
@@ -2,10 +2,10 @@
Done: 0, Todo: 8
DOJI - Doji Candlestick Pattern
ER - Elder Ray Pattern
*ER - Elder Ray Pattern (Bull Power, Bear Power)
MARU - Marubozu Candlestick Pattern
PIV - Pivot Points
PP - Price Pivots
RPP - Rolling Pivot Points
*PIV - Pivot Points (Support 1-3, Pivot, Resistance 1-3)
*PP - Price Pivots (Support 1-3, Pivot, Resistance 1-3)
*RPP - Rolling Pivot Points (Support 1-3, Pivot, Resistance 1-3)
WF - Williams Fractal
ZZ - Zig Zag Pattern
+5 -5
View File
@@ -1,8 +1,8 @@
# Statistics indicators
Done: 13, Todo: 6
BETA - Beta coefficient
CORR - Correlation Coefficient
*BETA - Beta coefficient (Beta, R-squared)
*CORR - Correlation Coefficient (Correlation, P-value)
✔️ CURVATURE - Rate of Change in Direction or Slope
✔️ ENTROPY - Measure of Uncertainty or Disorder
HUBER - Huber Loss
@@ -13,11 +13,11 @@ HURST - Hurst Exponent
✔️ MIN - Minimum with exponential decay
✔️ MODE - Most Frequent Value
✔️ PERCENTILE - Rank Order
RSQUARED - Coefficient of Determination R-Squared
*RSQUARED - Coefficient of Determination (R-squared, Adjusted R-squared)
✔️ SKEW - Skewness, asymmetry of distribution
✔️ SLOPE - Rate of Change, Linear Regression
✔️ STDDEV - Standard Deviation, Measure of Spread
THEIL - Theil's U Statistics
TSF - Time Series Forecast
*THEIL - Theil's U Statistics (U1, U2)
*TSF - Time Series Forecast (Forecast, Confidence Interval)
✔️ VARIANCE - Average of Squared Deviations
✔️ ZSCORE - Standardized Score
+102
View File
@@ -0,0 +1,102 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// TR: True Range
/// A basic volatility measure that represents the greatest of three price ranges:
/// current high-low, current high-previous close, or current low-previous close.
/// </summary>
/// <remarks>
/// The TR calculation process:
/// 1. Calculate three differences:
/// - Current High minus Current Low
/// - |Current High minus Previous Close|
/// - |Current Low minus Previous Close|
/// 2. TR is the maximum of these three values
///
/// Key characteristics:
/// - Basic volatility measure
/// - Accounts for gaps between trading periods
/// - Foundation for other indicators (ATR, etc.)
/// - No upper bound
/// - Always positive
///
/// Formula:
/// TR = max(High - Low, |High - Previous Close|, |Low - Previous Close|)
///
/// Market Applications:
/// - Volatility measurement
/// - Stop loss placement
/// - Position sizing
/// - Market analysis
/// - Risk assessment
///
/// Sources:
/// J. Welles Wilder Jr. - Original development
/// https://www.investopedia.com/terms/t/truerange.asp
///
/// Note: True Range accounts for gaps between periods, making it more accurate than simple high-low range
/// </remarks>
[SkipLocalsInit]
public sealed class Tr : AbstractBase
{
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tr()
{
WarmupPeriod = 2; // Need previous close
Name = "TR";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tr(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return BarInput.High - BarInput.Low;
}
// Calculate True Range
double tr = Math.Max(BarInput.High - BarInput.Low,
Math.Max(Math.Abs(BarInput.High - _prevClose),
Math.Abs(BarInput.Low - _prevClose)));
// Store current close for next calculation
_prevClose = BarInput.Close;
IsHot = _index >= WarmupPeriod;
return tr;
}
}
+114
View File
@@ -0,0 +1,114 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// UI: Ulcer Index
/// A technical indicator that measures downside risk by incorporating both
/// the depth and duration of price declines over a given period.
/// </summary>
/// <remarks>
/// The UI calculation process:
/// 1. Calculate percentage drawdown from recent high for each period
/// 2. Square the drawdowns to emphasize larger declines
/// 3. Calculate the average of squared drawdowns
/// 4. Take the square root of the average
///
/// Key characteristics:
/// - Measures downside volatility
/// - Emphasizes larger drawdowns
/// - Default period is 14 days
/// - Always positive
/// - No upper bound
///
/// Formula:
/// Drawdown = ((Close - 14-period High) / 14-period High) * 100
/// UI = sqrt(sum(Drawdown^2) / period)
///
/// Market Applications:
/// - Risk assessment
/// - Portfolio analysis
/// - Trading system evaluation
/// - Market timing
/// - Trend strength measurement
///
/// Sources:
/// Peter Martin - Original development (1987)
/// https://www.investopedia.com/terms/u/ulcerindex.asp
///
/// Note: Higher values indicate higher risk due to deeper or more frequent drawdowns
/// </remarks>
[SkipLocalsInit]
public sealed class Ui : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _prices;
private readonly CircularBuffer _drawdowns;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ui(int period = 14)
{
_period = period;
WarmupPeriod = period;
Name = $"UI({_period})";
_prices = new CircularBuffer(period);
_drawdowns = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ui(object source, int period = 14) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prices.Clear();
_drawdowns.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Add current price to buffer
_prices.Add(BarInput.Close);
// Need enough prices for calculation
if (_index <= _period)
{
return 0;
}
// Calculate maximum price in period
double maxPrice = _prices.Max();
// Calculate percentage drawdown
double drawdown = Math.Abs(maxPrice) > double.Epsilon ? ((BarInput.Close - maxPrice) / maxPrice) * 100 : 0;
// Add squared drawdown to buffer
_drawdowns.Add(drawdown * drawdown);
// Calculate Ulcer Index
double ui = Math.Sqrt(_drawdowns.Average());
IsHot = _index >= WarmupPeriod;
return ui;
}
}
+164
View File
@@ -0,0 +1,164 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VC: Volatility Cone
/// A technical indicator that analyzes volatility across different time periods
/// to identify normal ranges and extreme values.
/// </summary>
/// <remarks>
/// The VC calculation process:
/// 1. Calculate volatility for the specified period
/// 2. Track mean and standard deviation of volatility
/// 3. Calculate upper and lower bounds:
/// Upper = Mean + (deviations * StdDev)
/// Lower = Mean - (deviations * StdDev)
///
/// Key characteristics:
/// - Multi-period volatility analysis
/// - Statistical approach
/// - Default period is 20 days
/// - Returns mean and bounds
/// - Adaptive to market conditions
///
/// Formula:
/// Volatility = StdDev(Returns) * sqrt(252) // Annualized
/// Upper = Mean(Volatility) + (deviations * StdDev(Volatility))
/// Lower = Mean(Volatility) - (deviations * StdDev(Volatility))
///
/// Market Applications:
/// - Options trading
/// - Risk assessment
/// - Volatility forecasting
/// - Trading strategy development
/// - Market regime analysis
///
/// Sources:
/// https://www.investopedia.com/terms/v/volatility-cone.asp
///
/// Note: Returns three values: mean volatility and its upper/lower bounds
/// </remarks>
[SkipLocalsInit]
public sealed class Vc : AbstractBase
{
private readonly int _period;
private readonly double _deviations;
private readonly CircularBuffer _returns;
private readonly CircularBuffer _volatilities;
private double _prevClose;
private double _upperBound;
private double _lowerBound;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vc(int period = 20, double deviations = 2.0)
{
_period = period;
_deviations = deviations;
WarmupPeriod = period * 2; // Need enough data for stable statistics
Name = $"VC({_period},{_deviations})";
_returns = new CircularBuffer(period);
_volatilities = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vc(object source, int period = 20, double deviations = 2.0) : this(period, deviations)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_upperBound = 0;
_lowerBound = 0;
_returns.Clear();
_volatilities.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private double CalculateVariance(CircularBuffer buffer)
{
if (buffer.Count == 0) return 0;
double mean = buffer.Average();
double sumSquaredDiff = 0;
for (int i = 0; i < buffer.Count; i++)
{
double diff = buffer[i] - mean;
sumSquaredDiff += diff * diff;
}
return sumSquaredDiff / buffer.Count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate return
double ret = Math.Abs(_prevClose) > double.Epsilon ? Math.Log(BarInput.Close / _prevClose) : 0;
_returns.Add(ret);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough returns for volatility calculation
if (_index <= _period)
{
return 0;
}
// Calculate current volatility (annualized)
double vol = Math.Sqrt(CalculateVariance(_returns)) * Math.Sqrt(252);
_volatilities.Add(vol);
// Need enough volatilities for cone calculation
if (_index <= WarmupPeriod)
{
return vol;
}
// Calculate mean and standard deviation of volatilities
double meanVol = _volatilities.Average();
double stdVol = Math.Sqrt(CalculateVariance(_volatilities));
// Calculate bounds
_upperBound = meanVol + (_deviations * stdVol);
_lowerBound = Math.Max(0, meanVol - (_deviations * stdVol));
IsHot = _index >= WarmupPeriod;
return meanVol;
}
/// <summary>
/// Gets the upper bound of the volatility cone
/// </summary>
public double UpperBound => _upperBound;
/// <summary>
/// Gets the lower bound of the volatility cone
/// </summary>
public double LowerBound => _lowerBound;
}
+131
View File
@@ -0,0 +1,131 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VOV: Volatility of Volatility
/// A technical indicator that measures the volatility of volatility itself,
/// providing insight into the stability of market volatility.
/// </summary>
/// <remarks>
/// The VOV calculation process:
/// 1. Calculate primary volatility (e.g., using True Range)
/// 2. Calculate standard deviation of primary volatility
/// 3. Normalize result for comparison
///
/// Key characteristics:
/// - Second-order volatility measure
/// - Default period is 20 days
/// - Always positive
/// - No upper bound
/// - Measures volatility stability
///
/// Formula:
/// Primary Volatility = TR (True Range)
/// VOV = StdDev(Primary Volatility, period) / Average(Primary Volatility, period)
///
/// Market Applications:
/// - Risk of risk assessment
/// - Volatility regime changes
/// - Market stability analysis
/// - Trading strategy adaptation
/// - Risk management
///
/// Note: Higher values indicate more unstable volatility conditions
/// </remarks>
[SkipLocalsInit]
public sealed class Vov : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _volatilities;
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vov(int period = 20)
{
_period = period;
WarmupPeriod = period + 1; // Need extra period for TR calculation
Name = $"VOV({_period})";
_volatilities = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vov(object source, int period = 20) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_volatilities.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private double CalculateVariance(CircularBuffer buffer)
{
if (buffer.Count == 0) return 0;
double mean = buffer.Average();
double sumSquaredDiff = 0;
for (int i = 0; i < buffer.Count; i++)
{
double diff = buffer[i] - mean;
sumSquaredDiff += diff * diff;
}
return sumSquaredDiff / buffer.Count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate True Range as primary volatility measure
double tr = Math.Max(BarInput.High - BarInput.Low,
Math.Max(Math.Abs(BarInput.High - _prevClose),
Math.Abs(BarInput.Low - _prevClose)));
// Store current close for next calculation
_prevClose = BarInput.Close;
// Add volatility to buffer
_volatilities.Add(tr);
// Need enough volatilities for VOV calculation
if (_index <= _period)
{
return 0;
}
// Calculate mean volatility
double meanVol = _volatilities.Average();
// Calculate VOV (normalized standard deviation)
double vov = meanVol > double.Epsilon ? Math.Sqrt(CalculateVariance(_volatilities)) / meanVol : 0;
IsHot = _index >= WarmupPeriod;
return vov;
}
}
+135
View File
@@ -0,0 +1,135 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VR: Volatility Ratio
/// A technical indicator that compares volatility across different time periods
/// to identify changes in market conditions.
/// </summary>
/// <remarks>
/// The VR calculation process:
/// 1. Calculate short-term volatility
/// 2. Calculate long-term volatility
/// 3. Calculate ratio between them
///
/// Key characteristics:
/// - Relative volatility measure
/// - Default periods are 10 and 20 days
/// - Values above 1 indicate increasing volatility
/// - Values below 1 indicate decreasing volatility
/// - Normalized comparison
///
/// Formula:
/// Short Volatility = StdDev(Returns, shortPeriod)
/// Long Volatility = StdDev(Returns, longPeriod)
/// VR = Short Volatility / Long Volatility
///
/// Market Applications:
/// - Volatility regime changes
/// - Market condition analysis
/// - Risk assessment
/// - Trading strategy adaptation
/// - Trend confirmation
///
/// Note: Values significantly different from 1 indicate changing market conditions
/// </remarks>
[SkipLocalsInit]
public sealed class Vr : AbstractBase
{
private readonly int _longPeriod;
private readonly CircularBuffer _shortReturns;
private readonly CircularBuffer _longReturns;
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vr(int shortPeriod = 10, int longPeriod = 20)
{
_longPeriod = longPeriod;
WarmupPeriod = longPeriod + 1; // Need one extra period for returns
Name = $"VR({shortPeriod},{_longPeriod})";
_shortReturns = new CircularBuffer(shortPeriod);
_longReturns = new CircularBuffer(longPeriod);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vr(object source, int shortPeriod = 10, int longPeriod = 20) : this(shortPeriod, longPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_shortReturns.Clear();
_longReturns.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private double CalculateVariance(CircularBuffer buffer)
{
if (buffer.Count == 0) return 0;
double mean = buffer.Average();
double sumSquaredDiff = 0;
for (int i = 0; i < buffer.Count; i++)
{
double diff = buffer[i] - mean;
sumSquaredDiff += diff * diff;
}
return sumSquaredDiff / buffer.Count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate return
double ret = _prevClose > double.Epsilon ? Math.Log(BarInput.Close / _prevClose) : 0;
// Add return to buffers
_shortReturns.Add(ret);
_longReturns.Add(ret);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough returns for both periods
if (_index <= _longPeriod)
{
return 0;
}
// Calculate volatilities
double shortVol = Math.Sqrt(CalculateVariance(_shortReturns));
double longVol = Math.Sqrt(CalculateVariance(_longReturns));
// Calculate ratio
double vr = longVol > double.Epsilon ? shortVol / longVol : 1;
IsHot = _index >= WarmupPeriod;
return vr;
}
}
+155
View File
@@ -0,0 +1,155 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VS: Volatility Stop
/// A technical indicator that uses volatility to determine stop levels,
/// adapting to market conditions for dynamic risk management.
/// </summary>
/// <remarks>
/// The VS calculation process:
/// 1. Calculate Average True Range (ATR)
/// 2. Calculate stop levels:
/// Long Stop = Close - (multiplier * ATR)
/// Short Stop = Close + (multiplier * ATR)
/// 3. Trail stops based on price movement
///
/// Key characteristics:
/// - Adaptive stop levels
/// - Based on ATR volatility
/// - Default period is 14 days
/// - Returns both long and short stops
/// - Trails with price movement
///
/// Formula:
/// ATR = Average(TR, period)
/// Long Stop = Close - (multiplier * ATR)
/// Short Stop = Close + (multiplier * ATR)
///
/// Market Applications:
/// - Stop loss placement
/// - Position management
/// - Risk control
/// - Trend following
/// - Exit strategy
///
/// Sources:
/// Adaptation of Volatility-Based Stops concept
/// https://www.investopedia.com/terms/v/volatility-stop.asp
///
/// Note: Returns two values: long stop and short stop levels
/// </remarks>
[SkipLocalsInit]
public sealed class Vs : AbstractBase
{
private readonly int _period;
private readonly double _multiplier;
private readonly CircularBuffer _tr;
private double _prevClose;
private double _longStop;
private double _shortStop;
private double _prevLongStop;
private double _prevShortStop;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vs(int period = 14, double multiplier = 2.0)
{
_period = period;
_multiplier = multiplier;
WarmupPeriod = period + 1; // Need one extra period for TR
Name = $"VS({_period},{_multiplier})";
_tr = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vs(object source, int period = 14, double multiplier = 2.0) : this(period, multiplier)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_longStop = 0;
_shortStop = 0;
_prevLongStop = 0;
_prevShortStop = 0;
_tr.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
_longStop = BarInput.Close;
_shortStop = BarInput.Close;
return 0;
}
// Calculate True Range
double tr = Math.Max(BarInput.High - BarInput.Low,
Math.Max(Math.Abs(BarInput.High - _prevClose),
Math.Abs(BarInput.Low - _prevClose)));
// Add TR to buffer
_tr.Add(tr);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough values for ATR calculation
if (_index <= _period)
{
return 0;
}
// Calculate ATR
double atr = _tr.Average();
// Calculate initial stop levels
double potentialLongStop = BarInput.Close - (_multiplier * atr);
double potentialShortStop = BarInput.Close + (_multiplier * atr);
// Trail stops
_longStop = BarInput.Close > _prevShortStop ? potentialLongStop : Math.Max(potentialLongStop, _prevLongStop);
_shortStop = BarInput.Close < _prevLongStop ? potentialShortStop : Math.Min(potentialShortStop, _prevShortStop);
// Store current stops for next calculation
_prevLongStop = _longStop;
_prevShortStop = _shortStop;
IsHot = _index >= WarmupPeriod;
return _longStop; // Return long stop as primary value
}
/// <summary>
/// Gets the long stop level
/// </summary>
public double LongStop => _longStop;
/// <summary>
/// Gets the short stop level
/// </summary>
public double ShortStop => _shortStop;
}
+14 -13
View File
@@ -1,37 +1,38 @@
# Volatility indicators
Done: 6, Todo: 25
Done: 11, Todo: 24
ADR - Average Daily Range
AP - Andrew's Pitchfork
✔️ ATR - Average True Range
ATRP - Average True Range Percent
ATRS - ATR Trailing Stop
BB - Bollinger Bands®
*BB - Bollinger Bands® (Upper, Middle, Lower)
CCV - Close-to-Close Volatility
CE - Chandelier Exit
CV - Conditional Volatility (ARCH/GARCH)
CVI - Chaikin's Volatility
DC - Donchian Channels
*DC - Donchian Channels (Upper, Middle, Lower)
EWMA - Exponential Weighted Moving Average Volatility
FCB - Fractal Chaos Bands
GKV - Garman-Klass Volatility
HLV - High-Low Volatility
✔️ HV - Historical Volatility
ICH - Ichimoku Cloud
*ICH - Ichimoku Cloud (Conversion, Base, Leading Span A, Leading Span B, Lagging Span)
✔️ JVOLTY - Jurik Volatility
KC - Keltner Channels
*KC - Keltner Channels (Upper, Middle, Lower)
NATR - Normalized Average True Range
PCH - Price Channel Indicator
PSAR - Parabolic Stop and Reverse
*PSAR - Parabolic Stop and Reverse (Value, Trend)
PV - Parkinson Volatility
RSV - Rogers-Satchell Volatility
✔️ RV - Realized Volatility
✔️ RVI - Relative Volatility Index
STARC - Starc Bands
*STARC - Starc Bands (Upper, Middle, Lower)
SV - Stochastic Volatility
TR - True Range
UI - Ulcer Index
VC - Volatility Cone
VOV - Volatility of Volatility
VR - Volatility Ratio
VS - Volatility Stop
✔️ TR - True Range
✔️ UI - Ulcer Index
✔️ *VC - Volatility Cone (Mean, Upper Bound, Lower Bound)
✔️ VOV - Volatility of Volatility
✔️ VR - Volatility Ratio
✔️ *VS - Volatility Stop (Long Stop, Short Stop)
YZV - Yang-Zhang Volatility
-29
View File
@@ -1,29 +0,0 @@
# Volatility Measures
## Single Value Input (Typically Closing Prices)
- **Jurik Volatility (Volty)**
- **Standard Deviation**
- **RVI Relative Volatility Index**
- **CMO Chande Momentum Oscillator**
- **Historical Volatility**
- **Average True Range (ATR) (High, Low, Close)**
- Normalized ATR
- Ulcer Index
- ARCH/GARCH Models
- Exponential Weighted Moving Average (EWMA) Volatility
- Conditional Volatility
- Volatility Ratio
- Close-to-Close Volatility
- Volatility of Volatility (VOV)
- Volatility Cone
- Bollinger Bands
- Stochastic Volatility: Typically modeled using closing prices, but can incorporate other price information
- Garman-Klass Volatility
- Rogers-Satchell Volatility
- Yang-Zhang Volatility
- Parkinson Volatility (High, Low)
- Chaikin Volatility (High, Low)
- Keltner Channels (typically Close, High, Low)
- High-Low Volatility (High, Low)
+141
View File
@@ -0,0 +1,141 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// MFI: Money Flow Index
/// A volume-weighted momentum indicator that measures the inflow and outflow of money into an asset
/// over a specific period of time. It's sometimes referred to as volume-weighted RSI.
/// </summary>
/// <remarks>
/// The MFI calculation process:
/// 1. Calculate Typical Price:
/// TP = (High + Low + Close) / 3
/// 2. Calculate Raw Money Flow:
/// RMF = TP * Volume
/// 3. Determine Positive/Negative Money Flow:
/// If TP > Previous TP: Positive Money Flow
/// If TP < Previous TP: Negative Money Flow
/// 4. Calculate Money Flow Ratio:
/// MFR = (14-period Positive Money Flow Sum) / (14-period Negative Money Flow Sum)
/// 5. Calculate Money Flow Index:
/// MFI = 100 - (100 / (1 + MFR))
///
/// Key characteristics:
/// - Oscillates between 0 and 100
/// - Default period is 14 days
/// - Overbought level typically at 80
/// - Oversold level typically at 20
/// - Volume-weighted measure
///
/// Formula:
/// TP = (High + Low + Close) / 3
/// RMF = TP * Volume
/// MFR = ΣPositive Money Flow / ΣNegative Money Flow
/// MFI = 100 - (100 / (1 + MFR))
///
/// Market Applications:
/// - Overbought/Oversold conditions
/// - Divergence analysis
/// - Trend confirmation
/// - Price reversals
/// - Volume flow analysis
///
/// Sources:
/// Gene Quong and Avrum Soudack - Original development
/// https://www.investopedia.com/terms/m/mfi.asp
///
/// Note: Values above 80 indicate overbought conditions, while values below 20 indicate oversold conditions
/// </remarks>
[SkipLocalsInit]
public sealed class Mfi : AbstractBase
{
private readonly CircularBuffer _posMf;
private readonly CircularBuffer _negMf;
private double _prevTp;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Mfi(int period = 14)
{
WarmupPeriod = period + 1; // Need one extra period for previous TP
Name = $"MFI({period})";
_posMf = new CircularBuffer(period);
_negMf = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Mfi(object source, int period = 14) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevTp = 0;
_posMf.Clear();
_negMf.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate Typical Price
double tp = (BarInput.High + BarInput.Low + BarInput.Close) / 3;
// Skip first period to establish previous TP
if (_index == 1)
{
_prevTp = tp;
return 0;
}
// Calculate Raw Money Flow
double rmf = tp * BarInput.Volume;
// Determine Positive/Negative Money Flow
if (tp > _prevTp)
{
_posMf.Add(rmf);
_negMf.Add(0);
}
else if (tp < _prevTp)
{
_posMf.Add(0);
_negMf.Add(rmf);
}
else
{
_posMf.Add(0);
_negMf.Add(0);
}
// Store current TP for next calculation
_prevTp = tp;
// Calculate Money Flow Ratio and Index
double posMfSum = _posMf.Sum();
double negMfSum = _negMf.Sum();
double mfi = Math.Abs(negMfSum) < double.Epsilon ? 100 : 100 - (100 / (1 + (posMfSum / negMfSum)));
IsHot = _index >= WarmupPeriod;
return mfi;
}
}
+114
View File
@@ -0,0 +1,114 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// NVI: Negative Volume Index
/// A cumulative indicator that focuses on days when volume decreases from the previous day.
/// It is based on the premise that smart money is active on days with lower volume.
/// </summary>
/// <remarks>
/// The NVI calculation process:
/// 1. Compare current volume with previous volume
/// 2. If current volume is less than previous volume:
/// NVI = Previous NVI + (((Close - Previous Close) / Previous Close) * Previous NVI)
/// 3. If current volume is greater than or equal to previous volume:
/// NVI = Previous NVI
///
/// Key characteristics:
/// - Cumulative indicator
/// - Only updates on lower volume days
/// - Starts at base value of 1000
/// - Focuses on smart money activity
/// - Volume-driven measure
///
/// Formula:
/// If Volume < Previous Volume:
/// NVI = Previous NVI + (Price % Change * Previous NVI)
/// Else:
/// NVI = Previous NVI
///
/// Market Applications:
/// - Smart money tracking
/// - Trend identification
/// - Market timing
/// - Volume analysis
/// - Price confirmation
///
/// Sources:
/// Paul Dysart - Original development (1930s)
/// Norman Fosback - Further development
/// https://www.investopedia.com/terms/n/nvi.asp
///
/// Note: Rising NVI suggests smart money is buying, while falling NVI suggests smart money is selling
/// </remarks>
[SkipLocalsInit]
public sealed class Nvi : AbstractBase
{
private double _prevClose;
private double _prevVolume;
private double _prevNvi;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Nvi()
{
WarmupPeriod = 2; // Need previous volume and close
Name = "NVI";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Nvi(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVolume = 0;
_prevNvi = 1000; // Standard starting value
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous values
if (_index == 1)
{
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
return _prevNvi;
}
// Calculate NVI
if (BarInput.Volume < _prevVolume)
{
double priceChange = ((BarInput.Close - _prevClose) / _prevClose);
_prevNvi += priceChange * _prevNvi;
}
// Store current values for next calculation
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
IsHot = _index >= WarmupPeriod;
return _prevNvi;
}
}
+117
View File
@@ -0,0 +1,117 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// OBV: On-Balance Volume
/// A momentum indicator that uses volume flow to predict changes in stock price.
/// It accumulates volume on up days and subtracts volume on down days.
/// </summary>
/// <remarks>
/// The OBV calculation process:
/// 1. Compare current close with previous close
/// 2. If current close is higher:
/// OBV = Previous OBV + Current Volume
/// 3. If current close is lower:
/// OBV = Previous OBV - Current Volume
/// 4. If current close equals previous close:
/// OBV = Previous OBV
///
/// Key characteristics:
/// - Cumulative indicator
/// - Volume-based momentum measure
/// - Leading indicator
/// - No upper or lower bounds
/// - Focuses on volume flow
///
/// Formula:
/// If Close > Previous Close:
/// OBV = Previous OBV + Volume
/// If Close < Previous Close:
/// OBV = Previous OBV - Volume
/// If Close = Previous Close:
/// OBV = Previous OBV
///
/// Market Applications:
/// - Trend confirmation
/// - Potential breakouts
/// - Divergence analysis
/// - Volume flow analysis
/// - Price movement prediction
///
/// Sources:
/// Joe Granville - Original development (1963)
/// https://www.investopedia.com/terms/o/onbalancevolume.asp
///
/// Note: Rising OBV suggests buying pressure, while falling OBV suggests selling pressure
/// </remarks>
[SkipLocalsInit]
public sealed class Obv : AbstractBase
{
private double _prevClose;
private double _prevObv;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Obv()
{
WarmupPeriod = 2; // Need previous close
Name = "OBV";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Obv(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevObv = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate OBV
if (BarInput.Close > _prevClose)
{
_prevObv += BarInput.Volume;
}
else if (BarInput.Close < _prevClose)
{
_prevObv -= BarInput.Volume;
}
// If prices equal, OBV remains the same
// Store current close for next calculation
_prevClose = BarInput.Close;
IsHot = _index >= WarmupPeriod;
return _prevObv;
}
}
+113
View File
@@ -0,0 +1,113 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVI: Positive Volume Index
/// A cumulative indicator that focuses on days when volume increases from the previous day.
/// It is based on the premise that the public is active on days with higher volume.
/// </summary>
/// <remarks>
/// The PVI calculation process:
/// 1. Compare current volume with previous volume
/// 2. If current volume is greater than previous volume:
/// PVI = Previous PVI + (((Close - Previous Close) / Previous Close) * Previous PVI)
/// 3. If current volume is less than or equal to previous volume:
/// PVI = Previous PVI
///
/// Key characteristics:
/// - Cumulative indicator
/// - Only updates on higher volume days
/// - Starts at base value of 1000
/// - Focuses on public activity
/// - Volume-driven measure
///
/// Formula:
/// If Volume > Previous Volume:
/// PVI = Previous PVI + (Price % Change * Previous PVI)
/// Else:
/// PVI = Previous PVI
///
/// Market Applications:
/// - Public participation tracking
/// - Trend identification
/// - Market timing
/// - Volume analysis
/// - Price confirmation
///
/// Sources:
/// Norman Fosback - Original development
/// https://www.investopedia.com/terms/p/pvi.asp
///
/// Note: Rising PVI suggests public buying pressure, while falling PVI suggests public selling pressure
/// </remarks>
[SkipLocalsInit]
public sealed class Pvi : AbstractBase
{
private double _prevClose;
private double _prevVolume;
private double _prevPvi;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvi()
{
WarmupPeriod = 2; // Need previous volume and close
Name = "PVI";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvi(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVolume = 0;
_prevPvi = 1000; // Standard starting value
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous values
if (_index == 1)
{
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
return _prevPvi;
}
// Calculate PVI
if (BarInput.Volume > _prevVolume)
{
double priceChange = ((BarInput.Close - _prevClose) / _prevClose);
_prevPvi += priceChange * _prevPvi;
}
// Store current values for next calculation
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
IsHot = _index >= WarmupPeriod;
return _prevPvi;
}
}
+111
View File
@@ -0,0 +1,111 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVO: Percentage Volume Oscillator
/// A momentum indicator for volume that shows the relationship between two volume moving averages
/// as a percentage. Similar to the Price Oscillator but uses volume instead of price.
/// </summary>
/// <remarks>
/// The PVO calculation process:
/// 1. Calculate short-term EMA of volume
/// 2. Calculate long-term EMA of volume
/// 3. Calculate PVO:
/// PVO = ((Short EMA - Long EMA) / Long EMA) * 100
///
/// Key characteristics:
/// - Volume-based momentum indicator
/// - Oscillates around zero
/// - Shows volume trends
/// - Default periods are 12 and 26 days
/// - Percentage-based measure
///
/// Formula:
/// Short EMA = EMA(Volume, shortPeriod)
/// Long EMA = EMA(Volume, longPeriod)
/// PVO = ((Short EMA - Long EMA) / Long EMA) * 100
///
/// Market Applications:
/// - Volume trend analysis
/// - Divergence identification
/// - Volume momentum measurement
/// - Market tops and bottoms
/// - Trading volume patterns
///
/// Sources:
/// https://www.investopedia.com/terms/p/pvo.asp
///
/// Note: Positive values indicate higher short-term volume, while negative values indicate higher long-term volume
/// </remarks>
[SkipLocalsInit]
public sealed class Pvo : AbstractBase
{
private readonly int _longPeriod;
private double _shortEma;
private double _longEma;
private readonly double _shortAlpha;
private readonly double _longAlpha;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvo(int shortPeriod = 12, int longPeriod = 26)
{
_longPeriod = longPeriod;
WarmupPeriod = longPeriod;
Name = $"PVO({shortPeriod},{_longPeriod})";
_shortAlpha = 2.0 / (shortPeriod + 1);
_longAlpha = 2.0 / (longPeriod + 1);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvo(object source, int shortPeriod = 12, int longPeriod = 26) : this(shortPeriod, longPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_shortEma = 0;
_longEma = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Initialize or update EMAs
if (_index <= _longPeriod)
{
_shortEma = BarInput.Volume;
_longEma = BarInput.Volume;
return 0;
}
// Update EMAs
_shortEma = (_shortAlpha * BarInput.Volume) + ((1 - _shortAlpha) * _shortEma);
_longEma = (_longAlpha * BarInput.Volume) + ((1 - _longAlpha) * _longEma);
// Calculate PVO
double pvo = Math.Abs(_longEma) >= double.Epsilon ? ((_shortEma - _longEma) / _longEma) * 100 : 0;
IsHot = _index >= WarmupPeriod;
return pvo;
}
}
+108
View File
@@ -0,0 +1,108 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVOL: Price-Volume
/// A technical indicator that measures the relationship between price and volume changes,
/// helping to identify the strength of price movements.
/// </summary>
/// <remarks>
/// The PVOL calculation process:
/// 1. Calculate price change:
/// Price Change = (Close - Previous Close) / Previous Close
/// 2. Calculate volume change:
/// Volume Change = (Volume - Previous Volume) / Previous Volume
/// 3. Calculate PVOL:
/// PVOL = Price Change * Volume Change * 100
///
/// Key characteristics:
/// - Measures price-volume relationship
/// - Oscillates around zero
/// - Shows momentum strength
/// - Identifies volume-supported moves
/// - No specific boundaries
///
/// Formula:
/// Price Change = (Close - Previous Close) / Previous Close
/// Volume Change = (Volume - Previous Volume) / Previous Volume
/// PVOL = Price Change * Volume Change * 100
///
/// Market Applications:
/// - Price movement confirmation
/// - Volume analysis
/// - Trend strength assessment
/// - Divergence identification
/// - Market momentum analysis
///
/// Note: High positive values indicate strong upward momentum with volume support,
/// while high negative values indicate strong downward momentum with volume support
/// </remarks>
[SkipLocalsInit]
public sealed class Pvol : AbstractBase
{
private double _prevClose;
private double _prevVolume;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvol()
{
WarmupPeriod = 2; // Need previous close and volume
Name = "PVOL";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvol(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVolume = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous values
if (_index == 1)
{
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
return 0;
}
// Calculate price and volume changes
double priceChange = (Math.Abs(_prevClose) >= double.Epsilon) ? (BarInput.Close - _prevClose) / _prevClose : 0;
double volumeChange = (Math.Abs(_prevVolume) >= double.Epsilon) ? (BarInput.Volume - _prevVolume) / _prevVolume : 0;
// Store current values for next calculation
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
// Calculate PVOL
double pvol = priceChange * volumeChange * 100;
IsHot = _index >= WarmupPeriod;
return pvol;
}
}
+109
View File
@@ -0,0 +1,109 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVR: Price Volume Rank
/// A technical indicator that ranks price and volume movements to identify
/// significant market moves based on their combined strength.
/// </summary>
/// <remarks>
/// The PVR calculation process:
/// 1. Calculate price change percentage:
/// Price Change = ((Close - Previous Close) / Previous Close) * 100
/// 2. Calculate volume ratio:
/// Volume Ratio = Current Volume / Previous Volume
/// 3. Calculate PVR:
/// PVR = Price Change * Volume Ratio
///
/// Key characteristics:
/// - Combines price and volume analysis
/// - No specific boundaries
/// - Measures movement significance
/// - Volume-weighted price change
/// - Identifies strong moves
///
/// Formula:
/// Price Change = ((Close - Previous Close) / Previous Close) * 100
/// Volume Ratio = Volume / Previous Volume
/// PVR = Price Change * Volume Ratio
///
/// Market Applications:
/// - Significant move identification
/// - Volume-supported moves
/// - Trend strength analysis
/// - Breakout confirmation
/// - Market momentum measurement
///
/// Note: Higher absolute values indicate more significant price moves with volume support
/// </remarks>
[SkipLocalsInit]
public sealed class Pvr : AbstractBase
{
private double _prevClose;
private double _prevVolume;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvr()
{
WarmupPeriod = 2; // Need previous close and volume
Name = "PVR";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvr(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVolume = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous values
if (_index == 1)
{
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
return 0;
}
// Calculate price change percentage
double priceChange = (Math.Abs(_prevClose) > double.Epsilon) ? ((BarInput.Close - _prevClose) / _prevClose) * 100 : 0;
// Calculate volume ratio
double volumeRatio = (Math.Abs(_prevVolume) > double.Epsilon) ? BarInput.Volume / _prevVolume : 1;
// Store current values for next calculation
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
// Calculate PVR
double pvr = priceChange * volumeRatio;
IsHot = _index >= WarmupPeriod;
return pvr;
}
}
+105
View File
@@ -0,0 +1,105 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVT: Price Volume Trend
/// A momentum indicator that combines price and volume to determine the strength of a trend.
/// Similar to OBV but uses percentage price changes in its calculation.
/// </summary>
/// <remarks>
/// The PVT calculation process:
/// 1. Calculate price change percentage:
/// Price Change = (Close - Previous Close) / Previous Close
/// 2. Calculate PVT:
/// PVT = Previous PVT + (Price Change * Volume)
///
/// Key characteristics:
/// - Cumulative indicator
/// - Volume-weighted price changes
/// - No upper or lower bounds
/// - Trend strength measure
/// - More sensitive than OBV
///
/// Formula:
/// Price Change = (Close - Previous Close) / Previous Close
/// PVT = Previous PVT + (Price Change * Volume)
///
/// Market Applications:
/// - Trend confirmation
/// - Divergence analysis
/// - Volume-price relationships
/// - Support/resistance levels
/// - Market momentum
///
/// Sources:
/// Norman Fosback - Original development
/// https://www.investopedia.com/terms/p/pvt.asp
///
/// Note: Rising PVT suggests buying pressure, while falling PVT suggests selling pressure
/// </remarks>
[SkipLocalsInit]
public sealed class Pvt : AbstractBase
{
private double _prevClose;
private double _prevPvt;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvt()
{
WarmupPeriod = 2; // Need previous close
Name = "PVT";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvt(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevPvt = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate price change percentage
double priceChange = (Math.Abs(_prevClose) > double.Epsilon) ? (BarInput.Close - _prevClose) / _prevClose : 0;
// Calculate PVT
_prevPvt += priceChange * BarInput.Volume;
// Store current close for next calculation
_prevClose = BarInput.Close;
IsHot = _index >= WarmupPeriod;
return _prevPvt;
}
}
+112
View File
@@ -0,0 +1,112 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// TVI: Trade Volume Index
/// A technical indicator that determines whether a security is being accumulated or distributed
/// based on price changes relative to a minimum tick value.
/// </summary>
/// <remarks>
/// The TVI calculation process:
/// 1. Calculate price change:
/// Price Change = Close - Previous Close
/// 2. Compare price change to minimum tick value:
/// If |Price Change| >= Minimum Tick:
/// Add/Subtract volume based on price direction
///
/// Key characteristics:
/// - Volume-based trend indicator
/// - Uses minimum tick value
/// - Cumulative measure
/// - No upper or lower bounds
/// - Focuses on significant moves
///
/// Formula:
/// If |Close - Previous Close| >= Minimum Tick:
/// If Close > Previous Close:
/// TVI = Previous TVI + Volume
/// If Close < Previous Close:
/// TVI = Previous TVI - Volume
/// Else:
/// TVI = Previous TVI
///
/// Market Applications:
/// - Trend identification
/// - Volume analysis
/// - Accumulation/distribution
/// - Price movement significance
/// - Trading signal generation
///
/// Note: Rising TVI suggests accumulation, while falling TVI suggests distribution
/// </remarks>
[SkipLocalsInit]
public sealed class Tvi : AbstractBase
{
private readonly double _minTick;
private double _prevClose;
private double _prevTvi;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tvi(double minTick = 0.5)
{
_minTick = minTick;
WarmupPeriod = 2; // Need previous close
Name = $"TVI({_minTick})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tvi(object source, double minTick = 0.5) : this(minTick)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevTvi = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate price change
double priceChange = BarInput.Close - _prevClose;
// Update TVI if price change exceeds minimum tick
if (Math.Abs(priceChange) >= _minTick)
{
_prevTvi += priceChange > 0 ? BarInput.Volume : -BarInput.Volume;
}
// Store current close for next calculation
_prevClose = BarInput.Close;
IsHot = _index >= WarmupPeriod;
return _prevTvi;
}
}
+11 -10
View File
@@ -1,5 +1,5 @@
# Volume indicators
Done: 6, Todo: 12
Done: 15, Todo: 3
✔️ ADL - Chaikin Accumulation Distribution Line
✔️ ADOSC - Chaikin Accumulation Distribution Oscillator
@@ -7,15 +7,16 @@ Done: 6, Todo: 12
✔️ CMF - Chaikin Money Flow
✔️ EOM - Ease of Movement
✔️ KVO - Klinger Volume Oscillator
MFI - Money Flow Index
NVI - Negative Volume Index
OBV - On-Balance Volume
PVI - Positive Volume Index
PVOL - Price-Volume
PVO - Percentage Volume Oscillator
PVR - Price Volume Rank
PVT - Price Volume Trend
TVI - Trade Volume Index
✔️ MFI - Money Flow Index
✔️ NVI - Negative Volume Index
✔️ OBV - On-Balance Volume
✔️ PVI - Positive Volume Index
✔️ PVOL - Price-Volume
✔️ PVO - Percentage Volume Oscillator
✔️ PVR - Price Volume Rank
✔️ PVT - Price Volume Trend
✔️ TVI - Trade Volume Index
VF - Volume Force
VP - Volume Profile
VWAP - Volume Weighted Average Price
VWMA - Volume Weighted Moving Average