mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-27 17:27:43 +00:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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`|
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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-)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user