diff --git a/README.md b/README.md
index cee2d2e..64ab278 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ The following list of indicators are currently supported by this package:
- [Aroon Indicator](trend/README.md#type-aroon)
- [Balance of Power (BoP)](trend/README.md#type-bop)
- Chande Forecast Oscillator (CFO)
-- Community Channel Index (CMI)
+- [Community Channel Index (CCI)](trend/README.md#type-cci)
- [Double Exponential Moving Average (DEMA)](trend/README.md#type-dema)
- [Exponential Moving Average (EMA)](trend/README.md#type-ema)
- [Mass Index (MI)](trend/README.md#type-massindex)
diff --git a/trend/README.md b/trend/README.md
index c5fc161..286ea38 100644
--- a/trend/README.md
+++ b/trend/README.md
@@ -34,6 +34,11 @@ The information provided on this project is strictly for informational purposes
- [type Bop](<#Bop>)
- [func NewBop\[T helper.Number\]\(\) \*Bop\[T\]](<#NewBop>)
- [func \(\*Bop\[T\]\) Compute\(opening, high, low, closing \<\-chan T\) \<\-chan T](<#Bop[T].Compute>)
+- [type Cci](<#Cci>)
+ - [func NewCci\[T helper.Number\]\(\) \*Cci\[T\]](<#NewCci>)
+ - [func NewCciWithPeriod\[T helper.Number\]\(period int\) \*Cci\[T\]](<#NewCciWithPeriod>)
+ - [func \(c \*Cci\[T\]\) Compute\(highs, lows, closings \<\-chan T\) \<\-chan T](<#Cci[T].Compute>)
+ - [func \(c \*Cci\[T\]\) IdlePeriod\(\) int](<#Cci[T].IdlePeriod>)
- [type Dema](<#Dema>)
- [func NewDema\[T helper.Number\]\(\) \*Dema\[T\]](<#NewDema>)
- [func \(d \*Dema\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Dema[T].Compute>)
@@ -68,6 +73,7 @@ The information provided on this project is strictly for informational purposes
- [func \(rma \*Rma\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Rma[T].Compute>)
- [type Sma](<#Sma>)
- [func NewSma\[T helper.Number\]\(\) \*Sma\[T\]](<#NewSma>)
+ - [func NewSmaWithPeriod\[T helper.Number\]\(period int\) \*Sma\[T\]](<#NewSmaWithPeriod>)
- [func \(s \*Sma\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Sma[T].Compute>)
- [type Tema](<#Tema>)
- [func NewTema\[T helper.Number\]\(\) \*Tema\[T\]](<#NewTema>)
@@ -177,6 +183,15 @@ const (
)
```
+
+
+```go
+const (
+ // DefaultCciPeriod is the default time period for CCI.
+ DefaultCciPeriod = 20
+)
+```
+
```go
@@ -353,6 +368,68 @@ func (*Bop[T]) Compute(opening, high, low, closing <-chan T) <-chan T
Compute processes a channel of open, high, low, and close values, computing the BOP for each entry.
+
+## type [Cci]()
+
+Cci represents the configuration parameters for calculating the Community Channel Index \(CCI\). CCI is a momentum\-based oscillator used to help determine when an investment vehicle is reaching a condition of being overbought or oversold.
+
+```
+Moving Average = Sma(Period, Typical Price)
+Mean Deviation = Sma(Period, Abs(Typical Price - Moving Average))
+CCI = (Typical Price - Moving Average) / (0.015 * Mean Deviation)
+```
+
+Example:
+
+```
+cmi := trend.NewCmi()
+cmi.Period = 20
+values = cmi.Compute(highs, lows, closings)
+```
+
+```go
+type Cci[T helper.Number] struct {
+ // Time period.
+ Period int
+}
+```
+
+
+### func [NewCci]()
+
+```go
+func NewCci[T helper.Number]() *Cci[T]
+```
+
+NewCci function initializes a new CCI instance with the default parameters.
+
+
+### func [NewCciWithPeriod]()
+
+```go
+func NewCciWithPeriod[T helper.Number](period int) *Cci[T]
+```
+
+NewCciWithPeriod function initializes a new CCI instance with the given period.
+
+
+### func \(\*Cci\[T\]\) [Compute]()
+
+```go
+func (c *Cci[T]) Compute(highs, lows, closings <-chan T) <-chan T
+```
+
+Compute function takes a channel of numbers and computes the CCI and the signal line.
+
+
+### func \(\*Cci\[T\]\) [IdlePeriod]()
+
+```go
+func (c *Cci[T]) IdlePeriod() int
+```
+
+IdlePeriod is the initial period that CCI won't yield any results.
+
## type [Dema]()
@@ -765,7 +842,7 @@ func (rma *Rma[T]) Compute(c <-chan T) <-chan T
Compute function takes a channel of numbers and computes the RMA over the specified period.
-## type [Sma]()
+## type [Sma]()
Sma represents the parameters for calculating the Simple Moving Average.
@@ -786,7 +863,7 @@ type Sma[T helper.Number] struct {
```
-### func [NewSma]()
+### func [NewSma]()
```go
func NewSma[T helper.Number]() *Sma[T]
@@ -794,8 +871,17 @@ func NewSma[T helper.Number]() *Sma[T]
NewSma function initializes a new SMA instance with the default parameters.
+
+### func [NewSmaWithPeriod]()
+
+```go
+func NewSmaWithPeriod[T helper.Number](period int) *Sma[T]
+```
+
+NewSmaWithPeriod function initializes a new SMA instance with the default parameters.
+
-### func \(\*Sma\[T\]\) [Compute]()
+### func \(\*Sma\[T\]\) [Compute]()
```go
func (s *Sma[T]) Compute(c <-chan T) <-chan T
diff --git a/trend/cci.go b/trend/cci.go
new file mode 100644
index 0000000..eb3ea0c
--- /dev/null
+++ b/trend/cci.go
@@ -0,0 +1,94 @@
+// Copyright (c) 2021-2023 Onur Cinar.
+// The source code is provided under GNU AGPLv3 License.
+// https://github.com/cinar/indicator
+
+package trend
+
+import (
+ "github.com/cinar/indicator/helper"
+)
+
+const (
+ // DefaultCciPeriod is the default time period for CCI.
+ DefaultCciPeriod = 20
+)
+
+// Cci represents the configuration parameters for calculating the Community Channel Index (CCI). CCI is a
+// momentum-based oscillator used to help determine when an investment vehicle is reaching a condition of
+// being overbought or oversold.
+//
+// Moving Average = Sma(Period, Typical Price)
+// Mean Deviation = Sma(Period, Abs(Typical Price - Moving Average))
+// CCI = (Typical Price - Moving Average) / (0.015 * Mean Deviation)
+//
+// Example:
+//
+// cmi := trend.NewCmi()
+// cmi.Period = 20
+// values = cmi.Compute(highs, lows, closings)
+type Cci[T helper.Number] struct {
+ // Time period.
+ Period int
+}
+
+// NewCci function initializes a new CCI instance with the default parameters.
+func NewCci[T helper.Number]() *Cci[T] {
+ return &Cci[T]{
+ Period: DefaultCciPeriod,
+ }
+}
+
+// NewCciWithPeriod function initializes a new CCI instance with the given period.
+func NewCciWithPeriod[T helper.Number](period int) *Cci[T] {
+ return &Cci[T]{
+ Period: period,
+ }
+}
+
+// Compute function takes a channel of numbers and computes the CCI and the signal line.
+func (c *Cci[T]) Compute(highs, lows, closings <-chan T) <-chan T {
+ typicalPrice := NewTypicalPrice[T]()
+ sma1 := NewSmaWithPeriod[T](c.Period)
+ sma2 := NewSmaWithPeriod[T](c.Period)
+
+ tps := helper.Duplicate[T](
+ typicalPrice.Compute(highs, lows, closings),
+ 3,
+ )
+
+ mas := helper.Duplicate[T](
+ sma1.Compute(tps[0]),
+ 2,
+ )
+
+ tps[1] = helper.Skip(tps[1], sma1.Period-1)
+ tps[2] = helper.Skip(tps[2], sma1.Period-1)
+
+ md := sma2.Compute(
+ helper.Abs(
+ helper.Subtract(tps[1], mas[0]),
+ ),
+ )
+
+ mas[1] = helper.Skip(mas[1], sma2.Period-1)
+ tps[2] = helper.Skip(tps[2], sma2.Period-1)
+
+ multiplier := 0.015
+
+ cci := helper.Divide(
+ helper.Subtract(
+ tps[2], mas[1],
+ ),
+ helper.MultiplyBy[T](
+ md,
+ T(multiplier),
+ ),
+ )
+
+ return cci
+}
+
+// IdlePeriod is the initial period that CCI won't yield any results.
+func (c *Cci[T]) IdlePeriod() int {
+ return (c.Period * 2) - 2
+}
diff --git a/trend/cci_test.go b/trend/cci_test.go
new file mode 100644
index 0000000..6f5a80d
--- /dev/null
+++ b/trend/cci_test.go
@@ -0,0 +1,44 @@
+// Copyright (c) 2021-2023 Onur Cinar.
+// The source code is provided under GNU AGPLv3 License.
+// https://github.com/cinar/indicator
+
+package trend_test
+
+import (
+ "testing"
+
+ "github.com/cinar/indicator/helper"
+ "github.com/cinar/indicator/trend"
+)
+
+func TestCci(t *testing.T) {
+ type Data struct {
+ High float64
+ Low float64
+ Close float64
+ Cci float64
+ }
+
+ input, err := helper.ReadFromCsvFile[Data]("testdata/cci.csv", true)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ inputs := helper.Duplicate(input, 4)
+ high := helper.Map(inputs[0], func(d *Data) float64 { return d.High })
+ low := helper.Map(inputs[1], func(d *Data) float64 { return d.Low })
+ closing := helper.Map(inputs[2], func(d *Data) float64 { return d.Close })
+ expected := helper.Map(inputs[3], func(d *Data) float64 { return d.Cci })
+
+ cci := trend.NewCci[float64]()
+
+ actual := cci.Compute(high, low, closing)
+ actual = helper.RoundDigits(actual, 2)
+
+ expected = helper.Skip(expected, cci.IdlePeriod())
+
+ err = helper.CheckEquals(actual, expected)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/trend/sma.go b/trend/sma.go
index 9abe0e5..dbacfa3 100644
--- a/trend/sma.go
+++ b/trend/sma.go
@@ -11,8 +11,7 @@ const (
DefaultSmaPeriod = 50
)
-// Sma represents the parameters for calculating
-// the Simple Moving Average.
+// Sma represents the parameters for calculating the Simple Moving Average.
//
// Example:
//
@@ -25,16 +24,22 @@ type Sma[T helper.Number] struct {
Period int
}
-// NewSma function initializes a new SMA instance
-// with the default parameters.
+// NewSma function initializes a new SMA instance with the default parameters.
func NewSma[T helper.Number]() *Sma[T] {
return &Sma[T]{
Period: DefaultSmaPeriod,
}
}
-// Compute function takes a channel of numbers and computes the SMA
-// over the specified period.
+// NewSmaWithPeriod function initializes a new SMA instance with the default parameters.
+func NewSmaWithPeriod[T helper.Number](period int) *Sma[T] {
+ sma := NewSma[T]()
+ sma.Period = period
+
+ return sma
+}
+
+// Compute function takes a channel of numbers and computes the SMA over the specified period.
func (s *Sma[T]) Compute(c <-chan T) <-chan T {
sum := NewMovingSum[T]()
sum.Period = s.Period
diff --git a/trend/sma_test.go b/trend/sma_test.go
index da70d0f..7082021 100644
--- a/trend/sma_test.go
+++ b/trend/sma_test.go
@@ -29,8 +29,7 @@ func TestSma(t *testing.T) {
23.43, 23.28, 23.13,
}
- sma := trend.NewSma[float64]()
- sma.Period = 10
+ sma := trend.NewSmaWithPeriod[float64](10)
actual := helper.ChanToSlice(helper.RoundDigits(sma.Compute(input), 2))
diff --git a/trend/testdata/brk-b.csv b/trend/testdata/brk-b.csv
deleted file mode 120000
index db88857..0000000
--- a/trend/testdata/brk-b.csv
+++ /dev/null
@@ -1 +0,0 @@
-../../asset/testdata/repository/brk-b.csv
\ No newline at end of file
diff --git a/trend/testdata/cci.csv b/trend/testdata/cci.csv
new file mode 100644
index 0000000..9933635
--- /dev/null
+++ b/trend/testdata/cci.csv
@@ -0,0 +1,252 @@
+High,Low,Close,Cci
+318.600006,308.700012,318.600006,0
+319.559998,313.299988,315.839996,0
+316.380005,312.75,316.149994,0
+315.660004,308.730011,310.570007,0
+310.290009,306.350006,307.779999,0
+309.380005,304.920013,305.820007,0
+307.48999,305.089996,305.98999,0
+308.339996,304.709991,306.390015,0
+311.910004,305.459991,311.450012,0
+318.910004,310.820007,312.329987,0
+316.359985,308.399994,309.290009,0
+306.959991,299.450012,301.910004,0
+302.470001,297.76001,300,0
+301.480011,297.149994,300.029999,0
+304.190002,297,302,0
+308.540009,304.160004,307.820007,0
+306.5,297.640015,302.690002,0
+306.570007,300.929993,306.48999,0
+308.579987,304.649994,305.549988,0
+307.459991,303.26001,303.429993,0
+309.380005,305.23999,309.059998,0
+309.040009,305.619995,308.899994,0
+312.390015,307.380005,309.910004,0
+316.890015,311.25,314.549988,0
+314.230011,310,312.899994,0
+320.160004,313.380005,318.690002,0
+320.5,314.75,315.529999,0
+316.799988,313.339996,316.350006,0
+320.570007,316.600006,320.369995,0
+321.320007,317.720001,318.929993,0
+318.420013,315.790009,317.640015,0
+318.519989,314.25,314.859985,0
+315.540009,307.75,308.299988,0
+307.23999,303.859985,305.230011,0
+310.01001,304.359985,309.869995,0
+312.730011,306.850006,310.420013,0
+312.829987,307.5,311.299988,0
+312.549988,307.709991,311.899994,0
+313.679993,309.579987,310.950012,-11.92
+311.730011,308.339996,309.170013,-38.49
+309.51001,306.809998,307.329987,-61.97
+311.859985,305.790009,311.519989,-37.93
+312.670013,306.380005,310.570007,-36.22
+312.600006,308.299988,311.859985,-21.45
+311.549988,305.920013,308.51001,-51.91
+308.799988,305.600006,308.429993,-64.16
+314.149994,306.630005,312.970001,-3.92
+313.410004,308.01001,308.480011,-23.05
+311.420013,306.98999,307.209991,-45.01
+309.980011,305.279999,309.890015,-42.54
+313.73999,309.619995,313.73999,67.77
+314.100006,309.040009,310.790009,49.53
+310.369995,308.279999,309.630005,-4.93
+310.200012,306.869995,308.179993,-43.22
+308.410004,305.480011,308.23999,-77.37
+307.299988,300.5,302.720001,-175.12
+305.269989,301.769989,303.160004,-152.43
+305.559998,300.25,303.070007,-141.22
+305.619995,300.01001,304.019989,-115.68
+305.779999,302.01001,304.660004,-86.13
+306.149994,303.410004,305.179993,-67.44
+305.619995,302.079987,304.619995,-79
+308.100006,301.450012,307.75,-36.55
+312.660004,308.5,312.450012,87.99
+317.290009,312.429993,316.970001,166.9
+316.5,310.230011,311.119995,98.44
+312.679993,309.25,311.369995,63.82
+313.179993,303.940002,304.820007,-9.5
+306.720001,301.920013,303.630005,-68.92
+306.589996,300.76001,302.880005,-75.07
+307.549988,301.679993,305.329987,-40.3
+300.549988,294.899994,297.880005,-149.4
+304.429993,295.359985,302.01001,-86.15
+301.299988,292.420013,293.51001,-141.64
+301.51001,295.059998,301.059998,-80.57
+305.630005,302.25,303.850006,-13.55
+307.049988,299.649994,299.730011,-41.06
+302.079987,296.299988,298.369995,-89.09
+299.5,293.390015,298.920013,-108
+303.209991,298.970001,302.140015,-41.77
+302.720001,300.589996,302.320007,-32.84
+305.380005,303.359985,305.299988,11.58
+307.470001,302.579987,305.079987,18.23
+308.809998,304.98999,308.769989,62.24
+311.5,308.23999,310.309998,109.82
+311,307.070007,309.070007,95.27
+311.070007,307.850006,310.390015,104.02
+313.220001,309.049988,312.51001,118.74
+313.700012,310.329987,312.619995,115.72
+315.940002,311.769989,313.700012,123.39
+316.920013,313.720001,314.549988,123.39
+318.809998,313.26001,318.049988,129.03
+321.880005,318.119995,319.73999,145.39
+323.980011,319,323.790009,150.78
+325.720001,322.5,324.630005,147.97
+324.549988,322.76001,323.089996,119.3
+324.369995,321.320007,323.820007,100.73
+324.850006,321.609985,324.329987,90.7
+326.399994,324.299988,326.049988,92.8
+327.100006,324.109985,324.339996,76.65
+323.73999,319,320.529999,37.09
+326.910004,322.109985,326.230011,57.31
+328.809998,325.190002,328.549988,64.03
+331.839996,328.570007,330.170013,72.18
+330.25,322.76001,325.859985,40.52
+328.070007,323.059998,323.220001,25.29
+325.98999,317.410004,320,-3.61
+325.160004,322.619995,323.880005,11.86
+330.690002,325.790009,326.140015,33.62
+326.880005,323.480011,324.869995,11.58
+326.160004,320.149994,322.98999,-7.36
+322.959991,319.809998,322.640015,-21.26
+324.23999,320.540009,322.48999,-18.29
+323.829987,320.130005,323.529999,-19.38
+324.690002,322.359985,323.75,-8.65
+328.26001,324.820007,327.390015,29.29
+329.980011,325.850006,329.76001,51.51
+333.940002,329.119995,330.390015,86.83
+331.48999,328.350006,329.130005,67.53
+329.269989,322.970001,323.109985,-4.41
+323,319.559998,320.200012,-79.37
+320.559998,317.709991,319.019989,-109.43
+322.630005,319.670013,320.600006,-74.64
+322.470001,319,322.190002,-68.52
+322.410004,319.390015,321.079987,-71.93
+323.220001,319.529999,323.119995,-46.6
+330.670013,324.420013,329.480011,90.7
+330.890015,327.570007,328.579987,98.75
+334.160004,328.679993,333.410004,153.53
+335.820007,331.429993,335.420013,168.63
+336.320007,334.100006,335.950012,160.42
+337.589996,334.920013,335.290009,144.07
+335.350006,332.220001,333.600006,96.52
+336.619995,332.200012,336.390015,100.89
+340.380005,334.089996,335.899994,106.94
+341.679993,335.540009,339.820007,118.64
+341.299988,337.660004,338.309998,107.73
+339.279999,336.619995,338.670013,91.86
+341.350006,336.369995,338.609985,90.18
+338.850006,335.660004,336.959991,63.52
+337.470001,334.190002,335.25,40.78
+335.829987,331.839996,334.119995,16.43
+336.730011,334.369995,335.339996,25.38
+336.399994,332.609985,334.149994,6.88
+337.01001,334.140015,336.910004,16.64
+342.5,338.399994,341,55.57
+342.079987,338.410004,342,50.59
+341.890015,338.700012,341.559998,43.33
+341.799988,338.910004,341.459991,39.98
+344.070007,340.390015,340.899994,49.45
+343.480011,339.869995,341.130005,44.93
+343.839996,340.929993,343.369995,58.11
+346.440002,344.309998,345.350006,84.23
+346.209991,343.450012,343.540009,67.52
+345,340.51001,341.089996,37.78
+345.720001,341.089996,344.25,58.45
+347.25,343.540009,345.339996,82.19
+345.380005,341.98999,342.429993,49.21
+346.790009,342.850006,346.609985,82.29
+347.619995,345.100006,345.76001,88.51
+351.190002,346.279999,349.630005,121.17
+349.660004,345.540009,347.579987,82.44
+351.089996,347.519989,349.799988,96.42
+351.269989,348.600006,349.309998,83.76
+351,348.320007,349.809998,71.01
+352.329987,350.209991,351.959991,86.94
+353.420013,351.25,352.26001,88.41
+352.890015,349.690002,351.190002,66.8
+354.470001,349.420013,353.809998,74.61
+355.109985,349.390015,349.98999,54.68
+364.630005,355.149994,362.579987,148.86
+364.25,358.850006,363.730011,144.06
+364.429993,356.059998,358.019989,103.28
+362.350006,355.920013,356.980011,82.36
+359.25,353.200012,358.350006,57.72
+358.950012,356.809998,358.480011,61.16
+357.920013,353.670013,354.5,29.15
+358.720001,353.380005,354.109985,23.43
+356.299988,351.880005,353.190002,2.39
+354.299988,351.25,352.559998,-13.29
+354.179993,349.609985,352.089996,-24.28
+353.5,349.660004,350.570007,-35.46
+354.320007,351.540009,354.26001,-13.07
+357.230011,354.130005,354.299988,6.91
+357.350006,352.920013,355.929993,5.81
+358.410004,354.529999,355.549988,13.92
+358.589996,354.01001,358.290009,23.55
+362.679993,358.600006,361.059998,75.58
+362.470001,359.25,360.200012,68.32
+363.390015,360.600006,362.459991,82.77
+366.470001,360,360.470001,91.9
+362.799988,359.26001,361.670013,83.9
+363.299988,360.869995,361.799988,102.73
+364.829987,361.769989,363.149994,127.32
+366.609985,364.51001,365.519989,161.12
+370.429993,365.470001,367.779999,188.45
+370.839996,365.970001,367.820007,166.25
+370.220001,368.26001,369.5,157.84
+370.200012,367.519989,367.859985,121.34
+371.329987,367.790009,370.429993,118.35
+373.339996,368.459991,370.480011,110.5
+371.339996,366.730011,366.820007,66.93
+367.200012,362.940002,363.279999,12.68
+363.420013,359.76001,360.160004,-31.98
+361.890015,357.269989,361.709991,-43.66
+360.790009,357.950012,359.420013,-54.38
+360.519989,354.269989,357.779999,-72.78
+359.470001,356.670013,357.059998,-68.01
+357.5,348.549988,350.299988,-117.3
+350,345.410004,348.079987,-143.08
+348.23999,342.130005,343.040009,-153.27
+344.01001,339.51001,343.690002,-149.13
+345.940002,342.369995,345.059998,-118.09
+348.76001,341.859985,346.339996,-98.26
+345.899994,342.829987,345.450012,-94.55
+349.51001,345.5,348.559998,-65.78
+349.600006,344.920013,348.429993,-60.29
+348.660004,343.019989,345.660004,-65.35
+348.440002,343.880005,345.089996,-57.22
+349.940002,345.829987,346.230011,-39.16
+348.410004,344.149994,345.390015,-40.65
+344.829987,339.959991,340.890015,-59.93
+342.690002,338.450012,338.660004,-62.5
+340,334.350006,335.859985,-72.96
+338.880005,333.48999,336.839996,-65.18
+339.850006,337.769989,338.630005,-43.13
+339.619995,336.549988,336.899994,-43.51
+338.320007,335.459991,336.160004,-43.33
+336.190002,330.579987,331.709991,-61.88
+338.359985,332.179993,337.410004,-39.73
+341.48999,337.5,341.329987,-11.75
+345.329987,340.579987,343.75,11.55
+349.390015,344.5,349.019989,48.42
+354.350006,349.790009,351.809998,84.73
+354.029999,344.059998,346.630005,53.31
+346.950012,344.299988,346.170013,33.3
+348,344.690002,346.299988,40.22
+350.109985,346.880005,348.179993,60.73
+351.200012,348.600006,350.559998,76.38
+350.649994,348.809998,350.01001,71.21
+355.950012,351.25,354.25,103.35
+357.309998,354.480011,356.790009,116.01
+360,357.230011,359.859985,128.97
+360.559998,358.070007,358.929993,118.42
+362.609985,358.179993,361.329987,118.45
+363.029999,360.25,361,110.25
+362.459991,360.049988,361.799988,97.26
+363.190002,361.23999,362.679993,91.73
+362.640015,359.579987,361.339996,71.52
+362.119995,359.209991,360.049988,55.71
+361.519989,358.299988,358.690002,39.74