From 50ae1d4917d2dcb73af837352577cbc49b754a5f Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Thu, 28 Apr 2022 12:52:00 -0700 Subject: [PATCH] CCI --- Docs/coverage.md | 2 +- Quantower/Indicators/CCI_chart.cs | 43 +++++++++++++++++++++++++ Quantower/Indicators/ZLMA_chart.cs | 8 ++--- Quantower/Quantower.csproj | 13 +++----- Source/Basics/Abstracts.cs | 2 +- Source/Indicators/CCI_Series.cs | 50 ++++++++++++++++++++++++++++++ Source/Indicators/MACD_Series.cs | 8 ++--- Source/Indicators/RSI_Series.cs | 8 ++--- Tests/Tests.csproj | 1 + Tests/Validations/Skender_Stock.cs | 8 +++++ Tests/Validations/TA_LIB.cs | 9 ++++++ 11 files changed, 129 insertions(+), 23 deletions(-) create mode 100644 Quantower/Indicators/CCI_chart.cs create mode 100644 Source/Indicators/CCI_Series.cs diff --git a/Docs/coverage.md b/Docs/coverage.md index e8bb893f..7e3a3c51 100644 --- a/Docs/coverage.md +++ b/Docs/coverage.md @@ -89,7 +89,7 @@ | AROON - Aroon oscillator ||✔️|✔️|✔️| | BBANDS - Bollinger Bands ||✔️|✔️|✔️| | BOP - Balance of Power ||✔️|✔️|✔️| -| CCI - Commodity Channel Index ||✔️|✔️|✔️| +| CCI - Commodity Channel Index |✔️|✔️|✔️|✔️| | CFO - Chande Forcast Oscillator ||||✔️| | CMF - Chaikin Money Flow |||✔️|✔️| | CMO - Chande Momentum Oscillator ||✔️||✔️| diff --git a/Quantower/Indicators/CCI_chart.cs b/Quantower/Indicators/CCI_chart.cs new file mode 100644 index 00000000..02a6bb57 --- /dev/null +++ b/Quantower/Indicators/CCI_chart.cs @@ -0,0 +1,43 @@ +using System.Diagnostics; +using System.Drawing; +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class CCI_chart : Indicator +{ + #region Parameters + + [InputParameter("Smoothing period", 0, 1, 999, 1, 1)] + private readonly int Period = 10; + + #endregion Parameters + + private TBars bars; + + /////// + private CCI_Series indicator; + /////// + + public CCI_chart() + { + this.SeparateWindow = true; + this.Name = "CCI - Commodity Channel Index"; + this.Description = "CCI description"; + this.AddLineSeries("CCI", Color.RoyalBlue, 3, LineStyle.Solid); + } + + protected override void OnInit() + { + this.ShortName = "CCI (" + this.Period + ")"; + this.bars = new(); + this.indicator = new(source: bars, period: this.Period, useNaN: false); + } + protected override void OnUpdate(UpdateArgs args) + { + bool update = (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar); + this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), this.GetPrice(PriceType.High), this.GetPrice(PriceType.Low), + this.GetPrice(PriceType.Close), this.GetPrice(PriceType.Volume), update); + double result = this.indicator[this.indicator.Count - 1].v; + this.SetValue(result); + } +} diff --git a/Quantower/Indicators/ZLMA_chart.cs b/Quantower/Indicators/ZLMA_chart.cs index 61020e38..35cddbdf 100644 --- a/Quantower/Indicators/ZLMA_chart.cs +++ b/Quantower/Indicators/ZLMA_chart.cs @@ -9,12 +9,12 @@ public class ZLMA_chart : Indicator #region Parameters [InputParameter("Smoothing period", 0, 1, 999, 1, 1)] - private int Period = 10; + private readonly int Period = 10; [InputParameter("Data source", 1, variants: new object[] { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int DataSource = 3; + private readonly int DataSource = 3; [InputParameter("MA algorithm", 2, variants: new object[] { "SMA", 0, @@ -27,7 +27,7 @@ public class ZLMA_chart : Indicator "JMA", 7, "SMMA", 8 })] - private int matype = 2; + private readonly int matype = 2; #endregion Parameters @@ -87,7 +87,7 @@ public class ZLMA_chart : Indicator this.GetPrice(PriceType.Close), this.GetPrice(PriceType.Volume), update); - double result = this.indicator[this.indicator.Count - 1].v; + double result = this.indicator[this.indicator.Count-1].v; this.SetValue(result); } } diff --git a/Quantower/Quantower.csproj b/Quantower/Quantower.csproj index cdf21a7c..bf162cf9 100644 --- a/Quantower/Quantower.csproj +++ b/Quantower/Quantower.csproj @@ -2,7 +2,7 @@ net48 - latest + preview true AnyCPU Indicator @@ -14,7 +14,6 @@ disable False - True 3 @@ -22,7 +21,6 @@ anycpu full - embedded True @@ -30,20 +28,17 @@ True anycpu - - + QuanTAlib\%(RecursiveDir)%(Filename)%(Extension) - - - .\dll\TradingPlatform.BusinessLayer.dll + C:\Quantower\TradingPlatform\v1.124.6\bin\TradingPlatform.BusinessLayer.dll - + \ No newline at end of file diff --git a/Source/Basics/Abstracts.cs b/Source/Basics/Abstracts.cs index 1233687c..85701a21 100644 --- a/Source/Basics/Abstracts.cs +++ b/Source/Basics/Abstracts.cs @@ -145,7 +145,7 @@ public abstract class Single_TBars_Indicator : TSeries } // 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.c, update); + public virtual void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar, bool update) => base.Add((TBar.t, TBar.c), update); // potentially overridable Add() method for the whole series (could be replaced with faster bulk algo) public virtual void Add(TBars bars) diff --git a/Source/Indicators/CCI_Series.cs b/Source/Indicators/CCI_Series.cs new file mode 100644 index 00000000..bac14b9e --- /dev/null +++ b/Source/Indicators/CCI_Series.cs @@ -0,0 +1,50 @@ +namespace QuanTAlib; +using System; + +/* +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 = 0; + for (int i = 0; i < this._tp.Count; i++) { _avgTp+=this._tp[i]; } + _avgTp /= this._tp.Count; + + // 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); + + var result = (TBar.t, (this.Count < this._p && this._NaN) ? double.NaN : _cci); + base.Add(result, update); + } +} \ No newline at end of file diff --git a/Source/Indicators/MACD_Series.cs b/Source/Indicators/MACD_Series.cs index 0e75a26e..b8172d46 100644 --- a/Source/Indicators/MACD_Series.cs +++ b/Source/Indicators/MACD_Series.cs @@ -16,9 +16,9 @@ Sources: public class MACD_Series : Single_TSeries_Indicator { - private EMA_Series _TSslow; - private EMA_Series _TSfast; - private SUB_Series _TSmacd; + 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) @@ -27,7 +27,7 @@ public class MACD_Series : Single_TSeries_Indicator _TSslow = new(source: source, period: slow, useNaN: false); _TSfast = new(source: source, period: fast, useNaN: false); _TSmacd = new(_TSfast, _TSslow); - Signal = new(source: _TSmacd, period: signal, useNaN: useNaN); + this.Signal = new(source: _TSmacd, period: signal, useNaN: useNaN); if (source.Count > 0) { base.Add(_TSmacd); } } diff --git a/Source/Indicators/RSI_Series.cs b/Source/Indicators/RSI_Series.cs index e9d57f39..5cec4d88 100644 --- a/Source/Indicators/RSI_Series.cs +++ b/Source/Indicators/RSI_Series.cs @@ -16,10 +16,10 @@ public class RSI_Series : Single_TSeries_Indicator { private readonly System.Collections.Generic.List _gain = new(); private readonly System.Collections.Generic.List _loss = new(); - double _avgGain = 0; - double _avgLoss = 0; - double _lastValue = 0; - double _lastlastValue = 0; + private double _avgGain; + private double _avgLoss; + private double _lastValue; + private double _lastlastValue; public RSI_Series(TSeries source, int period = 10, bool useNaN = false) : base(source, period: period, useNaN: useNaN) { if (source.Count > 0) { base.Add(source); } } diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index e7f92739..4e5c2357 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -2,6 +2,7 @@ net7.0 + preview enable enable diff --git a/Tests/Validations/Skender_Stock.cs b/Tests/Validations/Skender_Stock.cs index 553e1209..13037492 100644 --- a/Tests/Validations/Skender_Stock.cs +++ b/Tests/Validations/Skender_Stock.cs @@ -99,6 +99,14 @@ public class Skender_Stock Assert.Equal(Math.Round((double)SK.Last().Atr!, 8), Math.Round(QL.Last().v, 8)); } + [Fact] + public void CCI() + { + CCI_Series QL = new(this.bars, this.period, false); + var SK = this.quotes.GetCci(this.period); + + Assert.Equal(Math.Round((double)SK.Last().Cci!, 8), Math.Round(QL.Last().v, 8)); + } [Fact] public void ATRP() diff --git a/Tests/Validations/TA_LIB.cs b/Tests/Validations/TA_LIB.cs index 32a78094..a464cd9b 100644 --- a/Tests/Validations/TA_LIB.cs +++ b/Tests/Validations/TA_LIB.cs @@ -102,6 +102,15 @@ public class TA_LIB Assert.Equal(Math.Round(this.TALIB[this.TALIB.Length - outBegIdx - 1], 8), Math.Round(QL.Last().v, 8)); } + [Fact] + public void CCI() + { + CCI_Series QL = new(this.bars, this.period, false); + Core.Cci(this.inhigh, this.inlow, this.inclose, 0, this.bars.Count - 1, this.TALIB, out int outBegIdx, out _, this.period); + + Assert.Equal(Math.Round(this.TALIB[this.TALIB.Length - outBegIdx - 1], 8), Math.Round(QL.Last().v, 8)); + } + [Fact] public void RSI() {