From 56f8db294970f8ecf69814d3430076bfae1aec00 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Thu, 4 May 2023 08:19:57 -0700 Subject: [PATCH] Update main automation workflow to use wildcard for dotcover report path --- .github/workflows/main_automation.yml | 8 +- .../{Statistics => Basics}/CORR_Series.cs | 0 .../{Statistics => Basics}/COVAR_Series.cs | 16 +- Calculations/Basics/MIDPRICE_Series.cs | 32 --- Calculations/Basics/TR_Series.cs | 40 ---- .../ClassStructures/Pair_TSeries_Abstract.cs | 186 +++++++++-------- .../ClassStructures/Single_TBars_Abstract.cs | 67 ------- .../Single_TSeries_Abstract.cs | 73 ------- Calculations/Logic/EQUITY_Series.cs | 6 +- .../{ClassStructures => Logic}/TOrders.cs | 0 Calculations/Statistics/LINREG_Series.cs | 93 --------- Calculations/Trends/CCI_Series.cs | 49 ----- Calculations/Trends/HWMA_Series.cs | 57 ------ Calculations/Trends/MACD_Series.cs | 45 ----- Calculations/Trends/MAMA_Series.cs | 158 --------------- Calculations/Volatility/ADL_Series.cs | 40 ---- Calculations/Volatility/ADOSC_Series.cs | 55 ----- Calculations/Volatility/ATRP_Series.cs | 48 ----- Calculations/Volatility/ATR_Series.cs | 49 ----- Calculations/Volatility/BBANDS_Series.cs | 73 ------- Calculations/Volatility/OBV_Series.cs | 59 ------ Calculations/_Updated/ADL_Series.cs | 69 +++++++ Calculations/_Updated/ADOSC_Series.cs | 90 +++++++++ Calculations/_Updated/ALMA_Series.cs | 69 ++++--- Calculations/_Updated/ATRP_Series.cs | 87 ++++++++ Calculations/_Updated/ATR_Series.cs | 88 ++++++++ Calculations/_Updated/BBANDS_Series.cs | 112 +++++++++++ Calculations/_Updated/BIAS_Series.cs | 5 +- Calculations/_Updated/CCI_Series.cs | 86 ++++++++ Calculations/_Updated/CMO_Series.cs | 6 +- Calculations/_Updated/CUSUM_Series.cs | 7 +- Calculations/_Updated/DECAY_Series.cs | 5 +- Calculations/_Updated/DEMA_Series.cs | 6 +- Calculations/_Updated/DWMA_Series.cs | 8 +- Calculations/_Updated/EMA_Series.cs | 7 +- Calculations/_Updated/ENTROPY_Series.cs | 5 +- Calculations/_Updated/FWMA_Series.cs | 10 +- Calculations/_Updated/HEMA_Series.cs | 20 +- Calculations/_Updated/HMA_Series.cs | 5 +- Calculations/_Updated/HWMA_Series.cs | 132 ++++++++++++ Calculations/_Updated/JMA_Series.cs | 5 +- Calculations/_Updated/KAMA_Series.cs | 9 +- Calculations/_Updated/KURTOSIS_Series.cs | 5 +- Calculations/_Updated/MACD_Series.cs | 78 ++++++++ Calculations/_Updated/MAD_Series.cs | 5 +- .../_Updated/{zMA_Series.cs => MAE_Series.cs} | 49 +++-- Calculations/_Updated/MAMA_Series.cs | 189 ++++++++++++++++++ Calculations/_Updated/MAPE_Series.cs | 5 +- Calculations/_Updated/MAX_Series.cs | 5 +- Calculations/_Updated/MEDIAN_Series.cs | 5 +- Calculations/_Updated/MIDPOINT_Series.cs | 5 +- Calculations/_Updated/MIDPRICE_Series.cs | 65 ++++++ Calculations/_Updated/MIN_Series.cs | 5 +- Calculations/_Updated/MSE_Series.cs | 5 +- Calculations/_Updated/OBV_Series.cs | 96 +++++++++ Calculations/_Updated/RMA_Series.cs | 7 +- Calculations/_Updated/RSI_Series.cs | 5 +- Calculations/_Updated/SDEV_Series.cs | 5 +- Calculations/_Updated/SLOPE_Series.cs | 122 +++++++++++ Calculations/_Updated/SMAPE_Series.cs | 7 +- Calculations/_Updated/SMA_Series.cs | 7 +- Calculations/_Updated/SMMA_Series.cs | 5 +- Calculations/_Updated/SSDEV_Series.cs | 5 +- Calculations/_Updated/SVAR_Series.cs | 5 +- Calculations/_Updated/T3_Series.cs | 5 +- Calculations/_Updated/TBars.cs | 26 ++- Calculations/_Updated/TEMA_Series.cs | 5 +- Calculations/_Updated/TRIMA_Series.cs | 7 +- Calculations/_Updated/TRIX_Series.cs | 17 +- Calculations/_Updated/TR_Series.cs | 79 ++++++++ Calculations/_Updated/TSeries.cs | 27 ++- Calculations/_Updated/VAR_Series.cs | 5 +- Calculations/_Updated/WMAPE_Series.cs | 5 +- Calculations/_Updated/WMA_Series.cs | 7 +- Calculations/_Updated/ZLEMA_Series.cs | 8 +- Calculations/_Updated/ZL_Series.cs | 7 +- Calculations/_Updated/ZSCORE_Series.cs | 5 +- Indicators/Charts/2MASlope_chart.cs | 9 +- Indicators/Charts/JMA_chart.cs | 8 +- Indicators/Charts/TrailingStop.cs | 27 +-- Strategies/SimpleMACross1.cs | 32 +-- Strategies/Strategies.csproj | 3 - Tests/Basic tests/Indicators.cs | 2 + Tests/Basic tests/Oscillators.cs | 6 +- Tests/Basic tests/TBars_input.cs | 96 +++++++++ Tests/Validations/Trends/Skender.cs | 8 +- Tests/Validations/Trends/TA_LIB.cs | 4 +- Tests/Validations/Trends/Tulip.cs | 6 +- docs/indicators.md | 3 +- 89 files changed, 1721 insertions(+), 1356 deletions(-) rename Calculations/{Statistics => Basics}/CORR_Series.cs (100%) rename Calculations/{Statistics => Basics}/COVAR_Series.cs (77%) delete mode 100644 Calculations/Basics/MIDPRICE_Series.cs delete mode 100644 Calculations/Basics/TR_Series.cs delete mode 100644 Calculations/ClassStructures/Single_TBars_Abstract.cs delete mode 100644 Calculations/ClassStructures/Single_TSeries_Abstract.cs rename Calculations/{ClassStructures => Logic}/TOrders.cs (100%) delete mode 100644 Calculations/Statistics/LINREG_Series.cs delete mode 100644 Calculations/Trends/CCI_Series.cs delete mode 100644 Calculations/Trends/HWMA_Series.cs delete mode 100644 Calculations/Trends/MACD_Series.cs delete mode 100644 Calculations/Trends/MAMA_Series.cs delete mode 100644 Calculations/Volatility/ADL_Series.cs delete mode 100644 Calculations/Volatility/ADOSC_Series.cs delete mode 100644 Calculations/Volatility/ATRP_Series.cs delete mode 100644 Calculations/Volatility/ATR_Series.cs delete mode 100644 Calculations/Volatility/BBANDS_Series.cs delete mode 100644 Calculations/Volatility/OBV_Series.cs create mode 100644 Calculations/_Updated/ADL_Series.cs create mode 100644 Calculations/_Updated/ADOSC_Series.cs create mode 100644 Calculations/_Updated/ATRP_Series.cs create mode 100644 Calculations/_Updated/ATR_Series.cs create mode 100644 Calculations/_Updated/BBANDS_Series.cs create mode 100644 Calculations/_Updated/CCI_Series.cs create mode 100644 Calculations/_Updated/HWMA_Series.cs create mode 100644 Calculations/_Updated/MACD_Series.cs rename Calculations/_Updated/{zMA_Series.cs => MAE_Series.cs} (59%) create mode 100644 Calculations/_Updated/MAMA_Series.cs create mode 100644 Calculations/_Updated/MIDPRICE_Series.cs create mode 100644 Calculations/_Updated/OBV_Series.cs create mode 100644 Calculations/_Updated/SLOPE_Series.cs create mode 100644 Calculations/_Updated/TR_Series.cs create mode 100644 Tests/Basic tests/TBars_input.cs diff --git a/.github/workflows/main_automation.yml b/.github/workflows/main_automation.yml index 0f91fe12..85a883a4 100644 --- a/.github/workflows/main_automation.yml +++ b/.github/workflows/main_automation.yml @@ -65,7 +65,7 @@ jobs: run: dotnet sonarscanner begin /o:"mihakralj" /k:"mihakralj_QuanTAlib" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" - /d:sonar.cs.dotcover.reportsPaths=./dotcover.xml + /d:sonar.cs.dotcover.reportsPaths=dotcover* ############# Build and test @@ -80,8 +80,10 @@ jobs: - name: Build Strategies DLL run: dotnet build ./Strategies/Strategies.csproj --configuration Release --nologo - - name: DotCover Test - run: dotnet dotcover test Tests/Tests.csproj --dcReportType=DetailedXML --dcReportType=HTML --dcoutput=dotcover.xml --dcoutput=dotcover.html + - name: DotCover Test HTML + run: dotnet dotcover test Tests/Tests.csproj --dcReportType=HTML --dcoutput=./dotcover.html + - name: DotCover Test XML + run: dotnet dotcover test Tests/Tests.csproj --dcReportType=DetailedXML --dcoutput=./dotcover.xml --verbosity=Detailed - name: Coverlet Test run: dotnet test -p:CollectCoverage=true --collect:"XPlat Code Coverage" --results-directory "./" diff --git a/Calculations/Statistics/CORR_Series.cs b/Calculations/Basics/CORR_Series.cs similarity index 100% rename from Calculations/Statistics/CORR_Series.cs rename to Calculations/Basics/CORR_Series.cs diff --git a/Calculations/Statistics/COVAR_Series.cs b/Calculations/Basics/COVAR_Series.cs similarity index 77% rename from Calculations/Statistics/COVAR_Series.cs rename to Calculations/Basics/COVAR_Series.cs index 8fd20b47..3826ee9d 100644 --- a/Calculations/Statistics/COVAR_Series.cs +++ b/Calculations/Basics/COVAR_Series.cs @@ -1,5 +1,6 @@ namespace QuanTAlib; using System; +using System.Collections.Generic; using System.Linq; /* @@ -12,11 +13,16 @@ Sources: */ + public class COVAR_Series : Pair_TSeries_Indicator { public COVAR_Series(TSeries d1, TSeries d2, int period, bool useNaN = false) : base(d1, d2, period, useNaN) { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } + if (base._d1.Count > 0 && base._d2.Count > 0) { + for (int i = 0; i < base._d1.Count; i++) { + this.Add(base._d1[i], base._d2[i], false); + } + } } private readonly System.Collections.Generic.List _x = new(); @@ -25,9 +31,9 @@ public class COVAR_Series : Pair_TSeries_Indicator public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) { - Add_Replace_Trim(_x, TValue1.v, _p, update); - Add_Replace_Trim(_y, TValue2.v, _p, update); - Add_Replace_Trim(_xy, TValue1.v * TValue2.v, _p, update); + BufferTrim(_x, TValue1.v, _p, update); + BufferTrim(_y, TValue2.v, _p, update); + BufferTrim(_xy, TValue1.v * TValue2.v, _p, update); double _avgx = _x.Average(); double _avgy = _y.Average(); @@ -37,4 +43,4 @@ public class COVAR_Series : Pair_TSeries_Indicator var result = (TValue1.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _covar); if (update) { base[base.Count - 1] = result; } else { base.Add(result); } } -} \ No newline at end of file +} diff --git a/Calculations/Basics/MIDPRICE_Series.cs b/Calculations/Basics/MIDPRICE_Series.cs deleted file mode 100644 index c3a3c444..00000000 --- a/Calculations/Basics/MIDPRICE_Series.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Linq; - -/* -MIDPRICE: Midpoint price (highhest high + lowest low)/2 in the given period in the series. - If period = 0 => period = full length of the series - - */ - -public class MIDPRICE_Series : Single_TBars_Indicator -{ - public MIDPRICE_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) - { - if (base._bars.Count > 0) - { base.Add(base._bars); } - } - private readonly System.Collections.Generic.List _bufferhi = new(); - private readonly System.Collections.Generic.List _bufferlo = new(); - - public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) - { - Add_Replace_Trim(_bufferhi, TBar.h, _p, update); - Add_Replace_Trim(_bufferlo, TBar.l, _p, update); - - double _max = _bufferhi.Max(); - double _min = _bufferlo.Min(); - double _mid = (_max + _min) * 0.5; - - base.Add((TBar.t, _mid), update, _NaN); - } -} \ No newline at end of file diff --git a/Calculations/Basics/TR_Series.cs b/Calculations/Basics/TR_Series.cs deleted file mode 100644 index 6f4c2391..00000000 --- a/Calculations/Basics/TR_Series.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -TR: True Range - True Range was introduced by J. Welles Wilder in his book New Concepts in Technical Trading Systems. - It measures the daily range plus any gap from the closing price of the preceding day. - -Calculation: - d1 = ABS(High - Low) - d2 = ABS(High - Previous close) - d3 = ABS(Previous close - Low) - TR = MAX(d1,d2,d3) - -Sources: - https://www.macroption.com/true-range/ - - */ - -public class TR_Series : Single_TBars_Indicator -{ - private double _cm1, _cm1_o; - public TR_Series(TBars source, bool useNaN = false) : base(source, period:0, useNaN:useNaN) { - _cm1 =_cm1_o = double.NaN; - if (this._bars.Count > 0) { base.Add(this._bars); } - } - - public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) - { - if (update) {_cm1 = _cm1_o; } else { _cm1_o = _cm1; } - if (_cm1 is double.NaN) { _cm1 = TBar.c; } //first bar - - double d1 = Math.Abs(TBar.h - TBar.l); - double d2 = Math.Abs(_cm1 - TBar.h); - double d3 = Math.Abs(_cm1 - TBar.l); - var ret = (TBar.t, (base.Count==0 && base._NaN) ? double.NaN : Math.Max(d1,Math.Max(d2,d3)) ); - base.Add(ret, update); - _cm1 = TBar.c; - } -} \ No newline at end of file diff --git a/Calculations/ClassStructures/Pair_TSeries_Abstract.cs b/Calculations/ClassStructures/Pair_TSeries_Abstract.cs index 564a9e39..2203d3f1 100644 --- a/Calculations/ClassStructures/Pair_TSeries_Abstract.cs +++ b/Calculations/ClassStructures/Pair_TSeries_Abstract.cs @@ -16,97 +16,117 @@ Abstract classes with all scaffolding required to build indicators. */ +public abstract class Pair_TSeries_Indicator : TSeries { + protected readonly int _p; + protected readonly bool _NaN; + protected readonly TSeries _d1; + protected readonly TSeries _d2; + protected readonly double _dd1, _dd2; -public abstract class Pair_TSeries_Indicator : TSeries -{ - protected readonly int _p; - protected readonly bool _NaN; - protected readonly TSeries _d1; - protected readonly TSeries _d2; - protected readonly double _dd1, _dd2; + // Chainable Constructors - add them at the end of primary constructors if needed + protected Pair_TSeries_Indicator(TSeries source1, TSeries source2, int period, bool useNaN) { + _p = period; + _NaN = useNaN; + _d1 = source1; + _d2 = source2; + _dd1 = double.NaN; + _dd2 = double.NaN; + _d1.Pub += Sub; + _d2.Pub += Sub; + } - // Chainable Constructors - add them at the end of primary constructors if needed - protected Pair_TSeries_Indicator(TSeries source1, TSeries source2, int period, bool useNaN) - { - this._p = period; - this._NaN = useNaN; - this._d1 = source1; - this._d2 = source2; - this._dd1 = double.NaN; - this._dd2 = double.NaN; - this._d1.Pub += this.Sub; - this._d2.Pub += this.Sub; - } - protected Pair_TSeries_Indicator(TSeries source1, TSeries source2) - { - this._d1 = source1; - this._d2 = source2; - this._dd1 = double.NaN; - this._dd2 = double.NaN; - this._d1.Pub += this.Sub; - this._d2.Pub += this.Sub; - } - protected Pair_TSeries_Indicator(TSeries source1, double dd2) - { - this._d1 = source1; - this._d2 = new(); - this._dd1 = double.NaN; - this._dd2 = dd2; - this._d1.Pub += this.Sub; - } - protected Pair_TSeries_Indicator(double dd1, TSeries source2) - { - this._d1 = new(); - this._d2 = source2; - this._dd1 = dd1; - this._dd2 = double.NaN; - this._d2.Pub += this.Sub; - } + protected Pair_TSeries_Indicator(TSeries source1, TSeries source2) { + _d1 = source1; + _d2 = source2; + _dd1 = double.NaN; + _dd2 = double.NaN; + _d1.Pub += Sub; + _d2.Pub += Sub; + } - // overridable Add(Tvalue, Tvalue) method to add/update a single value at the end of the list - public virtual void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update) => base.Add(TValue: (TValue1.t, 0), update: update); // default inserts zeros + protected Pair_TSeries_Indicator(TSeries source1, double dd2) { + _d1 = source1; + _d2 = new TSeries(); + _dd1 = double.NaN; + _dd2 = dd2; + _d1.Pub += Sub; + } - // potentially overridable Add() bulk variations (could be replaced with faster bulk algos) - public virtual void Add(TSeries d1, TSeries d2) { for (int i = 0; i < d1.Count; i++) { this.Add(d1[i], d2[i], update: false); }} - public virtual void Add(TSeries d1, double dd2) { for (int i = 0; i < d1.Count; i++) { this.Add(d1[i], (d1[i].t, dd2), update: false); }} - public virtual void Add(double dd1, TSeries d2) { for (int i = 0; i < d2.Count; i++) { this.Add((d2[i].t, dd1), d2[i], update: false); }} + protected Pair_TSeries_Indicator(double dd1, TSeries source2) { + _d1 = new TSeries(); + _d2 = source2; + _dd1 = dd1; + _dd2 = double.NaN; + _d2.Pub += Sub; + } - public void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2) => this.Add(TValue1, TValue2, update: false); + // overridable Add(Tvalue, Tvalue) method to add/update a single value at the end of the list + public virtual void Add((DateTime t, double v) TValue1, (DateTime t, double v) TValue2, bool update) { + base.Add((TValue1.t, 0), update); + // default inserts zeros + } - public void Add(bool update) - { - if ((this._dd1 is double.NaN) && (this._dd2 is double.NaN)) - { - // (Series, Series) - if (update || (this._d1.Count > this.Count && this._d2.Count > this.Count)) - { this.Add(this._d1[this._d1.Count - 1], this._d2[this._d2.Count - 1], update); } - } - else if ((this._dd2 is not double.NaN) && (this._dd1 is double.NaN)) - { - // (Series, Double) - this.Add(TValue1: this._d1[this._d1.Count - 1], TValue2: (this._d1[this._d1.Count - 1].t, this._dd2), update: update); - } - else - { - // (Double, Series) - this.Add(TValue1: (this._d2[this._d2.Count - 1].t, this._dd1), TValue2: this._d2[this._d2.Count - 1], update: update); + // potentially overridable Add() bulk variations (could be replaced with faster bulk algos) + public virtual void Add(TSeries d1, TSeries d2) { + for (var i = 0; i < d1.Count; i++) { + Add(d1[i], d2[i], false); } } - public void Add() => this.Add(update: false); - public new void Sub(object source, TSeriesEventArgs e) => this.Add(e.update); + public virtual void Add(TSeries d1, double dd2) { + for (var i = 0; i < d1.Count; i++) { + Add(d1[i], (d1[i].t, dd2), false); + } + } - protected static void Add_Replace(List l, double v, bool update) - { - if (update) - { l[l.Count - 1] = v; } - else - { l.Add(v); } - } - protected static void Add_Replace_Trim(List l, double v, int p, bool update) - { - Add_Replace(l, v, update); - if (l.Count > p && p != 0) - { l.RemoveAt(0); } - } + public virtual void Add(double dd1, TSeries d2) { + for (var i = 0; i < d2.Count; i++) { + Add((d2[i].t, dd1), d2[i], false); + } + } + + public void Add((DateTime t, double v) TValue1, (DateTime t, double v) TValue2) { + Add(TValue1, TValue2, false); + } + + public void Add(bool update) { + if (_dd1 is double.NaN && _dd2 is double.NaN) { + // (Series, Series) + if (update || (_d1.Count > Count && _d2.Count > Count)) { + Add(_d1[_d1.Count - 1], _d2[_d2.Count - 1], update); + } + } + else if (_dd2 is not double.NaN && _dd1 is double.NaN) { + // (Series, Double) + Add(_d1[_d1.Count - 1], (_d1[_d1.Count - 1].t, _dd2), update); + } + else { + // (Double, Series) + Add((_d2[_d2.Count - 1].t, _dd1), _d2[_d2.Count - 1], update); + } + } + + public void Add() { + Add(false); + } + + public new void Sub(object source, TSeriesEventArgs e) { + Add(e.update); + } + + protected static void Add_Replace(List l, double v, bool update) { + if (update) { + l[l.Count - 1] = v; + } + else { + l.Add(v); + } + } + + protected static void Add_Replace_Trim(List l, double v, int p, bool update) { + Add_Replace(l, v, update); + if (l.Count > p && p != 0) { + l.RemoveAt(0); + } + } } diff --git a/Calculations/ClassStructures/Single_TBars_Abstract.cs b/Calculations/ClassStructures/Single_TBars_Abstract.cs deleted file mode 100644 index 1d3c8ac8..00000000 --- a/Calculations/ClassStructures/Single_TBars_Abstract.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -Abstract classes with all scaffolding required to build indicators. - All abstracts support period, NaN, and all permutations of Add() methods. - Indicator classess need to implement: - - Chaining constructor (Abstract's constructor executes first) - - Default Add(value) class - - optional Add(series) bulk insert class (for optimization of historical analysis) - - Single_TSeries_Indicator - one single-value TSeries in, one TSeries out. - Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring) - Single_TBars_Indicator - One OHLCV TBars in, one TSeries out. - - */ - -public abstract class Single_TBars_Indicator : TSeries -{ - protected readonly int _p; - protected readonly bool _NaN; - protected readonly TBars _bars; - - // Chainable Constructor - add it at the end of primary constructor :base(source: source, period: period, useNaN: useNaN) - protected Single_TBars_Indicator(TBars source, int period, bool useNaN) - { - this._p = period; - this._bars = source; - this._NaN = useNaN; - this._bars.Pub += this.Sub; - - } - - // overridable Add() method to add/update a single item at the end of the list - - - public virtual void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar, bool update) => base.Add((TBar.t, 0.0), update); - public virtual void Add((System.DateTime t, double v) TValue, bool update, bool useNaN) - { - var res = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : TValue.v); - base.Add(res, update); - } - - // potentially overridable Add() method for the whole bars or series (could be replaced with faster bulk algo) - public virtual void Add(TBars bars) { for (int i = 0; i < bars.Count; i++) { this.Add(TBar: bars[i], update: false); } } - public virtual new void Add(TSeries data) { for (int i = 0; i < data.Count; i++) { base.Add(TValue: data[i], update: false); } } - public void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar) => this.Add(TBar: TBar, update: false); - public void Add(bool update) => this.Add(TBar: this._bars[this._bars.Count - 1], update: update); - public void Add() => this.Add(TBar: this._bars[this._bars.Count - 1], update: false); - public new void Sub(object source, TSeriesEventArgs e) => this.Add(TBar: this._bars[this._bars.Count - 1], update: e.update); - - protected static void Add_Replace(List l, double v, bool update) - { - if (update) - { l[l.Count - 1] = v; } - else - { l.Add(v); } - } - protected static void Add_Replace_Trim(List l, double v, int p, bool update) - { - Add_Replace(l, v, update); - if (l.Count > p && p != 0) - { l.RemoveAt(0); } - } - -} diff --git a/Calculations/ClassStructures/Single_TSeries_Abstract.cs b/Calculations/ClassStructures/Single_TSeries_Abstract.cs deleted file mode 100644 index ca8d3d89..00000000 --- a/Calculations/ClassStructures/Single_TSeries_Abstract.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -Abstract classes with all scaffolding required to build indicators. - All abstracts support period, NaN, and all permutations of Add() methods. - Indicator classess need to implement: - - Chaining constructor (Abstract's constructor executes first) - - Default Add(value) class - - optional Add(series) bulk insert class (for optimization of historical analysis) - - Single_TSeries_Indicator - one single-value TSeries in, one TSeries out. - Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring) - Single_TBars_Indicator - One OHLCV TBars in, one TSeries out. - - */ -public abstract class Single_TSeries_Indicator : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - protected int _p; - - // Chainable Constructor - add it at the end of primary constructor :base(source: source, period: period, useNaN: useNaN) - protected Single_TSeries_Indicator(TSeries source, int period, bool useNaN) - { - _data = source; - _period = period; - _p = _period; - _NaN = useNaN; - _data.Pub += Sub; - } - - // overridable Add() method to add/update a single item at the end of the list - - public virtual void Add((DateTime t, double v) TValue, bool update, bool useNaN) - { - if (_period == 0) { _p = Length; } - var res = (TValue.t, Count < _p - 1 && _NaN ? double.NaN : TValue.v); - base.Add(res, update); - } - public new virtual void Add((DateTime t, double v) TValue, bool update) => base.Add(TValue, update); - - // potentially overridable Add() method for the whole series (could be replaced with faster bulk algo) - public virtual new void Add(TSeries data) - { - foreach (var item in data) { Add(TValue: item, update: false); } - } - public new void Add((System.DateTime t, double v) TValue) => this.Add(TValue: TValue, update: false); - public void Add(bool update) => this.Add(TValue: this._data[this._data.Count - 1], update: update); - public void Add() => this.Add(TValue: this._data[this._data.Count - 1], update: false); - public new void Sub(object source, TSeriesEventArgs e) => this.Add(TValue: this._data[this._data.Count - 1], update: e.update); - - protected static void Add_Replace(List l, double v, bool update) - { - if (update) - { l[l.Count - 1] = v; } - else - { l.Add(v); } - } - protected static double Add_Replace_Trim(List l, double v, int p, bool update) - { - Add_Replace(l, v, update); - double ret = (l.Count > 0) ? l.First() : 0; - if (l.Count > p && p != 0) - { - l.RemoveAt(0); - } - return ret; - } -} diff --git a/Calculations/Logic/EQUITY_Series.cs b/Calculations/Logic/EQUITY_Series.cs index 50313152..78e8fa0d 100644 --- a/Calculations/Logic/EQUITY_Series.cs +++ b/Calculations/Logic/EQUITY_Series.cs @@ -12,6 +12,8 @@ EQUITY - Generates P&L portfolio based on trades signals and equity prices //optional: long, short, long&short //optional: warmup period: warmup +/* + public class EQUITY_Series : Single_TSeries_Indicator { readonly TSeries inmarket; //for every bar private readonly TSeries _price; @@ -84,4 +86,6 @@ public class EQUITY_Series : Single_TSeries_Indicator { inmarket.Add((TValue.t, (double)_inmarket)); base.Add((TValue.t, _equity), update, _NaN); } -} \ No newline at end of file +} + +*/ \ No newline at end of file diff --git a/Calculations/ClassStructures/TOrders.cs b/Calculations/Logic/TOrders.cs similarity index 100% rename from Calculations/ClassStructures/TOrders.cs rename to Calculations/Logic/TOrders.cs diff --git a/Calculations/Statistics/LINREG_Series.cs b/Calculations/Statistics/LINREG_Series.cs deleted file mode 100644 index 96458c54..00000000 --- a/Calculations/Statistics/LINREG_Series.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -LINREG: Linear Regression (using Least Square Method) - Linear Regression provides a slope of a straight line that is the best approximation of the given set of data. - The method of least squares is a standard approach in linear regression analysis to approximate the solution - by minimizing the sum of the squares of the residuals made in the results of each individual equation. - -Additional outputs provided by LINREG: - .Intercept - y-intercept point of the best fit line - .RSquared - R-Squared (R²), Coefficient of Determination - .StdDev - Standard Deviation of data over given periods - - y = Slope * x + Intercept - -Sources: - https://en.wikipedia.org/wiki/Least_squares - - */ - -public class LINREG_Series : Single_TSeries_Indicator -{ - private readonly TSeries p_Intercept = new(); - private readonly TSeries p_RSquared = new(); - private readonly TSeries p_StdDev = new(); - private readonly System.Collections.Generic.List _buffer = new(); - public TSeries Intercept => p_Intercept; - public TSeries RSquared => p_RSquared; - public TSeries StdDev => p_StdDev; -public LINREG_Series(TSeries source, int period, bool useNaN = false) - : base(source, period, useNaN) - { - if (this._data.Count > 0) { base.Add(this._data); } - } - - public override void Add((System.DateTime t, double v) TValue, bool update) - { - Add_Replace_Trim(_buffer, TValue.v, _p, update); - - int _len = this._buffer.Count; - - // get averages for period - double sumX = 0; - double sumY = 0; - - for (int p = 0; p < _len; p++) - { - sumX += this.Count - _len + 2 + p; - sumY += _buffer[p]; - } - double avgX = sumX / _len; - double avgY = sumY / _len; - - // least squares method - double sumSqX = 0; - double sumSqY = 0; - double sumSqXY = 0; - - for (int p = 0; p < _len; p++) - { - double devX = this.Count - _len + 2 + p - avgX; - double devY = _buffer[p] - avgY; - - sumSqX += devX * devX; - sumSqY += devY * devY; - sumSqXY += devX * devY; - } - - double _slope = sumSqXY / sumSqX; - double _intercept = avgY - (_slope * avgX); - - // calculate Standard Deviation and R-Squared - double stdDevX = Math.Sqrt(sumSqX / _len); - double stdDevY = Math.Sqrt(sumSqY / _len); - double _StdDev = stdDevY; - - double arrr = (stdDevX * stdDevY != 0) ? sumSqXY / (stdDevX * stdDevY) / _len : 0; - double _RSquared = arrr * arrr; - - var ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _slope); - base.Add(ret, update, _NaN); - - ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _intercept); - p_Intercept.Add(ret, update); - - ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _StdDev); - p_StdDev.Add(ret, update); - - ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _RSquared); - p_RSquared.Add(ret, update); - } -} \ No newline at end of file diff --git a/Calculations/Trends/CCI_Series.cs b/Calculations/Trends/CCI_Series.cs deleted file mode 100644 index aa3505b3..00000000 --- a/Calculations/Trends/CCI_Series.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Linq; -using static System.Net.Mime.MediaTypeNames; - -/* -CCI: Commodity Channel Index - Commodity Channel Index is a momentum oscillator used to primarily identify overbought - and oversold levels relative to a mean. CCI measures the current price level relative - to an average price level over a given period of time: - - CCI is relatively high when prices are far above their average. - - CCI is relatively low when prices are far below their average. - Using this method, CCI can be used to identify overbought and oversold levels. - -Sources: - https://www.investopedia.com/terms/c/commoditychannelindex.asp - https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cci - - */ - -public class CCI_Series : Single_TBars_Indicator -{ - private readonly System.Collections.Generic.List _tp = new(); - - public CCI_Series(TBars source, int period = 10, bool useNaN = false) : base(source, period: period, useNaN: useNaN) - { - if (_bars.Count > 0) { base.Add(_bars); } - } - - public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) - { - double _tpItem = (TBar.h + TBar.l + TBar.c) / 3.0; - if (update) { this._tp[this._tp.Count - 1] = _tpItem; } else { this._tp.Add(_tpItem); } - if (this._tp.Count > this._p) { this._tp.RemoveAt(0); } - - // average TP over _tp buffer - double _avgTp = _tp.Average(); - - // average Deviation over _tp buffer - double _avgDv = 0; - for (int i = 0; i < this._tp.Count; i++) { _avgDv += Math.Abs(_avgTp - this._tp[i]); } - _avgDv /= this._tp.Count; - - - double _cci = (_avgDv == 0) ? double.NaN : (this._tp[this._tp.Count-1] - _avgTp) / (0.015 * _avgDv); - - base.Add((TBar.t, _cci), update, _NaN); - } -} \ No newline at end of file diff --git a/Calculations/Trends/HWMA_Series.cs b/Calculations/Trends/HWMA_Series.cs deleted file mode 100644 index f2ba9ae3..00000000 --- a/Calculations/Trends/HWMA_Series.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -HWMA: Holt-Winter Moving Average - Indicator HWMA (Holt-Winter Moving Average) is a three-parameter moving - average by the Holt-Winter method; Holt-Winters Exponential Smoothing is - used for forecasting time series data that exhibits both a trend and a - seasonal variation. - - -Sources: - https://timeseriesreasoning.com/contents/holt-winters-exponential-smoothing/ - https://www.mql5.com/en/code/20856 - -nA - smoothed series (from 0 to 1) -nB - assess the trend (from 0 to 1) -nC - assess seasonality (from 0 to 1) - -F[i] = (1-nA) * (F[i-1] + V[i-1] + 0.5 * A[i-1]) + nA * Price[i] -V[i] = (1-nB) * (V[i-1] + A[i-1]) + nB * (F[i] - F[i-1]) -A[i] = (1-nC) * A[i-1] + nC * (V[i] - V[i-1]) -HWMA[i] = F[i] + V[i] + 0.5 * A[i] - - */ - -public class HWMA_Series : Single_TSeries_Indicator { - readonly double _nA, _nB, _nC; - double _pF, _pV, _pA; - double _ppF, _ppV, _ppA; - - public HWMA_Series(TSeries source, double nA = 0.2, double nB = 0.1, double nC = 0.1, bool useNaN = false) : base(source, 0, useNaN) { - - _nA = nA; - _nB = nB; - _nC = nC; - if (this._data.Count > 0) { base.Add(this._data); } - } - public override void Add((DateTime t, double v) TValue, bool update) { - double _F, _V, _A; - if (this.Count == 0) { _pF = TValue.v; _pA = _pV = 0; } - - if (update) { _pF = _ppF; _pV = _ppV; _pA = _ppA; } - else { _ppF = _pF; _ppV = _pV; _ppA = _pA; } - - _F = (1 - _nA) * (_pF + _pV + 0.5 * _pA) + _nA * TValue.v; - _V = (1 - _nB) * (_pV + _pA) + _nB * (_F - _pF); - _A = (1 - _nC) * _pA + _nC * (_V - _pV); - - double _hwma = _F + _V + 0.5 * _A; - _pF = _F; - _pV = _V; - _pA = _A; - - base.Add((TValue.t, _hwma), update, _NaN); - } -} diff --git a/Calculations/Trends/MACD_Series.cs b/Calculations/Trends/MACD_Series.cs deleted file mode 100644 index 02f9360c..00000000 --- a/Calculations/Trends/MACD_Series.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -MACD: Moving Average Convergence/Divergence - Moving average convergence divergence (MACD) is a trend-following momentum - indicator that shows the relationship between two moving averages of a series. - The MACD is calculated by subtracting the 26-period exponential moving average (EMA) - from the 12-period EMA. MACD Signal is 9-day EMA of MACD. - -Sources: - https://www.investopedia.com/terms/m/macd.asp - https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/macd - - */ - -public class MACD_Series : Single_TSeries_Indicator -{ - private readonly EMA_Series _TSslow; - private readonly EMA_Series _TSfast; - private readonly SUB_Series _TSmacd; - public EMA_Series Signal { get; } - - public MACD_Series(TSeries source, int slow = 26, int fast = 12, int signal = 9, bool useNaN = false) - : base(source, period: 0, useNaN) - { - _TSslow = new(source: source, period: slow, useNaN: false); - _TSfast = new(source: source, period: fast, useNaN: false); - _TSmacd = new(_TSfast, _TSslow); - this.Signal = new(source: _TSmacd, period: signal, useNaN: useNaN); - - if (source.Count > 0) { base.Add(_TSmacd); } - } - public override void Add((System.DateTime t, double v) TValue, bool update) - { - double _macd; - if (update) - { - _TSslow.Add(TValue, true); - _TSfast.Add(TValue, true); - } - _macd = this._TSmacd[(this.Count < this._TSmacd.Count) ? this.Count : this._TSmacd.Count - 1].v; - base.Add((TValue.t, _macd), update, _NaN); - } -} \ No newline at end of file diff --git a/Calculations/Trends/MAMA_Series.cs b/Calculations/Trends/MAMA_Series.cs deleted file mode 100644 index 5a04d341..00000000 --- a/Calculations/Trends/MAMA_Series.cs +++ /dev/null @@ -1,158 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -MAMA: MESA Adaptive Moving Average - Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of - high/low price that uses classic electrical radio-frequency signal processing algorithms - to reduce noise. - - KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 ) - -Sources: - https://mesasoftware.com/papers/MAMA.pdf - https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/ - - */ - - - -public class MAMA_Series : Single_TSeries_Indicator { - public MAMA_Series(TSeries source, double fastlimit = 0.5, double slowlimit = 0.05, bool useNaN = false) : base(source, 5, useNaN) { - fastl = fastlimit; - slowl = slowlimit; - Fama = new TSeries(); - if (_data.Count > 0) { - base.Add(_data); - } - } - - private double sumPr, jI, jQ; - private readonly double fastl, slowl; - private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt; - private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama; - public TSeries Fama { get; } - - public override void Add((DateTime t, double v) TValue, bool update) { - if (!update) { - // roll forward (oldx = x) - pr.io = pr.i6; - pr.i6 = pr.i5; - pr.i5 = pr.i4; - pr.i4 = pr.i3; - pr.i3 = pr.i2; - pr.i2 = pr.i1; - pr.i1 = pr.i; - i1.io = i1.i6; - i1.i6 = i1.i5; - i1.i5 = i1.i4; - i1.i4 = i1.i3; - i1.i3 = i1.i2; - i1.i2 = i1.i1; - i1.i1 = i1.i; - q1.io = q1.i6; - q1.i6 = q1.i5; - q1.i5 = q1.i4; - q1.i4 = q1.i3; - q1.i3 = q1.i2; - q1.i2 = q1.i1; - q1.i1 = q1.i; - dt.io = dt.i6; - dt.i6 = dt.i5; - dt.i5 = dt.i4; - dt.i4 = dt.i3; - dt.i3 = dt.i2; - dt.i2 = dt.i1; - dt.i1 = dt.i; - sm.io = sm.i6; - sm.i6 = sm.i5; - sm.i5 = sm.i4; - sm.i4 = sm.i3; - sm.i3 = sm.i2; - sm.i2 = sm.i1; - sm.i1 = sm.i; - i2.io = i2.i1; - i2.i1 = i2.i; - q2.io = q2.i1; - q2.i1 = q2.i; - re.io = re.i1; - re.i1 = re.i; - im.io = im.i1; - im.i1 = im.i; - pd.io = pd.i1; - pd.i1 = pd.i; - ph.io = ph.i1; - ph.i1 = ph.i; - mama.io = mama.i1; - mama.i1 = mama.i; - fama.io = fama.i1; - fama.i1 = fama.i; - } - - var i = Count; - pr.i = TValue.v; - if (i > 5) { - var adj = 0.075 * pd.i1 + 0.54; - - // smooth and detrender - sm.i = (4 * pr.i + 3 * pr.i1 + 2 * pr.i2 + pr.i3) / 10; - dt.i = (0.0962 * sm.i + 0.5769 * sm.i2 - 0.5769 * sm.i4 - 0.0962 * sm.i6) * adj; - - // in-phase and quadrature - q1.i = (0.0962 * dt.i + 0.5769 * dt.i2 - 0.5769 * dt.i4 - 0.0962 * dt.i6) * adj; - i1.i = dt.i3; - - // advance the phases by 90 degrees - jI = (0.0962 * i1.i + 0.5769 * i1.i2 - 0.5769 * i1.i4 - 0.0962 * i1.i6) * adj; - jQ = (0.0962 * q1.i + 0.5769 * q1.i2 - 0.5769 * q1.i4 - 0.0962 * q1.i6) * adj; - - // phasor addition for 3-bar averaging - i2.i = i1.i - jQ; - q2.i = q1.i + jI; - - i2.i = 0.2 * i2.i + 0.8 * i2.i1; // smoothing it - q2.i = 0.2 * q2.i + 0.8 * q2.i1; - - // homodyne discriminator - re.i = i2.i * i2.i1 + q2.i * q2.i1; - im.i = i2.i * q2.i1 - q2.i * i2.i1; - - re.i = 0.2 * re.i + 0.8 * re.i1; // smoothing it - im.i = 0.2 * im.i + 0.8 * im.i1; - - // calculate period - pd.i = im.i != 0 && re.i != 0 ? 6.283185307179586 / Math.Atan(im.i / re.i) : 0d; - - // adjust period to thresholds - pd.i = pd.i > 1.5 * pd.i1 ? 1.5 * pd.i1 : pd.i; - pd.i = pd.i < 0.67 * pd.i1 ? 0.67 * pd.i1 : pd.i; - pd.i = pd.i < 6d ? 6d : pd.i; - pd.i = pd.i > 50d ? 50d : pd.i; - - // smooth the period - pd.i = 0.2 * pd.i + 0.8 * pd.i1; - - // determine phase position - ph.i = i1.i != 0 ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0; - - // change in phase - var delta = Math.Max(ph.i1 - ph.i, 1d); - - // adaptive alpha value - var alpha = Math.Max(fastl / delta, slowl); - - // final indicators - mama.i = alpha * pr.i + (1d - alpha) * mama.i1; - fama.i = 0.5d * alpha * mama.i + (1d - 0.5d * alpha) * fama.i1; - } - else { - sumPr += pr.i; - pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0; - mama.i = fama.i = sumPr / (i + 1); - } - - base.Add((TValue.t, mama.i), update, _NaN); - var result = (TValue.t, Count < _p - 1 && _NaN ? double.NaN : fama.i); - Fama.Add(result, update); - } -} diff --git a/Calculations/Volatility/ADL_Series.cs b/Calculations/Volatility/ADL_Series.cs deleted file mode 100644 index 0eb43ec2..00000000 --- a/Calculations/Volatility/ADL_Series.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -ADL: Chaikin Accumulation/Distribution Line - ADL is a volume-based indicator that measures the cumulative Money Flow Volume: - - 1. Money Flow Multiplier = [(Close - Low) - (High - Close)] /(High - Low) - 2. Money Flow Volume = Money Flow Multiplier x Volume for the Period - 3. ADL = Previous ADL + Current Period's Money Flow Volume - -Sources: - https://school.stockcharts.com/doku.php?id=technical_indicators:accumulation_distribution_line - - */ - -public class ADL_Series : Single_TBars_Indicator -{ - private double _lastadl, _lastlastadl; - - public ADL_Series(TBars source, bool useNaN = false) : base(source, 0, useNaN) - { - _lastadl = _lastlastadl = 0; - if (_bars.Count > 0) { base.Add(_bars); } - } - - public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) - { - if (update) { this._lastadl = this._lastlastadl; } - - double _adl = 0; - double tmp = TBar.h - TBar.l; - if (tmp > 0.0 ) { _adl = _lastadl + ((2*TBar.c - TBar.l - TBar.h) / tmp * TBar.v); } - - this._lastlastadl = this._lastadl; - this._lastadl = _adl; - - base.Add((TBar.t, _adl), update, _NaN); - } -} \ No newline at end of file diff --git a/Calculations/Volatility/ADOSC_Series.cs b/Calculations/Volatility/ADOSC_Series.cs deleted file mode 100644 index 797e1f85..00000000 --- a/Calculations/Volatility/ADOSC_Series.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -ADO: Chaikin Accumulation/Distribution Oscillator - ADO measures the momentum of ADL using the difference between slow (10-day) EMA(ADL) - and fast (3-day) EMA(ADL): - - Chaikin A/D Oscillator is defined as 3-day EMA of ADL minus 10-day EMA of ADL - -Sources: - https://school.stockcharts.com/doku.php?id=technical_indicators:chaikin_oscillator - - */ - - -public class ADOSC_Series : Single_TBars_Indicator -{ - private readonly double _k1, _k2; - private double _lastema1, _lastlastema1, _lastema2, _lastlastema2; - private double _lastadl, _lastlastadl; - - public ADOSC_Series(TBars source, int shortPeriod = 3, int longPeriod =10, bool useNaN = false) : base(source, period: 0, useNaN) - { - _k1 = 2.0 / (shortPeriod + 1); - _k2 = 2.0 / (longPeriod + 1); - _lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0; - if (_bars.Count > 0) { base.Add(_bars); } - } - - public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) - { - if (update) { - _lastadl = _lastlastadl; - _lastema1 = _lastlastema1; - _lastema2 = _lastlastema2; - } - - double _adl = 0; - double tmp = TBar.h - TBar.l; - if (tmp > 0.0) { _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); } - if (this.Count == 0) { _lastema1 = _lastema2 = _adl; } - - double _ema1 = (_adl - _lastema1) * _k1 + _lastema1; - double _ema2 = (_adl - _lastema2) * _k2 + _lastema2; - - _lastlastadl = _lastadl; _lastadl = _adl; - _lastlastema1 = _lastema1; _lastema1 = _ema1; - _lastlastema2 = _lastema2; _lastema2 = _ema2; - - double _adosc = _ema1 - _ema2; - base.Add((TBar.t, _adosc), update, _NaN); - } - -} diff --git a/Calculations/Volatility/ATRP_Series.cs b/Calculations/Volatility/ATRP_Series.cs deleted file mode 100644 index 3f4457ea..00000000 --- a/Calculations/Volatility/ATRP_Series.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -ATRP: Average True Range Percent - Average True Range Percent is (ATR/Close Price)*100. - This normalizes so it can be compared to other stocks. - -Sources: - https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/atrp - - */ - -public class ATRP_Series : Single_TBars_Indicator { - private readonly System.Collections.Generic.List _buffer = new(); - private readonly double _k; - private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; - private readonly int _period; - - public ATRP_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) { - _period = period; - _k = 1.0 / (double)(_period); - _lastatr = _lastlastatr = _cm1 = _lastcm1 = _sum = _oldsum = 0; - if (this._bars.Count > 0) { base.Add(this._bars); } - } - - public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) { - if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } - else { _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; } - - if (this.Count == 0) { _cm1 = TBar.c; } - double d1 = Math.Abs(TBar.h - TBar.l); - double d2 = Math.Abs(_cm1 - TBar.h); - double d3 = Math.Abs(_cm1 - TBar.l); - (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); - _cm1 = TBar.c; - - double _atr = 0; - if (this.Count == 0) { _atr = d.v; } - else if (this.Count < _p + 1) { _sum += d.v; _atr = _sum / (this.Count); } - else { _atr = _k * (d.v - _lastatr) + _lastatr; } - _lastatr = _atr; - - double _atrp = 100 * (_atr / TBar.c); - var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _atrp); - base.Add(ret, update); - } -} diff --git a/Calculations/Volatility/ATR_Series.cs b/Calculations/Volatility/ATR_Series.cs deleted file mode 100644 index bc641e92..00000000 --- a/Calculations/Volatility/ATR_Series.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -ATR: wildeR Moving Average - The average true range (ATR) is a price volatility indicator - showing the average price variation of assets within a given time period. - -Sources: - https://en.wikipedia.org/wiki/Average_true_range - https://www.tradingview.com/wiki/Average_True_Range_(ATR) - https://www.investopedia.com/terms/a/atr.asp - - */ - -public class ATR_Series : Single_TBars_Indicator { - private readonly System.Collections.Generic.List _buffer = new(); - private readonly double _k; - private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; - private readonly int _period; - - public ATR_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) { - _period = period; - _k = 1.0 / (double)(_p); - _lastatr = _lastlastatr = _cm1 = _lastcm1 = _sum = _oldsum = 0; - if (this._bars.Count > 0) { base.Add(this._bars); } - } - - public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) { - if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } - else { _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; } - - if (this.Count == 0) { _cm1 = TBar.c; } - double d1 = Math.Abs(TBar.h - TBar.l); - double d2 = Math.Abs(_cm1 - TBar.h); - double d3 = Math.Abs(_cm1 - TBar.l); - (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); - _cm1 = TBar.c; - - double _atr = 0; - if (this.Count == 0) { _atr = d.v; } - else if (this.Count < _p + 1) { _sum += d.v; _atr = _sum / (this.Count); } - else { _atr = _k * (d.v - _lastatr) + _lastatr; } - _lastatr = _atr; - - var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _atr); - base.Add(ret, update); - } -} \ No newline at end of file diff --git a/Calculations/Volatility/BBANDS_Series.cs b/Calculations/Volatility/BBANDS_Series.cs deleted file mode 100644 index 169f8f35..00000000 --- a/Calculations/Volatility/BBANDS_Series.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -BBANDS: Bollinger Bands® - Price channels created by John Bollinger, depict volatility as standard deviation boundary - line range from a moving average of price. The bands automatically widen when volatility - increases and contract when volatility decreases. Their dynamic nature allows them to be - used on different securities with the standard settings. - - Mid Band = simple moving average (SMA) - Upper Band = SMA + (standard deviation of price x multiplier) - Lower Band = SMA - (standard deviation of price x multiplier) - Bandwidth = Width of the channel: (Upper-Lower)/SMA - %B = The location of the data point within the channel: (Price-Lower)/(Upper/Lower) - Z-Score = number of standard deviations of the data point from SMA - -Sources: - https://www.investopedia.com/terms/b/bollingerbands.asp - https://school.stockcharts.com/doku.php?id=technical_indicators:bollinger_bands - -Note: - Bollinger Bands® is a registered trademark of John A. Bollinger. - - */ - -public class BBANDS_Series : Single_TSeries_Indicator -{ - public SMA_Series Mid { get; } - public ADD_Series Upper { get; } - public SUB_Series Lower { get; } - public DIV_Series PercentB { get; } - public DIV_Series Bandwidth { get; } - public DIV_Series Zscore { get; } - - private readonly SDEV_Series _sdev; - private readonly MUL_Series _mulsdev; - private readonly SUB_Series _pbdnd; - private readonly SUB_Series _pbdvr; - private readonly SUB_Series _zdnd; - - public BBANDS_Series(TSeries source, int period = 26, double multiplier = 2.0, bool useNaN = false) - : base(source, period: 0, useNaN) - { - this.Mid = new(source: source, period: period, useNaN: useNaN); - - _sdev = new(source, period, useNaN: useNaN); - _mulsdev = new(_sdev, multiplier); - this.Upper = new(Mid, _mulsdev); - this.Lower = new(Mid, _mulsdev); - - _pbdnd = new(source, Lower); - _pbdvr = new(Upper, Lower); - - this.PercentB = new(_pbdnd, _pbdvr); - this.Bandwidth = new(_pbdvr, Mid); - - _zdnd = new(source, Mid); - this.Zscore = new(_zdnd, _sdev); - - if (source.Count > 0) - { base.Add(this.Bandwidth); } - } - public override void Add((System.DateTime t, double v) TValue, bool update) - { - double _bbandwidth; - if (update) - { _sdev.Add(TValue, true); } - _bbandwidth = this.Bandwidth[(this.Count < this.Bandwidth.Count) ? this.Count : this.Bandwidth.Count - 1].v; - var result = (TValue.t, _bbandwidth); - base.Add(result, update); - } -} \ No newline at end of file diff --git a/Calculations/Volatility/OBV_Series.cs b/Calculations/Volatility/OBV_Series.cs deleted file mode 100644 index 73d4e327..00000000 --- a/Calculations/Volatility/OBV_Series.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -OBV: On-Balance Volume - On-balance volume (OBV) is a technical trading momentum indicator that uses volume flow to predict - changes in stock price. Joseph Granville first developed the OBV metric in the 1963 book - Granville's New Key to Stock Market Profits. - - | +volume; if close > close[previous] - OBV = OBV[previous] + | 0; if close = close[previous] - | -volume; if close < close[previous] - -Sources: - https://www.investopedia.com/terms/o/onbalancevolume.asp - https://www.tradingview.com/wiki/On_Balance_Volume_(OBV) - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/on-balance-volume-obv/ - https://www.motivewave.com/studies/on_balance_volume.htm - -Note: - There is no consensus on what is the first OBV value in the series: - - TA-LIB uses the first volume: OBV[0] = volume[0] - - Skender stock library uses 0: OBV[0] = 0 - - */ - -public class OBV_Series : Single_TBars_Indicator -{ - private double _lastobv, _lastlastobv; - private double _lastclose, _lastlastclose; - public OBV_Series(TBars source, int period = 10, bool useNaN = false) : base(source, period: period, useNaN: useNaN) - { - this._lastobv = this._lastlastobv = 0; - this._lastclose = this._lastlastclose = 0; - if (_bars.Count > 0) { base.Add(_bars); } - } - - public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) - { - if (update) - { - this._lastobv = this._lastlastobv; - this._lastclose = this._lastlastclose; - } - - double _obv = this._lastobv; - if (TBar.c > this._lastclose) { _obv += TBar.v; } - if (TBar.c < this._lastclose) { _obv -= TBar.v; } - - this._lastlastobv = this._lastobv; - this._lastobv = _obv; - - this._lastlastclose = this._lastclose; - this._lastclose = TBar.c; - - var result = (TBar.t, (this.Count < this._p && this._NaN) ? double.NaN : _obv); - base.Add(result, update); - } -} diff --git a/Calculations/_Updated/ADL_Series.cs b/Calculations/_Updated/ADL_Series.cs new file mode 100644 index 00000000..aa33c7b2 --- /dev/null +++ b/Calculations/_Updated/ADL_Series.cs @@ -0,0 +1,69 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +ADL: Chaikin Accumulation/Distribution Line + ADL is a volume-based indicator that measures the cumulative Money Flow Volume: + + 1. Money Flow Multiplier = [(Close - Low) - (High - Close)] /(High - Low) + 2. Money Flow Volume = Money Flow Multiplier x Volume for the Period + 3. ADL = Previous ADL + Current Period's Money Flow Volume + +Sources: + https://school.stockcharts.com/doku.php?id=technical_indicators:accumulation_distribution_line + + */ + +public class ADL_Series : TSeries { + protected readonly TBars _data; + private double _lastadl, _lastlastadl; + + //core constructors + public ADL_Series() { + Name = $"ADL()"; + _lastadl = _lastlastadl = 0; + } + public ADL_Series(TBars source) { + _data = source; + Name = $"ADL({(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _lastadl = _lastlastadl = 0; + _data.Pub += Sub; + Add(data: _data); + } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { + if (update) { this._lastadl = this._lastlastadl; } + else { this._lastlastadl = this._lastadl; } + + double _adl = 0; + double tmp = TBar.h - TBar.l; + if (tmp > 0.0) { + _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); + } + _lastadl = _adl; + + var ret = (TBar.t, _adl); + return base.Add(ret, update); + } + + public new void Add(TBars data) { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _lastadl = _lastlastadl = 0; + } +} \ No newline at end of file diff --git a/Calculations/_Updated/ADOSC_Series.cs b/Calculations/_Updated/ADOSC_Series.cs new file mode 100644 index 00000000..63aa2104 --- /dev/null +++ b/Calculations/_Updated/ADOSC_Series.cs @@ -0,0 +1,90 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +ADOSC: Chaikin Accumulation/Distribution Oscillator + ADO measures the momentum of ADL using the difference between slow (10-day) EMA(ADL) + and fast (3-day) EMA(ADL): + + Chaikin A/D Oscillator is defined as 3-day EMA of ADL minus 10-day EMA of ADL + +Sources: + https://school.stockcharts.com/doku.php?id=technical_indicators:chaikin_oscillator + + */ + +public class ADOSC_Series : TSeries { + protected readonly TBars _data; + private readonly double _k1, _k2; + private double _lastema1, _lastlastema1, _lastema2, _lastlastema2; + private double _lastadl, _lastlastadl; + + //core constructors + public ADOSC_Series(int shortPeriod, int longPeriod, bool useNaN = false) { + Name = $"ADOSC()"; + _k1 = 2.0 / (shortPeriod + 1); + _k2 = 2.0 / (longPeriod + 1); + _lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0; + } + public ADOSC_Series(TBars source, int shortPeriod, int longPeriod, bool useNaN = false) :this(shortPeriod, longPeriod, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _lastadl = _lastlastadl = 0; + _data.Pub += Sub; + Add(data: _data); + } + + public ADOSC_Series() : this(shortPeriod: 3, longPeriod: 10, useNaN: false) {} + + public ADOSC_Series(TBars source) : this(source, shortPeriod: 3, longPeriod:10, useNaN:false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update= false) { + + if (update) { + _lastadl = _lastlastadl; + _lastema1 = _lastlastema1; + _lastema2 = _lastlastema2; + } + + double _adl = 0; + double tmp = TBar.h - TBar.l; + if (tmp > 0.0) { _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); } + if (this.Count == 0) { _lastema1 = _lastema2 = _adl; } + + double _ema1 = (_adl - _lastema1) * _k1 + _lastema1; + double _ema2 = (_adl - _lastema2) * _k2 + _lastema2; + + _lastlastadl = _lastadl; + _lastadl = _adl; + _lastlastema1 = _lastema1; + _lastema1 = _ema1; + _lastlastema2 = _lastema2; + _lastema2 = _ema2; + + double _adosc = _ema1 - _ema2; + + var ret = (TBar.t, _adosc); + return base.Add(ret, update); + } + + public new void Add(TBars data) { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0; + } +} \ No newline at end of file diff --git a/Calculations/_Updated/ALMA_Series.cs b/Calculations/_Updated/ALMA_Series.cs index 18ba3339..514e0e34 100644 --- a/Calculations/_Updated/ALMA_Series.cs +++ b/Calculations/_Updated/ALMA_Series.cs @@ -25,12 +25,12 @@ public class ALMA_Series : TSeries { protected readonly TSeries _data; private readonly System.Collections.Generic.List _buffer = new(); - private readonly System.Collections.Generic.List _weight = new(); + private readonly System.Collections.Generic.List _weight; private double _norm; private readonly double _offset, _sigma; //core constructors - public ALMA_Series(int period, double offset, double sigma, bool useNaN) : base() { + public ALMA_Series(int period, double offset, double sigma, bool useNaN) { _period = period; _NaN = useNaN; Name = $"ALMA({period})"; @@ -55,36 +55,41 @@ public class ALMA_Series : TSeries { public ALMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, offset: 0.85, sigma: 6.0, useNaN: useNaN) { } // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) { - BufferTrim(_buffer, TValue.v, _period, update); - if (_weight.Count < _buffer.Count) { - for (int i = 0; i < (_buffer.Count - _weight.Count); i++) { _weight.Add(0.0); } - } - if (this._buffer.Count <= _period || _period ==0) { - int _len = this._buffer.Count; - _norm = 0; - double _m = _offset * (_len - 1); - double _s = _len / _sigma; - for (int i = 0; i < _len; i++) { - double _wt = Math.Exp(-((i - _m) * (i - _m)) / (2 * _s * _s)); - _weight[i] = _wt; - _norm += _wt; - } + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { + if (double.IsNaN(TValue.v)) { + return base.Add((TValue.t, double.NaN), update); } - double _weightedSum = 0; - for (int i = 0; i < this._buffer.Count; i++) { _weightedSum += _weight[i] * _buffer[i]; } - double _alma = _weightedSum / _norm; + BufferTrim(_buffer, TValue.v, _period, update); + if (_weight.Count < _buffer.Count) { + for (var i = 0; i < _buffer.Count - _weight.Count; i++) { + _weight.Add(0.0); + } + } + - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _alma); - return base.Add(res, update); - } + if (_buffer.Count <= _period || _period == 0) { + var _len = _buffer.Count; + _norm = 0; + var _m = _offset * (_len - 1); + var _s = _len / _sigma; + for (var i = 0; i < _len; i++) { + var _wt = Math.Exp(-((i - _m) * (i - _m)) / (2 * _s * _s)); + _weight[i] = _wt; + _norm += _wt; + } + } - //reset calculation - public override void Reset() { - _buffer.Clear(); - _weight.Clear(); - } + double _weightedSum = 0; + for (var i = 0; i < _buffer.Count; i++) { + _weightedSum += _weight[i] * _buffer[i]; + } + + var _alma = _weightedSum / _norm; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _alma); + return base.Add(res, update); + } //variation of Add() public override (DateTime t, double v) Add(TSeries data) { @@ -92,9 +97,6 @@ public class ALMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } @@ -104,4 +106,9 @@ public class ALMA_Series : TSeries { private new void Sub(object source, TSeriesEventArgs e) { Add(TValue: _data.Last, update: e.update); } + //reset calculation + public override void Reset() { + _buffer.Clear(); + _weight.Clear(); + } } \ No newline at end of file diff --git a/Calculations/_Updated/ATRP_Series.cs b/Calculations/_Updated/ATRP_Series.cs new file mode 100644 index 00000000..3ba2c1dc --- /dev/null +++ b/Calculations/_Updated/ATRP_Series.cs @@ -0,0 +1,87 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +ATRP: Average True Range Percent + Average True Range Percent is (ATR/Close Price)*100. + This normalizes so it can be compared to other stocks. + +Sources: + https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/atrp + + */ + +public class ATRP_Series : TSeries { + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private double _k; + private int _len; + private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; + + //core constructors + public ATRP_Series(int period, bool useNaN) { + _period = period; + _k = 1.0 / (double)(_period); + _NaN = useNaN; + _len = 0; + Name = $"ATRP({period})"; + } + public ATRP_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public ATRP_Series() : this(period: 1, useNaN: false) { } + public ATRP_Series(int period) : this(period: period, useNaN: false) { } + public ATRP_Series(TBars source) : this(source, period: 1, useNaN: false) { } + public ATRP_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { + if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } + else { + _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; + _k = (_period == 0) ? 1 / (double)_len : _k; + _len++; + } + + if (_len == 1) { _cm1 = TBar.c; } + double d1 = Math.Abs(TBar.h - TBar.l); + double d2 = Math.Abs(_cm1 - TBar.h); + double d3 = Math.Abs(_cm1 - TBar.l); + (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); + _cm1 = TBar.c; + + double _atr = 0; + if (this.Count == 0) { _atr = d.v; } + else if (this.Count < _period + 1) { _sum += d.v; _atr = _sum / (this.Count); } + else { _atr = _k * (d.v - _lastatr) + _lastatr; } + _lastatr = _atr; + double _atrp = 100 * (_atr / TBar.c); + + var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _atrp); + return base.Add(res, update); + } + + public new void Add(TBars data) { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _len = 0; + } +} \ No newline at end of file diff --git a/Calculations/_Updated/ATR_Series.cs b/Calculations/_Updated/ATR_Series.cs new file mode 100644 index 00000000..9935f5c4 --- /dev/null +++ b/Calculations/_Updated/ATR_Series.cs @@ -0,0 +1,88 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +ATR: wildeR Moving Average + The average true range (ATR) is a price volatility indicator + showing the average price variation of assets within a given time period. + +Sources: + https://en.wikipedia.org/wiki/Average_true_range + https://www.tradingview.com/wiki/Average_True_Range_(ATR) + https://www.investopedia.com/terms/a/atr.asp + + */ + +public class ATR_Series : TSeries { + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private double _k; + private int _len; + private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; + + //core constructors + public ATR_Series(int period, bool useNaN) { + _period = period; + _k = 1.0 / (double)(_period); + _NaN = useNaN; + _len = 0; + Name = $"ATR({period})"; + } + public ATR_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public ATR_Series() : this(period: 1, useNaN: false) { } + public ATR_Series(int period) : this(period: period, useNaN: false) { } + public ATR_Series(TBars source) : this(source, period: 1, useNaN: false) { } + public ATR_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { + if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } + else { + _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; + _k = (_period == 0) ? 1 / (double)_len : _k; + _len++; + } + + if (_len == 1) { _cm1 = TBar.c; } + double d1 = Math.Abs(TBar.h - TBar.l); + double d2 = Math.Abs(_cm1 - TBar.h); + double d3 = Math.Abs(_cm1 - TBar.l); + (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); + _cm1 = TBar.c; + + double _atr = 0; + if (this.Count == 0) { _atr = d.v; } + else if (this.Count < _period + 1) { _sum += d.v; _atr = _sum / (this.Count); } + else { _atr = _k * (d.v - _lastatr) + _lastatr; } + _lastatr = _atr; + + var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _atr); + return base.Add(res, update); + } + + public new void Add(TBars data) { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _len = 0; + } +} \ No newline at end of file diff --git a/Calculations/_Updated/BBANDS_Series.cs b/Calculations/_Updated/BBANDS_Series.cs new file mode 100644 index 00000000..198b2a38 --- /dev/null +++ b/Calculations/_Updated/BBANDS_Series.cs @@ -0,0 +1,112 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +BBANDS: Bollinger Bands® + Price channels created by John Bollinger, depict volatility as standard deviation boundary + line range from a moving average of price. The bands automatically widen when volatility + increases and contract when volatility decreases. Their dynamic nature allows them to be + used on different securities with the standard settings. + + Mid Band = simple moving average (SMA) + Upper Band = SMA + (standard deviation of price x multiplier) + Lower Band = SMA - (standard deviation of price x multiplier) + Bandwidth = Width of the channel: (Upper-Lower)/SMA + %B = The location of the data point within the channel: (Price-Lower)/(Upper/Lower) + Z-Score = number of standard deviations of the data point from SMA + +Sources: + https://www.investopedia.com/terms/b/bollingerbands.asp + https://school.stockcharts.com/doku.php?id=technical_indicators:bollinger_bands + +Note: + Bollinger Bands® is a registered trademark of John A. Bollinger. + + */ + +public class BBANDS_Series : TSeries { + protected readonly int _period; + protected readonly double _multiplier; + protected readonly bool _NaN; + protected readonly TSeries _data; + public SMA_Series Mid { get; } + public TSeries Upper { get; } + public TSeries Lower { get; } + public TSeries PercentB { get; } + public TSeries Bandwidth { get; } + public TSeries Zscore { get; } + private readonly SDEV_Series _sdev; + + //core constructors + public BBANDS_Series(int period, double multiplier, bool useNaN) { + _period = period; + _multiplier = multiplier; + _NaN = useNaN; + Name = $"BBANDS({period})"; + } + public BBANDS_Series(TSeries source, int period, double multiplier, bool useNaN) : this(period, multiplier, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + Upper = new("BB_Up"); + Lower = new("BB_Low"); + Bandwidth = new("BBandwidth"); + PercentB = new("%BBandwidth"); + Zscore = new("Zscore"); + + Mid = new(period, false); + _sdev = new(period, false); + + _data.Pub += Sub; + Add(_data); + } + + public BBANDS_Series() : this(period:0, multiplier: 2.0, useNaN: false) { } + public BBANDS_Series(int period) : this(period: period, multiplier: 2.0, useNaN:false) { } + public BBANDS_Series(TBars source) : this(source:source.Close, period:0, multiplier: 2.0, useNaN:false) { } + public BBANDS_Series(TBars source, int period) : this(source:source.Close, period:period, multiplier: 2.0, useNaN: false) { } + public BBANDS_Series(TBars source, int period, double multiplier, bool useNaN) : this(source.Close, period:period, multiplier:multiplier, useNaN: false) { } + public BBANDS_Series(TSeries source) : this(source, period:0, useNaN:false) { } + public BBANDS_Series(TSeries source, int period) : this(source:source, period:period, useNaN:false) { } + public BBANDS_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, multiplier: 2.0, useNaN: useNaN) { } + + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) { + var _mid = Mid.Add(TValue,update); + var _sd = this._sdev.Add(TValue, update); + var _upper = Upper.Add((TValue.t, _mid.v + _sd.v * _multiplier), update); + var _lower = Lower.Add((TValue.t, _mid.v - _sd.v * _multiplier), update); + double _pbdnd = TValue.v - _lower.v; + double _pbdvr = _upper.v - _lower.v; + PercentB.Add((TValue.t, _pbdnd/_pbdvr), update); + Zscore.Add((TValue.t, (TValue.v-_mid.v)/_sd.v), update); + Bandwidth.Add((TValue.t, _pbdvr / _mid.v), update); + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pbdvr / _mid.v); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TValue: _data.Last, update: e.update); + } + //reset calculation + public override void Reset() { + Mid.Clear(); + _sdev.Clear(); + Upper.Clear(); + Lower.Clear(); + } +} \ No newline at end of file diff --git a/Calculations/_Updated/BIAS_Series.cs b/Calculations/_Updated/BIAS_Series.cs index 8fa734d5..df9bc5a5 100644 --- a/Calculations/_Updated/BIAS_Series.cs +++ b/Calculations/_Updated/BIAS_Series.cs @@ -20,7 +20,7 @@ public class BIAS_Series : TSeries { private readonly SMA_Series _sma; //core constructors - public BIAS_Series(int period, bool useNaN) : base() { + public BIAS_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"BIAS({period})"; @@ -55,9 +55,6 @@ public class BIAS_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/CCI_Series.cs b/Calculations/_Updated/CCI_Series.cs new file mode 100644 index 00000000..3bf2e553 --- /dev/null +++ b/Calculations/_Updated/CCI_Series.cs @@ -0,0 +1,86 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +CCI: Commodity Channel Index + Commodity Channel Index is a momentum oscillator used to primarily identify overbought + and oversold levels relative to a mean. CCI measures the current price level relative + to an average price level over a given period of time: + - CCI is relatively high when prices are far above their average. + - CCI is relatively low when prices are far below their average. + Using this method, CCI can be used to identify overbought and oversold levels. + +Sources: + https://www.investopedia.com/terms/c/commoditychannelindex.asp + https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cci + + */ + +public class CCI_Series : TSeries { + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private readonly System.Collections.Generic.List _tp = new(); + + //core constructors + public CCI_Series(int period, bool useNaN) { + _period = period; + _NaN = useNaN; + Name = $"CCI({period})"; + } + public CCI_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public CCI_Series() : this(period: 2, useNaN: false) { } + public CCI_Series(int period) : this(period: period, useNaN: false) { } + public CCI_Series(TBars source) : this(source, period: 2, useNaN: false) { } + public CCI_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { + double _tpItem = (TBar.h + TBar.l + TBar.c) / 3.0; + if (update) { + this._tp[this._tp.Count - 1] = _tpItem; + } + else { + this._tp.Add(_tpItem); + } + if (this._tp.Count > this._period) { this._tp.RemoveAt(0); } + + // average TP over _tp buffer + double _avgTp = _tp.Average(); + + // average Deviation over _tp buffer + double _avgDv = 0; + for (int i = 0; i < this._tp.Count; i++) { _avgDv += Math.Abs(_avgTp - this._tp[i]); } + _avgDv /= this._tp.Count; + + double _cci = (_avgDv == 0) ? 0 : (this._tp[this._tp.Count - 1] - _avgTp) / (0.015 * _avgDv); + var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _cci); + return base.Add(res, update); + } + + public new void Add(TBars data) { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _tp.Clear(); + } +} \ No newline at end of file diff --git a/Calculations/_Updated/CMO_Series.cs b/Calculations/_Updated/CMO_Series.cs index 6a7520b1..6906067a 100644 --- a/Calculations/_Updated/CMO_Series.cs +++ b/Calculations/_Updated/CMO_Series.cs @@ -27,7 +27,7 @@ public class CMO_Series : TSeries { private double _plast_value, _last_value; //core constructors - public CMO_Series(int period, bool useNaN) : base() { + public CMO_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"CMO({period})"; @@ -71,9 +71,7 @@ public class CMO_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } + public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/CUSUM_Series.cs b/Calculations/_Updated/CUSUM_Series.cs index 333a1466..e61d288d 100644 --- a/Calculations/_Updated/CUSUM_Series.cs +++ b/Calculations/_Updated/CUSUM_Series.cs @@ -19,7 +19,7 @@ public class CUSUM_Series : TSeries { protected readonly TSeries _data; //core constructors - public CUSUM_Series(int period, bool useNaN) : base() { + public CUSUM_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"CUSUM({period})"; @@ -35,7 +35,7 @@ public class CUSUM_Series : TSeries { public CUSUM_Series(TBars source) : this(source.Close, 0, false) { } public CUSUM_Series(TBars source, int period) : this(source.Close, period, false) { } public CUSUM_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public CUSUM_Series(TSeries source) : this(source, 0, false) { } + public CUSUM_Series(TSeries source) : this(source, period: 0, useNaN: false) { } public CUSUM_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } ////////////////// @@ -54,9 +54,6 @@ public class CUSUM_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/DECAY_Series.cs b/Calculations/_Updated/DECAY_Series.cs index 194f639a..a0193dc2 100644 --- a/Calculations/_Updated/DECAY_Series.cs +++ b/Calculations/_Updated/DECAY_Series.cs @@ -22,7 +22,7 @@ public class DECAY_Series : TSeries { private readonly double _dfactor; //core constructors - public DECAY_Series(int period, bool exponential, bool useNaN) : base() { + public DECAY_Series(int period, bool exponential, bool useNaN) { _period = period; _NaN = useNaN; Name = $"DECAY({period})"; @@ -66,9 +66,6 @@ public class DECAY_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/DEMA_Series.cs b/Calculations/_Updated/DEMA_Series.cs index afec6a2c..0cdac2b0 100644 --- a/Calculations/_Updated/DEMA_Series.cs +++ b/Calculations/_Updated/DEMA_Series.cs @@ -28,7 +28,7 @@ public class DEMA_Series : TSeries { protected readonly TSeries _data; //core constructor - public DEMA_Series(int period, bool useNaN, bool useSMA) : base() { + public DEMA_Series(int period, bool useNaN, bool useSMA) { _period = period; _NaN = useNaN; _useSMA = useSMA; @@ -107,10 +107,6 @@ public class DEMA_Series : TSeries { return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } - public (DateTime t, double v) Add(bool update) { return Add(_data.Last, update); } diff --git a/Calculations/_Updated/DWMA_Series.cs b/Calculations/_Updated/DWMA_Series.cs index 91df946c..3e5f4808 100644 --- a/Calculations/_Updated/DWMA_Series.cs +++ b/Calculations/_Updated/DWMA_Series.cs @@ -13,14 +13,14 @@ DWMA: Double Weighted Moving Average public class DWMA_Series : TSeries { private readonly List _buffer = new(); - private List _weights = new(); + private List _weights; protected readonly int _period; protected readonly bool _NaN; protected readonly TSeries _data; protected int _len; //core constructors - public DWMA_Series(int period, bool useNaN) : base() { + public DWMA_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"DWMA({period})"; @@ -91,10 +91,6 @@ public class DWMA_Series : TSeries { return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } - public (DateTime t, double v) Add(bool update) { return Add(_data.Last, update); } diff --git a/Calculations/_Updated/EMA_Series.cs b/Calculations/_Updated/EMA_Series.cs index 8136f7da..138f04ea 100644 --- a/Calculations/_Updated/EMA_Series.cs +++ b/Calculations/_Updated/EMA_Series.cs @@ -33,7 +33,7 @@ public class EMA_Series : TSeries { //core constructors - public EMA_Series(int period, bool useNaN, bool useSMA) : base() { + public EMA_Series(int period, bool useNaN, bool useSMA) { _period = period; _NaN = useNaN; _useSMA = useSMA; @@ -59,7 +59,7 @@ public class EMA_Series : TSeries { ////////////////// // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) { + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { if (update) { _lastema = _oldema; _sum = _oldsum; @@ -102,9 +102,6 @@ public class EMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/ENTROPY_Series.cs b/Calculations/_Updated/ENTROPY_Series.cs index 4abd1239..23523a50 100644 --- a/Calculations/_Updated/ENTROPY_Series.cs +++ b/Calculations/_Updated/ENTROPY_Series.cs @@ -27,7 +27,7 @@ public class ENTROPY_Series : TSeries { private readonly System.Collections.Generic.List _buff2 = new(); //core constructors - public ENTROPY_Series(int period, double logbase, bool useNaN) : base() { + public ENTROPY_Series(int period, double logbase, bool useNaN) { _period = period; _NaN = useNaN; _logbase = logbase; @@ -69,9 +69,6 @@ public class ENTROPY_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/FWMA_Series.cs b/Calculations/_Updated/FWMA_Series.cs index fb50117c..78cfef0e 100644 --- a/Calculations/_Updated/FWMA_Series.cs +++ b/Calculations/_Updated/FWMA_Series.cs @@ -12,13 +12,13 @@ FWMA: Fibonacci's Weighted Moving Average is similar to a Weighted Moving Averag */ public class FWMA_Series : TSeries { private readonly List _buffer = new(); - private List _weights = new(); + private List _weights; protected readonly int _period; protected readonly bool _NaN; protected readonly TSeries _data; protected int _len; - public FWMA_Series(int period, bool useNaN) : base() { + public FWMA_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"FWMA({period})"; @@ -65,12 +65,6 @@ public class FWMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } public (DateTime t, double v) Add() { return Add(TValue: _data.Last, update: false); } diff --git a/Calculations/_Updated/HEMA_Series.cs b/Calculations/_Updated/HEMA_Series.cs index 84d292bf..b2f53114 100644 --- a/Calculations/_Updated/HEMA_Series.cs +++ b/Calculations/_Updated/HEMA_Series.cs @@ -24,11 +24,11 @@ public class HEMA_Series : TSeries { private double _lasthema, _oldhema; //core constructors - public HEMA_Series(int period, bool useNaN) : base() { + public HEMA_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"HEMA({period})"; - CalculateK(_period, out _k1, out _k2, out _k3); + (_k1, _k2, _k3) = CalculateK(_period); _len = 0; _lastema1 = _oldema1 = _lastema2 = _oldema2 = _lasthema = _oldhema = 0; } @@ -62,7 +62,7 @@ public class HEMA_Series : TSeries { double _ema1, _ema2, _hema; if (_period == 0) { _len++; - CalculateK(_len, out _k1, out _k2, out _k3); + (_k1, _k2, _k3) = CalculateK(_len); } if (double.IsNaN(TValue.v)) { return base.Add((TValue.t, double.NaN), update); @@ -88,9 +88,6 @@ public class HEMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } @@ -108,9 +105,12 @@ public class HEMA_Series : TSeries { _len = 0; } - public static void CalculateK(int len, out double k1, out double k2, out double k3) { - k1 = 8 / (double)(len + 7); - k2 = 3 / (double)(len + 2); - k3 = 2 / Math.Sqrt(len + 3); + public static (double k1, double k2, double k3) CalculateK(int len) { + double k1 = 8 / (double)(len + 7); + double k2 = 3 / (double)(len + 2); + double k3 = 2 / Math.Sqrt(len + 3); + + return (k1, k2, k3); } + } \ No newline at end of file diff --git a/Calculations/_Updated/HMA_Series.cs b/Calculations/_Updated/HMA_Series.cs index 3631acf4..9230a76b 100644 --- a/Calculations/_Updated/HMA_Series.cs +++ b/Calculations/_Updated/HMA_Series.cs @@ -25,7 +25,7 @@ public class HMA_Series : TSeries { protected WMA_Series _wma1, _wma2, _wma3; //core constructors - public HMA_Series(int period, bool useNaN) : base() { + public HMA_Series(int period, bool useNaN) { _period = period; _period2 = period /2; _psqrt = (int)Math.Sqrt(period); @@ -69,9 +69,6 @@ public class HMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/HWMA_Series.cs b/Calculations/_Updated/HWMA_Series.cs new file mode 100644 index 00000000..f67249e2 --- /dev/null +++ b/Calculations/_Updated/HWMA_Series.cs @@ -0,0 +1,132 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +HWMA: Holt-Winter Moving Average + Indicator HWMA (Holt-Winter Moving Average) is a three-parameter moving + average by the Holt-Winter method; Holt-Winters Exponential Smoothing is + used for forecasting time series data that exhibits both a trend and a + seasonal variation. + + +Sources: + https://timeseriesreasoning.com/contents/holt-winters-exponential-smoothing/ + https://www.mql5.com/en/code/20856 + +nA - smoothed series (from 0 to 1) +nB - assess the trend (from 0 to 1) +nC - assess seasonality (from 0 to 1) + +Heuristic for determining alpha, beta, and gamma from period: + alpha = 2 / (1 + period) + beta = 1 / period + gamma = 1 / period + +F[i] = (1-nA) * (F[i-1] + V[i-1] + 0.5 * A[i-1]) + nA * Price[i] +V[i] = (1-nB) * (V[i-1] + A[i-1]) + nB * (F[i] - F[i-1]) +A[i] = (1-nC) * A[i-1] + nC * (V[i] - V[i-1]) +HWMA[i] = F[i] + V[i] + 0.5 * A[i] + + */ + +public class HWMA_Series : TSeries { + private int _len; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + double _nA, _nB, _nC; + double _pF, _pV, _pA; + double _ppF, _ppV, _ppA; + + //core constructors + + public HWMA_Series(double nA, double nB, double nC, bool useNaN) { + _period = (int)((2 - nA) / nA); + _nA = nA; + _nB = nB; + _nC = nC; + _NaN = useNaN; + Name = $"HWMA({_period})"; + _len = 0; + } + public HWMA_Series(TSeries source, double nA, double nB, double nC, bool useNaN = false) : this(nA, nB, nC, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public HWMA_Series() : this(period: 0, useNaN: false) { } + public HWMA_Series(int period) : this(period, useNaN: false) { } + public HWMA_Series(int period, bool useNaN) : this(nA: 2 / (1 + (double)period), nB: 1 / (double)period, nC: 1 / (double)period, useNaN) { + _period = period; + } + public HWMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { } + public HWMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public HWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public HWMA_Series(TSeries source, int period) : this(source, period, false) { } + public HWMA_Series(TSeries source, int period, bool useNaN) : this(source, nA: 2 / (1 + (double)period), nB: 1 / (double)period, nC: 1 / (double)period, useNaN: useNaN) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { + if (double.IsNaN(TValue.v)) { + return base.Add((TValue.t, Double.NaN), update); + } + double _F, _V, _A; + if (_len == 0) { _pF = TValue.v; _pA = _pV = 0; } + + if (update) { _pF = _ppF; _pV = _ppV; _pA = _ppA; } + else { + _ppF = _pF; + _ppV = _pV; + _ppA = _pA; + _len++; + } + + if (_period == 0) { + _nA = 2 / (1 + (double)_len); + _nB = 1 / (double)_len; + _nC = 1 / (double)_len; + } + if (_period == 1) { + _nA = 1; + _nB = 0; + _nC = 0; + } + + _F = (1 - _nA) * (_pF + _pV + 0.5 * _pA) + _nA * TValue.v; + _V = (1 - _nB) * (_pV + _pA) + _nB * (_F - _pF); + _A = (1 - _nC) * _pA + _nC * (_V - _pV); + + double _hwma = _F + _V + 0.5 * _A; + _pF = _F; + _pV = _V; + _pA = _A; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hwma); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _len = 0; + } +} \ No newline at end of file diff --git a/Calculations/_Updated/JMA_Series.cs b/Calculations/_Updated/JMA_Series.cs index bfa9f445..13f16a81 100644 --- a/Calculations/_Updated/JMA_Series.cs +++ b/Calculations/_Updated/JMA_Series.cs @@ -34,7 +34,7 @@ public class JMA_Series : TSeries { private readonly int _voltyS, _voltyL; //core constructors - public JMA_Series(int period, double phase, int vshort, int vlong, bool useNaN) : base() { + public JMA_Series(int period, double phase, int vshort, int vlong, bool useNaN) { _period = period; _NaN = useNaN; Name = $"JMA({period})"; @@ -147,9 +147,6 @@ public class JMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/KAMA_Series.cs b/Calculations/_Updated/KAMA_Series.cs index 87200bef..a3bcf394 100644 --- a/Calculations/_Updated/KAMA_Series.cs +++ b/Calculations/_Updated/KAMA_Series.cs @@ -27,17 +27,15 @@ Remark: public class KAMA_Series : TSeries { private readonly System.Collections.Generic.List _buffer = new(); private double _lastkama, _lastlastkama; - private int _len; private readonly double _scFast, _scSlow; protected readonly int _period; protected readonly bool _NaN; protected readonly TSeries _data; //core constructors - public KAMA_Series(int period, int fast, int slow, bool useNaN) : base() { + public KAMA_Series(int period, int fast, int slow, bool useNaN) { _period = period; _NaN = useNaN; - _len = 0; _scFast = 2.0 / (((period < fast) ? period : fast) + 1); _scSlow = 2.0 / (slow + 1); _lastkama = _lastlastkama = 0; @@ -81,7 +79,6 @@ public class KAMA_Series : TSeries { double _sc = (_er * (_scFast - _scSlow)) + _scSlow; _kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama))); } - _len++; _lastkama = _kama; var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kama); return base.Add(res, update); @@ -92,9 +89,6 @@ public class KAMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } @@ -108,7 +102,6 @@ public class KAMA_Series : TSeries { //reset calculation public override void Reset() { _buffer.Clear(); - _len = 0; _lastkama = _lastlastkama = 0; } } \ No newline at end of file diff --git a/Calculations/_Updated/KURTOSIS_Series.cs b/Calculations/_Updated/KURTOSIS_Series.cs index 6c1b48c6..71b8cfba 100644 --- a/Calculations/_Updated/KURTOSIS_Series.cs +++ b/Calculations/_Updated/KURTOSIS_Series.cs @@ -32,7 +32,7 @@ public class KURTOSIS_Series : TSeries { private readonly System.Collections.Generic.List _buffer = new(); //core constructors - public KURTOSIS_Series(int period, bool useNaN) : base() { + public KURTOSIS_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"KURTOSIS({period})"; @@ -79,9 +79,6 @@ public class KURTOSIS_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/MACD_Series.cs b/Calculations/_Updated/MACD_Series.cs new file mode 100644 index 00000000..a81ce1a9 --- /dev/null +++ b/Calculations/_Updated/MACD_Series.cs @@ -0,0 +1,78 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MACD: Moving Average Convergence/Divergence + Moving average convergence divergence (MACD) is a trend-following momentum + indicator that shows the relationship between two moving averages of a series. + The MACD is calculated by subtracting the 26-period exponential moving average (EMA) + from the 12-period EMA. MACD Signal is 9-day EMA of MACD. + + */ + +public class MACD_Series : TSeries { + private readonly System.Collections.Generic.List _buffer = new(); + + protected readonly int _slow, _fast, _signal; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly EMA_Series _TSlow; + private readonly EMA_Series _TFast; + public EMA_Series Signal { get; } + + //core constructors + public MACD_Series(int slow = 26, int fast = 12, int signal = 9, bool useNaN = false) { + _slow = slow; + _fast = fast; + _signal = signal; + _NaN = useNaN; + Name = $"MACD({slow},{fast},{signal})"; + _TSlow = new(slow, useNaN:false, useSMA:true); + _TFast = new(fast, useNaN: false, useSMA: true); + Signal = new(signal, useNaN: false, useSMA: true); + } + public MACD_Series(TSeries source, int slow, int fast, int signal, bool useNaN) : this(slow, fast, signal, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MACD_Series(TSeries source) : this(source:source, slow:26, fast:12, signal:9 , useNaN:false) { } + public MACD_Series(TSeries source, int slow, int fast, int signal) : this(source: source, slow: slow, fast:fast, signal:signal, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { + if (double.IsNaN(TValue.v)) { + return base.Add((TValue.t, Double.NaN), update); + } + + var _sslow = _TSlow.Add(TValue,update); + var _sfast = _TFast.Add(TValue, update); + Signal.Add((TValue.t, _sfast.v-_sslow.v)); + + var res = (TValue.t, Count < _fast - 1 && _NaN ? double.NaN : _sfast.v-_sslow.v); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/Calculations/_Updated/MAD_Series.cs b/Calculations/_Updated/MAD_Series.cs index e1f358cd..0378f1c5 100644 --- a/Calculations/_Updated/MAD_Series.cs +++ b/Calculations/_Updated/MAD_Series.cs @@ -24,7 +24,7 @@ public class MAD_Series : TSeries { protected readonly TSeries _data; //core constructors - public MAD_Series(int period, bool useNaN) : base() { + public MAD_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"MAD({period})"; @@ -62,9 +62,6 @@ public class MAD_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/zMA_Series.cs b/Calculations/_Updated/MAE_Series.cs similarity index 59% rename from Calculations/_Updated/zMA_Series.cs rename to Calculations/_Updated/MAE_Series.cs index c37a41e5..b01839d2 100644 --- a/Calculations/_Updated/zMA_Series.cs +++ b/Calculations/_Updated/MAE_Series.cs @@ -1,49 +1,57 @@ -namespace QuanTAlib; +using System.Linq; + +namespace QuanTAlib; using System; using System.Collections.Generic; /* +MAE: Mean Absolute Error + Defined as a Mean (Average) of the absolute difference between actual and estimated values. + MAE = (1/n) * Σ|y_i - MA_i| + +Sources: + https://en.wikipedia.org/wiki/Mean_absolute_error */ -public class xMA_Series : TSeries { +public class MAE_Series : TSeries { private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; protected readonly bool _NaN; protected readonly TSeries _data; //core constructors - public xMA_Series(int period, bool useNaN) : base() { + public MAE_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; - Name = $"xMA({period})"; + Name = $"MSE({period})"; } - public xMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { + public MAE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { _data = source; Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; _data.Pub += Sub; Add(_data); } - public xMA_Series() : this(period: 0, useNaN: false) { } - public xMA_Series(int period) : this(period: period, useNaN: false) { } - public xMA_Series(TBars source) : this(source.Close, 0, false) { } - public xMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public xMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public xMA_Series(TSeries source) : this(source, 0, false) { } - public xMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + public MAE_Series() : this(period: 0, useNaN: false) { } + public MAE_Series(int period) : this(period: period, useNaN: false) { } + public MAE_Series(TBars source) : this(source.Close, 0, false) { } + public MAE_Series(TBars source, int period) : this(source.Close, period, false) { } + public MAE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MAE_Series(TSeries source) : this(source, 0, false) { } + public MAE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } ////////////////// // core Add() algo public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - double _xma = 0; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _xma); + double _sma = _buffer.Average(); + + double _mae = 0; + for (int i = 0; i < _buffer.Count; i++) { _mae += Math.Abs(_buffer[i] - _sma); } + _mae /= this._buffer.Count; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mae); return base.Add(res, update); } @@ -52,9 +60,6 @@ public class xMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/MAMA_Series.cs b/Calculations/_Updated/MAMA_Series.cs new file mode 100644 index 00000000..52bd5d7b --- /dev/null +++ b/Calculations/_Updated/MAMA_Series.cs @@ -0,0 +1,189 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +MAMA: MESA Adaptive Moving Average + Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of + high/low price that uses classic electrical radio-frequency signal processing algorithms + to reduce noise. + + KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 ) + +Sources: + https://mesasoftware.com/papers/MAMA.pdf + https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/ + + */ + +public class MAMA_Series : TSeries { + private int _len; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + private double sumPr; + private double fastl, slowl; + private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt; + private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama; + public TSeries Fama { get; } + private double mamaseed, famaseed; + + //core constructors + + public MAMA_Series(double fastlimit, double slowlimit, bool useNaN) { + _period = (int)(2 / fastlimit) - 1; + fastl = fastlimit; + slowl = slowlimit; + Fama = new TSeries(); + _NaN = useNaN; + Name = $"MAMA({_period})"; + _len = 0; + } + public MAMA_Series(TSeries source, double fastlimit, double slowlimit, bool useNaN = false) : this(fastlimit, slowlimit, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MAMA_Series() : this(period: 0, useNaN: false) { } + public MAMA_Series(int period) : this(period, useNaN: false) { } + public MAMA_Series(int period, bool useNaN) : this(fastlimit: 2 / (period + 1), slowlimit: 0.2 / (period + 1), useNaN) { + _period = period; + } + public MAMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { } + public MAMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public MAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MAMA_Series(TSeries source, int period) : this(source, period, false) { } + public MAMA_Series(TSeries source, int period, bool useNaN) : this(source, fastlimit: 2 / ((double)period + 1), slowlimit: 0.2 / ((double)period + 1), useNaN: useNaN) { } + + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { + if (double.IsNaN(TValue.v)) { + return base.Add((TValue.t, Double.NaN), update); + } + if (!update) { + // roll forward (oldx = x) + pr.io = pr.i6; pr.i6 = pr.i5; pr.i5 = pr.i4; pr.i4 = pr.i3; pr.i3 = pr.i2; pr.i2 = pr.i1; pr.i1 = pr.i; + i1.io = i1.i6; i1.i6 = i1.i5; i1.i5 = i1.i4; i1.i4 = i1.i3; i1.i3 = i1.i2; i1.i2 = i1.i1; i1.i1 = i1.i; + q1.io = q1.i6; q1.i6 = q1.i5; q1.i5 = q1.i4; q1.i4 = q1.i3; q1.i3 = q1.i2; q1.i2 = q1.i1; q1.i1 = q1.i; + dt.io = dt.i6; dt.i6 = dt.i5; dt.i5 = dt.i4; dt.i4 = dt.i3; dt.i3 = dt.i2; dt.i2 = dt.i1; dt.i1 = dt.i; + sm.io = sm.i6; sm.i6 = sm.i5; sm.i5 = sm.i4; sm.i4 = sm.i3; sm.i3 = sm.i2; sm.i2 = sm.i1; sm.i1 = sm.i; + i2.io = i2.i1; i2.i1 = i2.i; q2.io = q2.i1; q2.i1 = q2.i; + re.io = re.i1; re.i1 = re.i; im.io = im.i1; im.i1 = im.i; + pd.io = pd.i1; pd.i1 = pd.i; ph.io = ph.i1; ph.i1 = ph.i; + mama.io = mama.i1; mama.i1 = mama.i; + fama.io = fama.i1; + fama.i1 = fama.i; + _len++; + } + if (_period == 0) { + fastl = 2 / (double)_len; + slowl = fastl * 0.1; + } + if (_period == 1) { + fastl = 1; + slowl = 1; + } + var i = _len - 1; + pr.i = TValue.v; + if (i > 5) { + var adj = 0.075 * pd.i1 + 0.54; + + // smooth and detrender + sm.i = (4 * pr.i + 3 * pr.i1 + 2 * pr.i2 + pr.i3) / 10; + dt.i = (0.0962 * sm.i + 0.5769 * sm.i2 - 0.5769 * sm.i4 - 0.0962 * sm.i6) * adj; + + // in-phase and quadrature + q1.i = (0.0962 * dt.i + 0.5769 * dt.i2 - 0.5769 * dt.i4 - 0.0962 * dt.i6) * adj; + i1.i = dt.i3; + + // advance the phases by 90 degrees + double jI = (0.0962 * i1.i + 0.5769 * i1.i2 - 0.5769 * i1.i4 - 0.0962 * i1.i6) * adj; + double jQ = (0.0962 * q1.i + 0.5769 * q1.i2 - 0.5769 * q1.i4 - 0.0962 * q1.i6) * adj; + + // phasor addition for 3-bar averaging + i2.i = i1.i - jQ; + q2.i = q1.i + jI; + + i2.i = 0.2 * i2.i + 0.8 * i2.i1; // smoothing it + q2.i = 0.2 * q2.i + 0.8 * q2.i1; + + // homodyne discriminator + re.i = i2.i * i2.i1 + q2.i * q2.i1; + im.i = i2.i * q2.i1 - q2.i * i2.i1; + + re.i = 0.2 * re.i + 0.8 * re.i1; // smoothing it + im.i = 0.2 * im.i + 0.8 * im.i1; + + // calculate period + pd.i = im.i != 0 && re.i != 0 ? 6.283185307179586 / Math.Atan(im.i / re.i) : 0d; + + // adjust period to thresholds + pd.i = pd.i > 1.5 * pd.i1 ? 1.5 * pd.i1 : pd.i; + pd.i = pd.i < 0.67 * pd.i1 ? 0.67 * pd.i1 : pd.i; + pd.i = pd.i < 6d ? 6d : pd.i; + pd.i = pd.i > 50d ? 50d : pd.i; + + // smooth the period + pd.i = 0.2 * pd.i + 0.8 * pd.i1; + + // determine phase position + ph.i = i1.i != 0 ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0; + + // change in phase + var delta = Math.Max(ph.i1 - ph.i, 1d); + + // adaptive alpha value + var alpha = Math.Max(fastl / delta, slowl); + + // final indicators + mama.i = alpha * (pr.i - mama.i1) + mama.i1; + fama.i = 0.5d * alpha * (mama.i - fama.i1) + fama.i1; + } + else { + sumPr += pr.i; + pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0; + mama.i = fama.i = sumPr / (i + 1); + + if (_len == 1) { + mamaseed = famaseed = TValue.v; + } + else { + mamaseed = fastl * (TValue.v - mamaseed) + mamaseed; + famaseed = slowl * (TValue.v - famaseed) + famaseed; + } + } + + double _fama = (i > 5) ? fama.i : famaseed; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fama); + Fama.Add(res, update); + double _mama = (i > 5) ? mama.i : mamaseed; + res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mama); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _len = 0; + } +} \ No newline at end of file diff --git a/Calculations/_Updated/MAPE_Series.cs b/Calculations/_Updated/MAPE_Series.cs index 776a9a12..43a6a9c6 100644 --- a/Calculations/_Updated/MAPE_Series.cs +++ b/Calculations/_Updated/MAPE_Series.cs @@ -27,7 +27,7 @@ public class MAPE_Series : TSeries { protected readonly TSeries _data; //core constructors - public MAPE_Series(int period, bool useNaN) : base() { + public MAPE_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"MAPE({period})"; @@ -68,9 +68,6 @@ public class MAPE_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/MAX_Series.cs b/Calculations/_Updated/MAX_Series.cs index 0eae2b6f..f199a329 100644 --- a/Calculations/_Updated/MAX_Series.cs +++ b/Calculations/_Updated/MAX_Series.cs @@ -17,7 +17,7 @@ public class MAX_Series : TSeries { protected readonly TSeries _data; //core constructors - public MAX_Series(int period, bool useNaN) : base() { + public MAX_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"MAX({period})"; @@ -51,9 +51,6 @@ public class MAX_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/MEDIAN_Series.cs b/Calculations/_Updated/MEDIAN_Series.cs index 18e46c67..0df36399 100644 --- a/Calculations/_Updated/MEDIAN_Series.cs +++ b/Calculations/_Updated/MEDIAN_Series.cs @@ -30,7 +30,7 @@ public class MEDIAN_Series : TSeries { protected readonly TSeries _data; //core constructors - public MEDIAN_Series(int period, bool useNaN) : base() { + public MEDIAN_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"MEDIAN({period})"; @@ -69,9 +69,6 @@ public class MEDIAN_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/MIDPOINT_Series.cs b/Calculations/_Updated/MIDPOINT_Series.cs index 686f9825..46bdd221 100644 --- a/Calculations/_Updated/MIDPOINT_Series.cs +++ b/Calculations/_Updated/MIDPOINT_Series.cs @@ -20,7 +20,7 @@ public class MIDPOINT_Series : TSeries { protected readonly TSeries _data; //core constructors - public MIDPOINT_Series(int period, bool useNaN) : base() { + public MIDPOINT_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"MIDPOINT({period})"; @@ -55,9 +55,6 @@ public class MIDPOINT_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/MIDPRICE_Series.cs b/Calculations/_Updated/MIDPRICE_Series.cs new file mode 100644 index 00000000..1bb914a8 --- /dev/null +++ b/Calculations/_Updated/MIDPRICE_Series.cs @@ -0,0 +1,65 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +MIDPRICE: Midpoint price (highhest high + lowest low)/2 in the given period in the series. + If period = 0 => period = full length of the series + + */ + +public class MIDPRICE_Series : TSeries { + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private readonly System.Collections.Generic.List _bufferhi = new(); + private readonly System.Collections.Generic.List _bufferlo = new(); + + //core constructors + public MIDPRICE_Series(int period, bool useNaN) { + _period = period; + _NaN = useNaN; + Name = $"MIDPRICE({period})"; + } + public MIDPRICE_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public MIDPRICE_Series() : this(period: 2, useNaN: false) { } + public MIDPRICE_Series(int period) : this(period: period, useNaN: false) { } + public MIDPRICE_Series(TBars source) : this(source, period: 2, useNaN: false) { } + public MIDPRICE_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { + BufferTrim(_bufferhi, TBar.h, _period, update); + BufferTrim(_bufferlo, TBar.l, _period, update); + double _mid = (_bufferhi.Max() + _bufferlo.Min()) * 0.5; + + var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _mid); + return base.Add(res, update); + } + + public new void Add(TBars data) { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _bufferhi.Clear(); + _bufferlo.Clear(); + } +} \ No newline at end of file diff --git a/Calculations/_Updated/MIN_Series.cs b/Calculations/_Updated/MIN_Series.cs index 30b68fae..3f30ffbe 100644 --- a/Calculations/_Updated/MIN_Series.cs +++ b/Calculations/_Updated/MIN_Series.cs @@ -17,7 +17,7 @@ public class MIN_Series : TSeries { protected readonly TSeries _data; //core constructors - public MIN_Series(int period, bool useNaN) : base() { + public MIN_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"MAX({period})"; @@ -51,9 +51,6 @@ public class MIN_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/MSE_Series.cs b/Calculations/_Updated/MSE_Series.cs index 18406554..262cbd6a 100644 --- a/Calculations/_Updated/MSE_Series.cs +++ b/Calculations/_Updated/MSE_Series.cs @@ -20,7 +20,7 @@ public class MSE_Series : TSeries { protected readonly TSeries _data; //core constructors - public MSE_Series(int period, bool useNaN) : base() { + public MSE_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"MSE({period})"; @@ -59,9 +59,6 @@ public class MSE_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/OBV_Series.cs b/Calculations/_Updated/OBV_Series.cs new file mode 100644 index 00000000..57531bdd --- /dev/null +++ b/Calculations/_Updated/OBV_Series.cs @@ -0,0 +1,96 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +OBV: On-Balance Volume + On-balance volume (OBV) is a technical trading momentum indicator that uses volume flow to predict + changes in stock price. Joseph Granville first developed the OBV metric in the 1963 book + Granville's New Key to Stock Market Profits. + + | +volume; if close > close[previous] + OBV = OBV[previous] + | 0; if close = close[previous] + | -volume; if close < close[previous] + +Sources: + https://www.investopedia.com/terms/o/onbalancevolume.asp + https://www.tradingview.com/wiki/On_Balance_Volume_(OBV) + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/on-balance-volume-obv/ + https://www.motivewave.com/studies/on_balance_volume.htm + +Note: + There is no consensus on what is the first OBV value in the series: + - TA-LIB uses the first volume: OBV[0] = volume[0] + - Skender stock library uses 0: OBV[0] = 0 + + */ + +public class OBV_Series : TSeries { + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private double _lastobv, _lastlastobv; + private double _lastclose, _lastlastclose; + + //core constructors + public OBV_Series(int period, bool useNaN) { + _period = period; + _NaN = useNaN; + Name = $"OBV({period})"; + this._lastobv = this._lastlastobv = 0; + this._lastclose = this._lastlastclose = 0; + } + public OBV_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public OBV_Series() : this(period: 2, useNaN: false) { } + public OBV_Series(int period) : this(period: period, useNaN: false) { } + public OBV_Series(TBars source) : this(source, period: 2, useNaN: false) { } + public OBV_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { + + if (update) { + this._lastobv = this._lastlastobv; + this._lastclose = this._lastlastclose; + } + + double _obv = this._lastobv; + if (TBar.c > this._lastclose) { _obv += TBar.v; } + if (TBar.c < this._lastclose) { _obv -= TBar.v; } + + this._lastlastobv = this._lastobv; + this._lastobv = _obv; + + this._lastlastclose = this._lastclose; + this._lastclose = TBar.c; + + var res = (TBar.t, (this.Count < this._period && this._NaN) ? double.NaN : _obv); + return base.Add(res, update); + } + + public new void Add(TBars data) { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + this._lastobv = this._lastlastobv = 0; + this._lastclose = this._lastlastclose = 0; + } +} \ No newline at end of file diff --git a/Calculations/_Updated/RMA_Series.cs b/Calculations/_Updated/RMA_Series.cs index b5be9d9c..bd84105f 100644 --- a/Calculations/_Updated/RMA_Series.cs +++ b/Calculations/_Updated/RMA_Series.cs @@ -31,7 +31,7 @@ public class RMA_Series : TSeries { protected readonly TSeries _data; //core constructor - public RMA_Series(int period, bool useNaN, bool useSMA) : base() { + public RMA_Series(int period, bool useNaN, bool useSMA) { _period = period; _NaN = useNaN; _useSMA = useSMA; @@ -57,7 +57,7 @@ public class RMA_Series : TSeries { } // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) { + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { if (update) { _lastrma = _oldrma; _sum = _oldsum; @@ -98,9 +98,6 @@ public class RMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/RSI_Series.cs b/Calculations/_Updated/RSI_Series.cs index a1cd561e..7acb6daa 100644 --- a/Calculations/_Updated/RSI_Series.cs +++ b/Calculations/_Updated/RSI_Series.cs @@ -26,7 +26,7 @@ public class RSI_Series : TSeries { private int i; //core constructors - public RSI_Series(int period, bool useNaN) : base() { + public RSI_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"RSI({period})"; @@ -103,9 +103,6 @@ public class RSI_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/SDEV_Series.cs b/Calculations/_Updated/SDEV_Series.cs index 90d269cd..4b5a91be 100644 --- a/Calculations/_Updated/SDEV_Series.cs +++ b/Calculations/_Updated/SDEV_Series.cs @@ -25,7 +25,7 @@ public class SDEV_Series : TSeries { protected readonly TSeries _data; //core constructors - public SDEV_Series(int period, bool useNaN) : base() { + public SDEV_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"SDEV({period})"; @@ -65,9 +65,6 @@ public class SDEV_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/SLOPE_Series.cs b/Calculations/_Updated/SLOPE_Series.cs new file mode 100644 index 00000000..c9fc3eae --- /dev/null +++ b/Calculations/_Updated/SLOPE_Series.cs @@ -0,0 +1,122 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +SLOPE: Slope of linear regression (using Least Square Method) + Linear Regression provides a slope of a straight line that is the best approximation of the given set of data. + The method of least squares is a standard approach in linear regression analysis to approximate the solution + by minimizing the sum of the squares of the residuals made in the results of each individual equation. + +Additional outputs provided by LINREG: + .Intercept - y-intercept point of the best fit line + .RSquared - R-Squared (R²), Coefficient of Determination + .StdDev - Standard Deviation of data over given periods + + y = Slope * x + Intercept + +Sources: + https://en.wikipedia.org/wiki/Least_squares + + */ + +public class SLOPE_Series : TSeries { + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly TSeries p_Intercept = new(); + private readonly TSeries p_RSquared = new(); + private readonly TSeries p_StdDev = new(); + private readonly System.Collections.Generic.List _buffer = new(); + public TSeries Intercept => p_Intercept; + public TSeries RSquared => p_RSquared; + public TSeries StdDev => p_StdDev; + //core constructors + public SLOPE_Series(int period, bool useNaN) { + _period = period; + _NaN = useNaN; + Name = $"SLOPE({period})"; + } + public SLOPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public SLOPE_Series() : this(period: 0, useNaN: false) { } + public SLOPE_Series(int period) : this(period: period, useNaN: false) { } + public SLOPE_Series(TBars source) : this(source.Close, 0, false) { } + public SLOPE_Series(TBars source, int period) : this(source.Close, period, false) { } + public SLOPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public SLOPE_Series(TSeries source) : this(source, 0, false) { } + public SLOPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { + BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); + + int _len = this._buffer.Count; + + // get averages for period + double sumX = 0; + double sumY = 0; + + for (int p = 0; p < _len; p++) { + sumX += this.Count - _len + 2 + p; + sumY += _buffer[p]; + } + double avgX = sumX / _len; + double avgY = sumY / _len; + + // least squares method + double sumSqX = 0; + double sumSqY = 0; + double sumSqXY = 0; + + for (int p = 0; p < _len; p++) { + double devX = this.Count - _len + 2 + p - avgX; + double devY = _buffer[p] - avgY; + + sumSqX += devX * devX; + sumSqY += devY * devY; + sumSqXY += devX * devY; + } + + double _slope = sumSqXY / sumSqX; + double _intercept = avgY - (_slope * avgX); + + // calculate Standard Deviation and R-Squared + double stdDevX = Math.Sqrt(sumSqX / _len); + double stdDevY = Math.Sqrt(sumSqY / _len); + double _StdDev = stdDevY; + + double arrr = (stdDevX * stdDevY != 0) ? sumSqXY / (stdDevX * stdDevY) / _len : 0; + double _RSquared = arrr * arrr; + + var ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _intercept); + p_Intercept.Add(ret, update); + + ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _StdDev); + p_StdDev.Add(ret, update); + + ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _RSquared); + p_RSquared.Add(ret, update); + + ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _slope); + return base.Add(ret, update); + } + + public override (DateTime t, double v) Add(TSeries data) { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + + //reset calculation + public override void Reset() { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/Calculations/_Updated/SMAPE_Series.cs b/Calculations/_Updated/SMAPE_Series.cs index 7ab08d6a..00ff1237 100644 --- a/Calculations/_Updated/SMAPE_Series.cs +++ b/Calculations/_Updated/SMAPE_Series.cs @@ -20,7 +20,7 @@ public class SMAPE_Series : TSeries { protected readonly TSeries _data; //core constructors - public SMAPE_Series(int period, bool useNaN) : base() { + public SMAPE_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"SMAPE({period})"; @@ -45,8 +45,6 @@ public class SMAPE_Series : TSeries { BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); double _sma = _buffer.Average(); - - double _smape = 0; for (int i = 0; i < _buffer.Count; i++) { _smape += Math.Abs(_buffer[i] - _sma) / (Math.Abs(_buffer[i]) + Math.Abs(_sma)); } _smape /= this._buffer.Count; @@ -60,9 +58,6 @@ public class SMAPE_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/SMA_Series.cs b/Calculations/_Updated/SMA_Series.cs index 30ccc4ba..ba25a3f6 100644 --- a/Calculations/_Updated/SMA_Series.cs +++ b/Calculations/_Updated/SMA_Series.cs @@ -25,7 +25,7 @@ public class SMA_Series : TSeries { protected readonly bool _NaN; //core constructor - public SMA_Series(int period, bool useNaN) : base() { + public SMA_Series(int period, bool useNaN) { _period = Math.Max(0, period); _NaN = useNaN; Name = $"SMA({period})"; @@ -47,7 +47,7 @@ public class SMA_Series : TSeries { ////////////////// // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) { + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { if (double.IsNaN(TValue.v)) { return (TValue.t, double.NaN); } else { if (update && _buffer.Count > 0) { @@ -77,9 +77,6 @@ public class SMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/SMMA_Series.cs b/Calculations/_Updated/SMMA_Series.cs index 50bed0ba..f653eab3 100644 --- a/Calculations/_Updated/SMMA_Series.cs +++ b/Calculations/_Updated/SMMA_Series.cs @@ -27,7 +27,7 @@ public class SMMA_Series : TSeries { private double _lastsmma, _lastlastsmma; //core constructors - public SMMA_Series(int period, bool useNaN) : base() { + public SMMA_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"SMMA({period})"; @@ -75,9 +75,6 @@ public class SMMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/SSDEV_Series.cs b/Calculations/_Updated/SSDEV_Series.cs index e295199a..20d41b95 100644 --- a/Calculations/_Updated/SSDEV_Series.cs +++ b/Calculations/_Updated/SSDEV_Series.cs @@ -25,7 +25,7 @@ public class SSDEV_Series : TSeries { protected readonly TSeries _data; //core constructors - public SSDEV_Series(int period, bool useNaN) : base() { + public SSDEV_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"SSDEV({period})"; @@ -65,9 +65,6 @@ public class SSDEV_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/SVAR_Series.cs b/Calculations/_Updated/SVAR_Series.cs index 4c4e0c8a..b121fe8b 100644 --- a/Calculations/_Updated/SVAR_Series.cs +++ b/Calculations/_Updated/SVAR_Series.cs @@ -25,7 +25,7 @@ public class SVAR_Series : TSeries { protected readonly TSeries _data; //core constructors - public SVAR_Series(int period, bool useNaN) : base() { + public SVAR_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"SVAR({period})"; @@ -64,9 +64,6 @@ public class SVAR_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/T3_Series.cs b/Calculations/_Updated/T3_Series.cs index d9abbb65..1ffac970 100644 --- a/Calculations/_Updated/T3_Series.cs +++ b/Calculations/_Updated/T3_Series.cs @@ -31,7 +31,7 @@ public class T3_Series : TSeries { protected readonly TSeries _data; //core constructors - public T3_Series(int period, double vfactor, bool useSMA, bool useNaN) : base() { + public T3_Series(int period, double vfactor, bool useSMA, bool useNaN) { _period = period; _len = 0; _NaN = useNaN; @@ -137,9 +137,6 @@ public class T3_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/TBars.cs b/Calculations/_Updated/TBars.cs index 8387219d..920dd17c 100644 --- a/Calculations/_Updated/TBars.cs +++ b/Calculations/_Updated/TBars.cs @@ -84,16 +84,16 @@ public class TBars : System.Collections.Generic.List<(DateTime t, double o, doub }; } - public virtual (DateTime t, double o, double h, double l, double c, double v) Add((double o, double h, double l, double c, double v) p, bool update = false) => + public virtual (DateTime t, double v) Add((double o, double h, double l, double c, double v) p, bool update = false) => Add((t: (this.Count == 0) ? DateTime.Today : this[^1].t.AddDays(1),p.o,p.h,p.l,p.c,p.v),update); - public virtual (DateTime t, double o, double h, double l, double c, double v) Add(double o, double h, double l, double c, double v, bool update = false) => + public virtual (DateTime t, double v) Add(double o, double h, double l, double c, double v, bool update = false) => Add((o,h,l,c,v),update); - public virtual (DateTime t, double o, double h, double l, double c, double v) Add(DateTime t, double o, double h, double l, double c, double v, bool update = false) => + public virtual (DateTime t, double v) Add(DateTime t, double o, double h, double l, double c, double v, bool update = false) => this.Add((t, o, h, l, c, v), update); - public virtual (DateTime t, double o, double h, double l, double c, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { + public virtual (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { if (update) { this[^1] = TBar; } else { base.Add(TBar); } _open.Add((TBar.t, TBar.o), update); @@ -109,8 +109,8 @@ public class TBars : System.Collections.Generic.List<(DateTime t, double o, doub _hlcc4.Add((TBar.t, (TBar.h + TBar.l + TBar.c + TBar.c) * 0.25), update); this.OnEvent(update); - return TBar; - } + return (TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25); + } public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); public event NewDataEventHandler Pub; @@ -120,7 +120,19 @@ public class TBars : System.Collections.Generic.List<(DateTime t, double o, doub public void Sub(object source, TSeriesEventArgs e) { TBars ss = (TBars)source; if (ss.Count > 1) { for (int i = 0; i < ss.Count; i++) { this.Add(ss[i]); } } else { - this.Add(ss[ss.Count - 1], e.update); + this.Add(ss[^1], e.update); } } + + /// common helpers + public static void BufferTrim(System.Collections.Generic.List buffer, double value, int period, bool update) { + if (!update) { + buffer.Add(value); + if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); } + return; + } + buffer[^1] = value; + } + public virtual void Reset() { + } } diff --git a/Calculations/_Updated/TEMA_Series.cs b/Calculations/_Updated/TEMA_Series.cs index b109995b..a9653f6b 100644 --- a/Calculations/_Updated/TEMA_Series.cs +++ b/Calculations/_Updated/TEMA_Series.cs @@ -29,7 +29,7 @@ public class TEMA_Series : TSeries { protected readonly TSeries _data; //core constructor - public TEMA_Series(int period, bool useNaN, bool useSMA) : base() { + public TEMA_Series(int period, bool useNaN, bool useSMA) { _period = period; _NaN = useNaN; _useSMA = useSMA; @@ -102,9 +102,6 @@ public class TEMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/TRIMA_Series.cs b/Calculations/_Updated/TRIMA_Series.cs index 70313693..210be772 100644 --- a/Calculations/_Updated/TRIMA_Series.cs +++ b/Calculations/_Updated/TRIMA_Series.cs @@ -17,13 +17,13 @@ Remark: public class TRIMA_Series : TSeries { private readonly int _p1a, _p1b; - private SMA_Series sma, trima; + private readonly SMA_Series sma, trima; protected readonly int _period; protected readonly bool _NaN; protected readonly TSeries _data; //core constructors - public TRIMA_Series(int period, bool useNaN) : base() { + public TRIMA_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"xMA({period})"; @@ -66,9 +66,6 @@ public class TRIMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/TRIX_Series.cs b/Calculations/_Updated/TRIX_Series.cs index 243dba19..30d9f58e 100644 --- a/Calculations/_Updated/TRIX_Series.cs +++ b/Calculations/_Updated/TRIX_Series.cs @@ -21,7 +21,7 @@ public class TRIX_Series : TSeries { private readonly System.Collections.Generic.List _buffer3 = new(); private double _lastema1, _lastema2, _lastema3; private double _llastema1, _llastema2, _llastema3; - + private int _len; private readonly bool _useSMA; protected readonly int _period; protected readonly bool _NaN; @@ -29,12 +29,13 @@ public class TRIX_Series : TSeries { //core constructors - public TRIX_Series(int period, bool useNaN, bool useSMA) : base() { + public TRIX_Series(int period, bool useNaN, bool useSMA) { _period = period; _NaN = useNaN; _useSMA = useSMA; Name = $"TRIX({period})"; _k = 2.0 / (_period + 1); + _len = 0; _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = 0; } public TRIX_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) { @@ -54,13 +55,14 @@ public class TRIX_Series : TSeries { ////////////////// // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) { + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { if (double.IsNaN(TValue.v)) { return base.Add((TValue.t, Double.NaN), update); } - if (this.Count == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; } + if (_len == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; } if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; } - else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; } + else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _len++; + } double _ema1, _ema2, _ema3; if ((this.Count < _period) && _useSMA) { @@ -99,9 +101,6 @@ public class TRIX_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } @@ -114,6 +113,6 @@ public class TRIX_Series : TSeries { //reset calculation public override void Reset() { - + _len = 0; } } \ No newline at end of file diff --git a/Calculations/_Updated/TR_Series.cs b/Calculations/_Updated/TR_Series.cs new file mode 100644 index 00000000..e3c61058 --- /dev/null +++ b/Calculations/_Updated/TR_Series.cs @@ -0,0 +1,79 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +TR: True Range + True Range was introduced by J. Welles Wilder in his book New Concepts in Technical Trading Systems. + It measures the daily range plus any gap from the closing price of the preceding day. + +Calculation: + d1 = ABS(High - Low) + d2 = ABS(High - Previous close) + d3 = ABS(Previous close - Low) + TR = MAX(d1,d2,d3) + +Sources: + https://www.macroption.com/true-range/ + + */ + +public class TR_Series : TSeries { + protected readonly TBars _data; + private double _cm1, _cm1_o; + + //core constructors + public TR_Series() { + Name = $"TR()"; + _cm1 = _cm1_o = double.NaN; + } + public TR_Series(TBars source) { + _data = source; + Name = $"TR({(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _cm1 = _cm1_o = double.NaN; + _data.Pub += Sub; + Add(data: _data); + } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { + + if (update) { + _cm1 = _cm1_o; + } + else { + _cm1_o = _cm1; + } + + if (_cm1 is double.NaN) { + _cm1 = TBar.c; + } + + double d1 = Math.Abs(TBar.h - TBar.l); + double d2 = Math.Abs(_cm1 - TBar.h); + double d3 = Math.Abs(_cm1 - TBar.l); + _cm1 = TBar.c; + var ret = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); + return base.Add(ret, update); + + } + + public new void Add(TBars data) { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() { + _cm1 = _cm1_o = double.NaN; + } +} \ No newline at end of file diff --git a/Calculations/_Updated/TSeries.cs b/Calculations/_Updated/TSeries.cs index 65c09886..f7becdb3 100644 --- a/Calculations/_Updated/TSeries.cs +++ b/Calculations/_Updated/TSeries.cs @@ -22,7 +22,7 @@ public class TSeriesEventArgs : EventArgs { public class TSeries : List<(DateTime t, double v)> { public List t => this.Select(item => item.t).ToList(); public List v => this.Select(item => item.v).ToList(); - public (DateTime t, double v) Last => this[^1]; + public (DateTime t, double v) Last => this[this.Count - 1]; public int Length => Count; public string Name { get; set; } @@ -35,13 +35,13 @@ public class TSeries : List<(DateTime t, double v)> { } public virtual (DateTime t, double v) Add(double v, bool update = false) { - var Value = (t: Count == 0 ? DateTime.Today : this[^1].t.AddDays(1), v); + var Value = (t: Count == 0 ? DateTime.Today : this[this.Count-1].t.AddDays(1), v); return Add(Value, update); } public virtual (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { if (update) { - this[^1] = TValue; + this[this.Count-1] = TValue; } else { base.Add(TValue); @@ -51,15 +51,32 @@ public class TSeries : List<(DateTime t, double v)> { return TValue; } + public virtual (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { + if (update) { + this[this.Count - 1] = (TBar.t, TBar.c); + } + else { + base.Add((TBar.t, TBar.c)); + } + + OnEvent(update); + return (TBar.t, TBar.c); + } + public virtual (DateTime t, double v) Add(TSeries data) { - foreach (var item in data) { Add(item, false); } + foreach (var item in data) { Add(item); } return data.Last; } + public virtual (DateTime t, double v) Add(TBars data) { + foreach (var item in data) { Add(item.c, false); } + return (data.Last.t, data.Last.c); + } + public void Sub(object source, TSeriesEventArgs e) { var data = (TSeries) source; if (data == null) { return; } - foreach (var item in data) { Add(item, update: false); } + foreach (var item in data) { Add(item); } } public delegate void NewEventHandler(object source, TSeriesEventArgs args); diff --git a/Calculations/_Updated/VAR_Series.cs b/Calculations/_Updated/VAR_Series.cs index 97ad50c3..72562277 100644 --- a/Calculations/_Updated/VAR_Series.cs +++ b/Calculations/_Updated/VAR_Series.cs @@ -25,7 +25,7 @@ public class VAR_Series : TSeries { protected readonly TSeries _data; //core constructors - public VAR_Series(int period, bool useNaN) : base() { + public VAR_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"VAR({period})"; @@ -64,9 +64,6 @@ public class VAR_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/WMAPE_Series.cs b/Calculations/_Updated/WMAPE_Series.cs index b7254958..b0479cb8 100644 --- a/Calculations/_Updated/WMAPE_Series.cs +++ b/Calculations/_Updated/WMAPE_Series.cs @@ -22,7 +22,7 @@ public class WMAPE_Series : TSeries { protected readonly TSeries _data; //core constructors - public WMAPE_Series(int period, bool useNaN) : base() { + public WMAPE_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"WMAPE({period})"; @@ -65,9 +65,6 @@ public class WMAPE_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/WMA_Series.cs b/Calculations/_Updated/WMA_Series.cs index f8d8bb39..48679fe4 100644 --- a/Calculations/_Updated/WMA_Series.cs +++ b/Calculations/_Updated/WMA_Series.cs @@ -19,7 +19,7 @@ Sources: public class WMA_Series : TSeries { private readonly System.Collections.Generic.List _buffer = new(); - private System.Collections.Generic.List _weights = new(); + private System.Collections.Generic.List _weights; protected int _period; protected readonly bool _NaN; protected readonly TSeries _data; @@ -30,7 +30,7 @@ public class WMA_Series : TSeries { } //core constructors - public WMA_Series(int period, bool useNaN) : base() { + public WMA_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"WMA({period})"; @@ -76,9 +76,6 @@ public class WMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/ZLEMA_Series.cs b/Calculations/_Updated/ZLEMA_Series.cs index 81ad2d67..512b5cc0 100644 --- a/Calculations/_Updated/ZLEMA_Series.cs +++ b/Calculations/_Updated/ZLEMA_Series.cs @@ -30,7 +30,7 @@ public class ZLEMA_Series : TSeries { private readonly EMA_Series _ema; //core constructor - public ZLEMA_Series(int period, bool useNaN, bool useSMA) : base() { + public ZLEMA_Series(int period, bool useNaN, bool useSMA) { _period = period; _NaN = useNaN; Name = $"ZLEMA({period})"; @@ -54,7 +54,7 @@ public class ZLEMA_Series : TSeries { } // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) { + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); int _lag; if (_period == 0) { @@ -76,9 +76,7 @@ public class ZLEMA_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } + public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/ZL_Series.cs b/Calculations/_Updated/ZL_Series.cs index cfb71d20..0f6af1aa 100644 --- a/Calculations/_Updated/ZL_Series.cs +++ b/Calculations/_Updated/ZL_Series.cs @@ -26,7 +26,7 @@ public class ZL_Series: TSeries { private readonly EMA_Series _ema; //core constructor - public ZL_Series(int period, bool useNaN, bool useSMA) : base() { + public ZL_Series(int period, bool useNaN, bool useSMA) { _period = period; _NaN = useNaN; Name = $"ZL({period})"; @@ -50,7 +50,7 @@ public class ZL_Series: TSeries { } // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) { + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); int _lag; if (_period == 0) { @@ -72,9 +72,6 @@ public class ZL_Series: TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Calculations/_Updated/ZSCORE_Series.cs b/Calculations/_Updated/ZSCORE_Series.cs index 8b213c04..c3ab4288 100644 --- a/Calculations/_Updated/ZSCORE_Series.cs +++ b/Calculations/_Updated/ZSCORE_Series.cs @@ -31,7 +31,7 @@ public class ZSCORE_Series : TSeries { protected readonly TSeries _data; //core constructors - public ZSCORE_Series(int period, bool useNaN) : base() { + public ZSCORE_Series(int period, bool useNaN) { _period = period; _NaN = useNaN; Name = $"ZSCORE({period})"; @@ -71,9 +71,6 @@ public class ZSCORE_Series : TSeries { foreach (var item in data) { Add(item, false); } return _data.Last; } - public new (DateTime t, double v) Add((DateTime t, double v) TValue) { - return Add(TValue, false); - } public (DateTime t, double v) Add(bool update) { return this.Add(TValue: _data.Last, update: update); } diff --git a/Indicators/Charts/2MASlope_chart.cs b/Indicators/Charts/2MASlope_chart.cs index 360af677..547dc860 100644 --- a/Indicators/Charts/2MASlope_chart.cs +++ b/Indicators/Charts/2MASlope_chart.cs @@ -39,7 +39,7 @@ public class MovingAverageSlope_chart : Indicator { private bool LongTrades = true; [InputParameter("Short trades", 8)] - private bool ShortTrades = false; + private bool ShortTrades; #endregion Parameters @@ -48,7 +48,7 @@ public class MovingAverageSlope_chart : Indicator { /////// private TSeries MA1, MA2; - private LINREG_Series sMA1, sMA2; + private SLOPE_Series sMA1, sMA2; private CROSS_Series sig1, sig2; private bool inLong, inShort; @@ -276,11 +276,12 @@ public class MovingAverageSlope_chart : Indicator { Graphics graphics = args.Graphics; var mainWindow = this.CurrentChart.MainWindow; int leftIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left)); - int rightIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right))); + int rightIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right))); +/* int historycount = HistoricalData.Count; int ymax = mainWindow.ClientRectangle.Height; -/* + for (int i = leftIndex; i <= rightIndex; i++) { int xi = (int)Math.Round(mainWindow.CoordinatesConverter.GetChartX(Time(Count - 1 - i))); int width = this.CurrentChart.BarsWidth; diff --git a/Indicators/Charts/JMA_chart.cs b/Indicators/Charts/JMA_chart.cs index 9390ea59..7d5e5341 100644 --- a/Indicators/Charts/JMA_chart.cs +++ b/Indicators/Charts/JMA_chart.cs @@ -24,7 +24,7 @@ public class JMA_chart : Indicator { private int Vlong = 65; [InputParameter("Phase", 4, -100, 100, 1, 2)] - private double Jphase = 0.0; + private double Jphase; #endregion Parameters @@ -38,7 +38,7 @@ public class JMA_chart : Indicator { protected int firstOnScreenBarIndex, lastOnScreenBarIndex; protected HistoricalData History; protected int HistPeriod; - public JMA_chart() :base() { + public JMA_chart() { Name = "JMA - Jurik Moving Avg"; Description = "Jurik Moving Average description"; AddLineSeries(lineName: "JMA", lineColor: Color.Yellow, lineWidth: 3,lineStyle: LineStyle.Solid); @@ -80,8 +80,10 @@ public class JMA_chart : Indicator { } public override void OnPaintChart(PaintChartEventArgs args) { base.OnPaintChart(args); - if (this.CurrentChart == null) + if (this.CurrentChart == null) { return; + } + graphics = args.Graphics; mainWindow = this.CurrentChart.MainWindow; diff --git a/Indicators/Charts/TrailingStop.cs b/Indicators/Charts/TrailingStop.cs index 2f20542e..29aee917 100644 --- a/Indicators/Charts/TrailingStop.cs +++ b/Indicators/Charts/TrailingStop.cs @@ -30,7 +30,7 @@ public class TrailingStop_chart : Indicator { /////// - public TrailingStop_chart() :base() { + public TrailingStop_chart() { Name = $"ATR Trailing Stop"; AddLineSeries(lineName: "TrailingATR Long", lineColor: Color.Yellow, lineWidth: 1,lineStyle: LineStyle.Dot); AddLineSeries(lineName: "Ratchet Long", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); @@ -91,30 +91,5 @@ public class TrailingStop_chart : Indicator { this.SetValue(_tslineS, lineIndex: 2); this.SetValue(_ratchetS, lineIndex: 3); } - - public override void OnPaintChart(PaintChartEventArgs args) { - base.OnPaintChart(args); - if (this.CurrentChart == null) { return; } - Graphics graphics = args.Graphics; - var mainWindow = this.CurrentChart.MainWindow; - int leftIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left)); - int rightIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right))); - int historycount = HistoricalData.Count; - int ymax = mainWindow.ClientRectangle.Height; - - /* - for (int i = leftIndex; i <= rightIndex; i++) { - int xi = (int)Math.Round(mainWindow.CoordinatesConverter.GetChartX(Time(Count - 1 - i))); - int width = this.CurrentChart.BarsWidth; - int height = (int)((equity[i+historycount].v) *proportion); - - Brush bb = Brushes.DarkSlateGray; - bb = (overunder[i+historycount].v>0 && LongTrades)? Brushes.Green : bb; - bb = (overunder[i + historycount].v < 0 && ShortTrades) ? Brushes.Red : bb; - - graphics.FillRectangle(bb, xi, ymax - height, width, height); - } - */ - } } diff --git a/Strategies/SimpleMACross1.cs b/Strategies/SimpleMACross1.cs index f6ffaeb6..ffefdb15 100644 --- a/Strategies/SimpleMACross1.cs +++ b/Strategies/SimpleMACross1.cs @@ -14,16 +14,16 @@ namespace SimpleMACross { public Account CurrentAccount { get; set; } [InputParameter("Fast MA", 2, minimum: 1, maximum: 100, increment: 1, decimalPlaces: 0)] - public int FastMA = 5; + private int FastMA = 5; [InputParameter("Slow MA", 3, minimum: 1, maximum: 100, increment: 1, decimalPlaces: 0)] - public int SlowMA = 10; + private int SlowMA = 10; [InputParameter("Quantity", 4, 0.1, 99999, 0.1, 2)] - public double Quantity = 1.0; + private double Quantity = 1.0; [InputParameter("Period", 5)] - public Period period = Period.MIN1; + private Period period = Period.MIN1; public override string[] MonitoringConnectionsIds => new string[] { this.CurrentSymbol?.ConnectionId, this.CurrentAccount?.ConnectionId }; @@ -31,9 +31,8 @@ namespace SimpleMACross { private DateTime prev_time; private readonly TBars bars = new(); - public SimpleMACross1() - : base() { - this.Name = "Miha MA Cross strategy 3"; + public SimpleMACross1() { + this.Name = "MA Cross strategy 3"; this.Description = "Raw strategy without any additional functional"; } @@ -73,24 +72,7 @@ namespace SimpleMACross { // An example of adding custom strategy metrics: result.Add("Bars processed", this.bars.Count.ToString()); - /* - result.Add("Trades [#]", "0"); - result.Add("Long trades [#]", this.longPositionsCount.ToString()); - result.Add("Short trades [#]", this.shortPositionsCount.ToString()); - result.Add("Profitable trades [#]", "0"); - result.Add("Win Rate [%]", "0"); - result.Add("Best Trade [%]", "0"); - result.Add("Worst Trade[%]", "0"); - result.Add("Avg Winning Trade [%]", "0"); - result.Add("Avg Losing Trade [%]", "0"); - result.Add("Profit Factor", "0"); - result.Add("Sharpe Ratio", "0"); - result.Add("Sortino Ratio", "0"); - result.Add("Omega Ratio", "0"); - result.Add("Calmar Ratio", "0"); - result.Add("Beta", "0"); - result.Add("Alpha", "0"); - */ + return result; } diff --git a/Strategies/Strategies.csproj b/Strategies/Strategies.csproj index 08f3bcdf..78d3a39f 100644 --- a/Strategies/Strategies.csproj +++ b/Strategies/Strategies.csproj @@ -34,9 +34,6 @@ True anycpu - - - diff --git a/Tests/Basic tests/Indicators.cs b/Tests/Basic tests/Indicators.cs index 29476d01..de49baae 100644 --- a/Tests/Basic tests/Indicators.cs +++ b/Tests/Basic tests/Indicators.cs @@ -26,6 +26,8 @@ public class Indicators typeof(T3_Series), typeof(KAMA_Series), typeof(TRIMA_Series), + typeof(MAMA_Series), + typeof(HWMA_Series), }; [Theory] diff --git a/Tests/Basic tests/Oscillators.cs b/Tests/Basic tests/Oscillators.cs index 458386fb..7d87e784 100644 --- a/Tests/Basic tests/Oscillators.cs +++ b/Tests/Basic tests/Oscillators.cs @@ -7,7 +7,7 @@ namespace Basics; #nullable disable public class Oscillators { - private static Type[] maSeriesTypes = new Type[] + private static Type[] maSeriesTypes = new[] { typeof(BIAS_Series), typeof(MAX_Series), @@ -19,7 +19,8 @@ public class Oscillators typeof(KURTOSIS_Series), typeof(MAD_Series), typeof(MAPE_Series), - typeof(MSE_Series), + typeof(MAE_Series), + typeof(MSE_Series), typeof(SDEV_Series), typeof(SMAPE_Series), typeof(WMAPE_Series), @@ -31,6 +32,7 @@ public class Oscillators typeof(CMO_Series), typeof(RSI_Series), typeof(TRIX_Series), + typeof(BBANDS_Series), }; [Theory] diff --git a/Tests/Basic tests/TBars_input.cs b/Tests/Basic tests/TBars_input.cs new file mode 100644 index 00000000..f3ee38b2 --- /dev/null +++ b/Tests/Basic tests/TBars_input.cs @@ -0,0 +1,96 @@ +using Xunit; +using System; +using System.Runtime.InteropServices; +using QuanTAlib; + +namespace Basics; +#nullable disable +public class TBars +{ + private static Type[] maSeriesTypes = new Type[] + { + typeof(ATR_Series), + typeof(ATRP_Series), + typeof(TR_Series), + typeof(ADL_Series), + typeof(CCI_Series), + typeof(OBV_Series), + typeof(ADOSC_Series), + typeof(MIDPRICE_Series), +}; + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Name_exists(Type classType) + { + GBM_Feed data = new(10); + + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + Assert.NotEmpty(MA_Series.Name); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Series_Length(Type classType) + { + GBM_Feed data = new(1000); + + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + Assert.Equal(1000, MA_Series.Count); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Return_data(Type classType) + { + GBM_Feed data = new(10); + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + var result = MA_Series.Add((DateTime.Today, 1,2,3,4,5)); + Assert.Equal(result.v, MA_Series.Last.v); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Update(Type classType) + { + GBM_Feed data = new(10); + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + var pre_update = MA_Series.Last; + + var pre_data = data.Last; + data.Add((DateTime.Today, 1, 2, 3, 4, 5), true); + data.Add(pre_data, true); + + Assert.Equal(pre_update.v, MA_Series.Last.v); + Assert.Equal(data.Count, MA_Series.Count); +} + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Reset(Type classType) + { + GBM_Feed data = new(10); + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + MA_Series.Reset(); + data.Add(); + Assert.False(double.IsNaN(MA_Series.Last.v)); +} + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Period_default(Type classType) { + GBM_Feed data = new(100); + + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + Assert.False(double.IsNaN(MA_Series.Last.v)); + } + + public static IEnumerable MASeriesData() + { + foreach (var type in maSeriesTypes) + { + yield return new object[] { type }; + } + } +} +#nullable restore \ No newline at end of file diff --git a/Tests/Validations/Trends/Skender.cs b/Tests/Validations/Trends/Skender.cs index 530af8ea..8f985767 100644 --- a/Tests/Validations/Trends/Skender.cs +++ b/Tests/Validations/Trends/Skender.cs @@ -33,7 +33,7 @@ public class Skender [Fact] public void ADL() { - ADL_Series QL = new(bars, false); + ADL_Series QL = new(bars); var SK = quotes.GetAdl().Select(i => i.Adl); for (int i = QL.Length; i > skip; i--) { @@ -228,9 +228,9 @@ public class Skender } } [Fact] - public void LINREG() + public void SLOPE() { - LINREG_Series QL = new(bars.Close, period, useNaN: false); + SLOPE_Series QL = new(bars.Close, period, useNaN: false); var SK = quotes.GetSlope(period); for (int i = QL.Length; i > skip; i--) { @@ -447,7 +447,7 @@ public class Skender [Fact] public void TR() { - TR_Series QL = new(bars, useNaN: false); + TR_Series QL = new(bars); var SK = quotes.GetTr().Select(i => i.Tr.Null2NaN()!); for (int i = QL.Length; i > skip; i--) { diff --git a/Tests/Validations/Trends/TA_LIB.cs b/Tests/Validations/Trends/TA_LIB.cs index 11282618..7a663212 100644 --- a/Tests/Validations/Trends/TA_LIB.cs +++ b/Tests/Validations/Trends/TA_LIB.cs @@ -48,7 +48,7 @@ public class Ta_Lib [Fact] public void ADL() { - ADL_Series QL = new(bars, false); + ADL_Series QL = new(bars); Core.Ad(inhigh, inlow, inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); for (int i = QL.Length - 1; i > 0; i--) { @@ -424,7 +424,7 @@ public class Ta_Lib [Fact] public void TR() { - TR_Series QL = new(bars, false); + TR_Series QL = new(bars); Core.TRange(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); for (int i = QL.Length - 1; i > skip; i--) { diff --git a/Tests/Validations/Trends/Tulip.cs b/Tests/Validations/Trends/Tulip.cs index 959b16d4..4f57e27f 100644 --- a/Tests/Validations/Trends/Tulip.cs +++ b/Tests/Validations/Trends/Tulip.cs @@ -36,7 +36,7 @@ public class Tulip_Test { double[][] arrin = {inhigh, inlow, inclose, involume }; double[][] arrout = { outdata }; - ADL_Series QL = new(bars, false); + ADL_Series QL = new(bars); Tulip.Indicators.ad.Run(inputs: arrin, options: new double[] { }, outputs: arrout); for (int i = QL.Length - 1; i > skip; i--) { @@ -274,7 +274,7 @@ public class Tulip_Test public void LINREG() { double[][] arrin = { inclose }; double[][] arrout = { outdata }; - LINREG_Series QL = new(bars.Close, period); + SLOPE_Series QL = new(bars.Close, period); Tulip.Indicators.linregslope.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); for (int i = QL.Length - 1; i > skip; i--) { double QL_item = QL[i].v; @@ -438,7 +438,7 @@ public class Tulip_Test public void TR() { double[][] arrin = { inhigh,inlow,inclose }; double[][] arrout = { outdata }; - TR_Series QL = new(bars, false); + TR_Series QL = new(bars); Tulip.Indicators.tr.Run(inputs: arrin, options: new double[] {}, outputs: arrout); for (int i = QL.Length - 1; i > skip; i--) { double QL_item = QL[i].v; diff --git a/docs/indicators.md b/docs/indicators.md index 13d2a95c..c1545a8d 100644 --- a/docs/indicators.md +++ b/docs/indicators.md @@ -33,8 +33,9 @@ |EDECAY - Exponential Decay|`DECAY_Series`|||decay|✔️edecay| |ENTROPY - Entropy|`ENTROPY_Series`|||entropy|| |KURTOSIS - Kurtosis|`KURT_Series`|||✔️kurtosis| -|LINREG - Linear Regression|`LINREG_Series`||✔️GetSlope||✔️linregslope| +|SLOPE - Slope of Linear Regression|`SLOPE_Series`||✔️GetSlope||✔️linregslope| |MAD - Mean Absolute Deviation|`MAD_Series`||✔️GetSmaAnalysis|✔️mad| +|MAE - Mean Absolute Error|`MAE_Series`|||| |MAPE - Mean Absolute Percent Error|`MAPE_Series`||✔️GetSmaAnalysis|| |MEDIAN - Median value|`MEDIAN_Series`|||✔️median| |MSE - Mean Squared Error|`MSE_Series`||✔️GetSmaAnalysis||