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()
{