diff --git a/Tests/Validations/Trends/Pandas_TA.cs b/Tests/Validations/Trends/Pandas_TA.cs index 611d9efe..34b56c1b 100644 --- a/Tests/Validations/Trends/Pandas_TA.cs +++ b/Tests/Validations/Trends/Pandas_TA.cs @@ -9,7 +9,7 @@ public class PandasTA : IDisposable { private readonly GBM_Feed bars; private readonly Random rnd = new(); - private readonly int period, sample; + private readonly int period, skip; private int digits; private readonly string dllpath; private readonly dynamic np; @@ -20,8 +20,8 @@ public class PandasTA : IDisposable public PandasTA() { bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0); period = rnd.Next(maxValue: 28) + 3; - sample = period+1; - digits = 10; + skip = period+10; + digits = 8; Installer.InstallPath = Path.GetFullPath(path: "."); Installer.SetupPython().Wait(); @@ -30,10 +30,7 @@ public class PandasTA : IDisposable Installer.PipInstallModule(module_name: "pandas"); Installer.PipInstallModule(module_name: "pandas-ta"); dllpath = Installer.InstallPath + "\\" + Installer.InstallDirectory + "\\" + Runtime.PythonDLL; - Runtime.PythonDLL = dllpath; - //Runtime.PythonDLL = "python.dll"; - PythonEngine.Initialize(); np = Py.Import(name: "numpy"); @@ -60,7 +57,7 @@ public class PandasTA : IDisposable [Fact] void ADL() { ADL_Series QL = new(bars); var pta = df.ta.ad(high: df.high, low: df.low, close:df.close, volume:df.volume); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i-1].v; double PanTA_item = (double)pta[i-1]; @@ -72,7 +69,7 @@ public class PandasTA : IDisposable [Fact] void ADOSC() { ADOSC_Series QL = new(bars); var pta = df.ta.adosc(high: df.high, low: df.low, close: df.close, volume: df.volume); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -82,7 +79,7 @@ public class PandasTA : IDisposable [Fact] void ATR() { ATR_Series QL = new(bars, period); var pta = df.ta.atr(high: df.high, low: df.low, close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -93,7 +90,7 @@ public class PandasTA : IDisposable void BBANDS() { BBANDS_Series QL = new(bars.Close, period); var pta = df.ta.bbands(close: df.close, length: period).to_numpy(); - for (int i = QL.Length-1; i > QL.Length - sample; i--) { + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL.Lower[i].v; double PanTA_item = (double)pta[i][0]; //lower Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); @@ -108,7 +105,7 @@ public class PandasTA : IDisposable [Fact] void BIAS() { BIAS_Series QL = new(bars.Close, period, false); var pta = df.ta.bias(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -119,7 +116,7 @@ public class PandasTA : IDisposable void CCI() { CCI_Series QL = new(bars, period, false); var pta = df.ta.cci(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length - sample; i--) { + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); @@ -130,7 +127,7 @@ public class PandasTA : IDisposable void CMO() { CMO_Series QL = new(bars.Close, period, false); var pta = df.ta.cmo(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length - sample; i--) { + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); @@ -140,7 +137,7 @@ public class PandasTA : IDisposable [Fact] void DEMA() { DEMA_Series QL = new(bars.Close, period, false); var pta = df.ta.dema(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -150,7 +147,7 @@ public class PandasTA : IDisposable [Fact] void EMA() { EMA_Series QL = new(bars.Close, period, false); var pta = df.ta.ema(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -160,7 +157,7 @@ public class PandasTA : IDisposable [Fact] void ENTROPY() { ENTROPY_Series QL = new(bars.Close, period, useNaN: false); var pta = df.ta.entropy(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -169,7 +166,7 @@ public class PandasTA : IDisposable } [Fact] void HL2() { var pta = df.ta.hl2(high: df.high, low: df.low); - for (int i = bars.HL2.Length; i > bars.HL2.Length-sample; i--) + for (int i = bars.HL2.Length-1; i > skip; i--) { double QL_item = bars.HL2[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -178,7 +175,7 @@ public class PandasTA : IDisposable } [Fact] void HLC3() { var pta = df.ta.hlc3(high: df.high, low: df.low, close: df.close); - for (int i = bars.HLC3.Length; i > bars.HLC3.Length-sample; i--) + for (int i = bars.HLC3.Length; i > skip; i--) { double QL_item = bars.HLC3[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -188,7 +185,7 @@ public class PandasTA : IDisposable [Fact] void HMA() { HMA_Series QL = new(bars.Close, period, false); var pta = df.ta.hma(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -199,7 +196,7 @@ public class PandasTA : IDisposable [Fact] void HWMA() { HWMA_Series QL = new(bars.Close, useNaN: false); var pta = df.ta.hwma(close: df.close); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -210,7 +207,7 @@ public class PandasTA : IDisposable [Fact] void KAMA() { KAMA_Series QL = new(bars.Close, period); var pta = df.ta.kama(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -220,7 +217,7 @@ public class PandasTA : IDisposable [Fact] void KURTOSIS() { KURTOSIS_Series QL = new(bars.Close, period, useNaN: false); var pta = df.ta.kurtosis(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -231,7 +228,7 @@ public class PandasTA : IDisposable void MACD() { MACD_Series QL = new(bars.Close, 26,fast: 12,signal:9); var pta = df.ta.macd(close: df.close).to_numpy(); - for (int i = QL.Length; i > QL.Length - sample; i--) { + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1][0]; Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); @@ -244,7 +241,7 @@ public class PandasTA : IDisposable { MAD_Series QL = new(bars.Close, period, useNaN: false); var pta = df.ta.mad(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -254,7 +251,7 @@ public class PandasTA : IDisposable [Fact] void MEDIAN() { MEDIAN_Series QL = new(bars.Close, period); var pta = df.ta.median(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -264,7 +261,7 @@ public class PandasTA : IDisposable [Fact] void OBV() { OBV_Series QL = new(bars); var pta = df.ta.obv(close: df.close, volume: df.volume); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -273,7 +270,7 @@ public class PandasTA : IDisposable } [Fact] void OHLC4() { var pta = df.ta.ohlc4(open: df.open, high: df.high, low: df.low, close: df.close); - for (int i = bars.OHLC4.Length; i > bars.OHLC4.Length-sample; i--) + for (int i = bars.OHLC4.Length; i > skip; i--) { double QL_item = bars.OHLC4[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -283,7 +280,7 @@ public class PandasTA : IDisposable [Fact] void RMA() { RMA_Series QL = new(bars.Close, period, false); var pta = df.ta.rma(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -293,7 +290,7 @@ public class PandasTA : IDisposable [Fact] void RSI() { RSI_Series QL = new(bars.Close, period); var pta = df.ta.rsi(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -303,7 +300,7 @@ public class PandasTA : IDisposable [Fact] void SDEV() { SDEV_Series QL = new(bars.Close, period, useNaN: false); var pta = df.ta.stdev(close: df.close, length: period, ddof: 0); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -313,7 +310,7 @@ public class PandasTA : IDisposable [Fact] void SMA() { SMA_Series QL = new(bars.Close, period, false); var pta = df.ta.sma(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -323,7 +320,7 @@ public class PandasTA : IDisposable [Fact] void SSDEV() { SSDEV_Series QL = new(bars.Close, period, useNaN: false); var pta = df.ta.stdev(close: df.close, length: period, ddof: 1); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -333,7 +330,7 @@ public class PandasTA : IDisposable [Fact] void SVARIANCE() { SVAR_Series QL = new(bars.Close, period); var pta = df.ta.variance(close: df.close, length: period, ddof: 1); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -344,7 +341,7 @@ public class PandasTA : IDisposable [Fact] void T3() { T3_Series QL = new(source: bars.Close, period: period, vfactor: 0.7, useNaN: false); var pta = df.ta.t3(close: df.close, length: period, a: 0.7); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -354,7 +351,7 @@ public class PandasTA : IDisposable [Fact] void TEMA() { TEMA_Series QL = new(bars.Close, period, false); var pta = df.ta.tema(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -364,7 +361,7 @@ public class PandasTA : IDisposable [Fact] void TR() { TR_Series QL = new(bars); var pta = df.ta.true_range(high: df.high, low: df.low, close: df.close); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -375,7 +372,7 @@ public class PandasTA : IDisposable // TODO: return length to variable length (period) when Pandas-TA fixes trima to calculate even periods right TRIMA_Series QL = new(bars.Close, 11); var pta = df.ta.trima(close: df.close, length: 11); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -385,7 +382,7 @@ public class PandasTA : IDisposable [Fact] void TRIX() { TRIX_Series QL = new(bars.Close, period); var pta = df.ta.trix(close: df.close, length: period).to_numpy(); - for (int i = QL.Length; i > QL.Length - sample; i--) { + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1][0]; Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); @@ -394,7 +391,7 @@ public class PandasTA : IDisposable [Fact] void VARIANCE() { VAR_Series QL = new(bars.Close, period); var pta = df.ta.variance(close: df.close, length: period, ddof:0); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -404,7 +401,7 @@ public class PandasTA : IDisposable [Fact] void WMA() { WMA_Series QL = new(bars.Close, period, false); var pta = df.ta.wma(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -414,7 +411,7 @@ public class PandasTA : IDisposable [Fact] void ZLEMA() { ZLEMA_Series QL = new(bars.Close, period, false); var pta = df.ta.zlma(close: df.close, length: period); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; @@ -424,7 +421,7 @@ public class PandasTA : IDisposable [Fact] void ZSCORE() { ZSCORE_Series QL = new(bars.Close, period, useNaN: false); var pta = df.ta.zscore(close: df.close, length: period, ddof: 0); - for (int i = QL.Length; i > QL.Length-sample; i--) + for (int i = QL.Length-1; i > skip; i--) { double QL_item = QL[i - 1].v; double PanTA_item = (double)pta[i - 1]; diff --git a/docs/SMA.md b/docs/SMA.md index 8a1d0707..94b1da1d 100644 --- a/docs/SMA.md +++ b/docs/SMA.md @@ -1,9 +1,5 @@ # SMA: Simple Moving Average -period = 10 - -![Alt text](./img/SMA_chart.svg) - SMA is is an arithmetic moving average where the weights in SMA are **equally** distributed across the given period, resulting in a mean() of the data within the period. ## Calculation @@ -12,38 +8,50 @@ SMA is a rolling calculation that is looking backwards from the position ${n}$ a $$ SMA_p{(data)} = \frac{1}{p}\sum_{i=n-p+1}^{n} data_i $$ -When calculating the value of the next $SMA_{p,next}$ while knowing all previous SMA values, SMA calculation can be reduced to: +When calculating the value of the next $SMA_{p,next}$ while knowing previous SMA values, SMA calculation can be reduced to: $$ SMA_{p,next} = SMA_{p,prev}+\frac{1}{p}\left( data_{n+1}-data_{n+1-p}\right) $$ -## Reference Calculation +## Implementation +`TSeries SMA_Series (TSeries source, int period = 0, bool useNaN = false)` + +- SMA_Series returns TSeries list +- `source`: input of type TSeries; SMA_Series automatically subscribes to events of new data added to the source +- `period`: optional size of a lookback window; if set to 0, SMA calculates cumulative average across the whole source +- `useNaN`: if set to _true_, SMA_Series will hide values within the initial period with NaN (for compatibility with other libraries) + +[Link to source](..\Source\Trends\SMA_Series.cs) + +## Behavior +![Alt text](./img/SMA_chart.svg) +## Reference Calculation & Validation period = 5 ``` TSeries data = new() {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29}; SMA_Series sma = new(data, 5, useNaN: false); -SMA_Series sma_nan = new(data, 5, useNaN: true); -for (int i=0; i< data.Count; i++) - Console.WriteLine($"{i}\t{data[i].v,7:f2}\t{sma_nan[i].v,7:f3}\t{sma[i].v,7:f3}"); ``` -|#|input|sma_NaN|sma| -|--|:--:|:--:|:--:| -|0| 81.59| NaN| 81.590| -|1| 81.06| NaN| 81.325| -|2| 82.87| NaN| 81.840| -|3| 83.00| NaN| 82.130| -|4| 83.61| 82.426| 82.426| -|5| 83.15| 82.738| 82.738| -|6| 82.84| 83.094| 83.094| -|7| 83.99| 83.318| 83.318| -|8| 84.55| 83.628| 83.628| -|9| 84.36| 83.778| 83.778| -|10| 85.53| 84.254| 84.254| -|11| 86.54| 84.994| 84.994| -|12| 86.89| 85.574| 85.574| -|13| 87.77| 86.218| 86.218| -|14| 87.29| 86.804| 86.804| +| #| Input | **QuanTAlib** | _TA-LIB_ | _Skender_ | _Pandas-TA_ | _Tulip_ | +|--|:--:|:--:|:--:|:--:|:--:|:--:| +| 0| 212.80|**212.80**| _NaN_| _NaN_| _NaN_| _NaN_| +| 1| 214.06|**213.43**| _NaN_| _NaN_| _NaN_| _NaN_| +| 2| 213.89|**213.58**| _NaN_| _NaN_| _NaN_| _NaN_| +| 3| 214.66|**213.85**| _NaN_| _NaN_| _NaN_| _NaN_| +| 4| 213.95|**213.87**| _213.87_| _213.87_| _213.87_| _213.87_| +| 5| 213.95|**214.10**| _214.10_| _214.10_| _214.10_| _214.10_| +| 6| 214.55|**214.20**| _214.20_| _214.20_| _214.20_| _214.20_| +| 7| 214.02|**214.23**| _214.23_| _214.23_| _214.23_| _214.23_| +| 8| 214.51|**214.20**| _214.20_| _214.20_| _214.20_| _214.20_| +| 9| 213.75|**214.16**| _214.16_| _214.16_| _214.16_| _214.16_| +|10| 214.22|**214.21**| _214.21_| _214.21_| _214.21_| _214.21_| +|11| 213.43|**213.99**| _213.99_| _213.99_| _213.99_| _213.99_| +|12| 214.21|**214.02**| _214.02_| _214.02_| _214.02_| _214.02_| +|13| 213.66|**213.85**| _213.85_| _213.85_| _213.85_| _213.85_| +|14| 215.03|**214.11**| _214.11_| _214.11_| _214.11_| _214.11_| +|15| 216.89|**214.64**| _214.64_| _214.64_| _214.64_| _214.64_| +|16| 216.66|**215.29**| _215.29_| _215.29_| _215.29_| _215.29_| + ## References - https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average diff --git a/docs/indicators.md b/docs/indicators.md index 36a3e7d6..df76b040 100644 --- a/docs/indicators.md +++ b/docs/indicators.md @@ -75,7 +75,7 @@ |PWMA - Pascal's Weighted Moving Average||||pwma| |⭐RMA - WildeR's Moving Average|`RMA_Series`|||✔️rma|✔️rma| |SINWMA - Sine Weighted Moving Average||||sinwma| -|⭐SMA - Simple Moving Average|`SMA_Series`|✔️SMA|✔️GetSma|✔️sma|✔️sma| +|⭐[SMA - Simple Moving Average](SMA.md)|`SMA_Series`|✔️SMA|✔️GetSma|✔️sma|✔️sma| |SMMA - Smoothed Moving Average|`SMMA_Series`||✔️GetSmma|| |SSF - Ehler's Super Smoother Filter||||ssf| |SUPERTREND - Supertrend||||supertrend|