Skip to content

Commit

Permalink
Community Channel Index (CCI).
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar committed Dec 30, 2023
1 parent 53c4214 commit 6ce0871
Show file tree
Hide file tree
Showing 8 changed files with 492 additions and 13 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
92 changes: 89 additions & 3 deletions trend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>)
Expand Down Expand Up @@ -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>)
Expand Down Expand Up @@ -177,6 +183,15 @@ const (
)
```

<a name="DefaultCciPeriod"></a>

```go
const (
// DefaultCciPeriod is the default time period for CCI.
DefaultCciPeriod = 20
)
```

<a name="DefaultRmaPeriod"></a>

```go
Expand Down Expand Up @@ -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.

<a name="Cci"></a>
## type [Cci](<https://github.com/cinar/indicator/blob/v2/trend/cci.go#L29-L32>)

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
}
```

<a name="NewCci"></a>
### func [NewCci](<https://github.com/cinar/indicator/blob/v2/trend/cci.go#L35>)

```go
func NewCci[T helper.Number]() *Cci[T]
```

NewCci function initializes a new CCI instance with the default parameters.

<a name="NewCciWithPeriod"></a>
### func [NewCciWithPeriod](<https://github.com/cinar/indicator/blob/v2/trend/cci.go#L42>)

```go
func NewCciWithPeriod[T helper.Number](period int) *Cci[T]
```

NewCciWithPeriod function initializes a new CCI instance with the given period.

<a name="Cci[T].Compute"></a>
### func \(\*Cci\[T\]\) [Compute](<https://github.com/cinar/indicator/blob/v2/trend/cci.go#L49>)

```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.

<a name="Cci[T].IdlePeriod"></a>
### func \(\*Cci\[T\]\) [IdlePeriod](<https://github.com/cinar/indicator/blob/v2/trend/cci.go#L92>)

```go
func (c *Cci[T]) IdlePeriod() int
```

IdlePeriod is the initial period that CCI won't yield any results.

<a name="Dema"></a>
## type [Dema](<https://github.com/cinar/indicator/blob/v2/trend/dema.go#L22-L30>)

Expand Down Expand Up @@ -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.

<a name="Sma"></a>
## type [Sma](<https://github.com/cinar/indicator/blob/v2/trend/sma.go#L23-L26>)
## type [Sma](<https://github.com/cinar/indicator/blob/v2/trend/sma.go#L22-L25>)

Sma represents the parameters for calculating the Simple Moving Average.

Expand All @@ -786,16 +863,25 @@ type Sma[T helper.Number] struct {
```

<a name="NewSma"></a>
### func [NewSma](<https://github.com/cinar/indicator/blob/v2/trend/sma.go#L30>)
### func [NewSma](<https://github.com/cinar/indicator/blob/v2/trend/sma.go#L28>)

```go
func NewSma[T helper.Number]() *Sma[T]
```

NewSma function initializes a new SMA instance with the default parameters.

<a name="NewSmaWithPeriod"></a>
### func [NewSmaWithPeriod](<https://github.com/cinar/indicator/blob/v2/trend/sma.go#L35>)

```go
func NewSmaWithPeriod[T helper.Number](period int) *Sma[T]
```

NewSmaWithPeriod function initializes a new SMA instance with the default parameters.

<a name="Sma[T].Compute"></a>
### func \(\*Sma\[T\]\) [Compute](<https://github.com/cinar/indicator/blob/v2/trend/sma.go#L38>)
### func \(\*Sma\[T\]\) [Compute](<https://github.com/cinar/indicator/blob/v2/trend/sma.go#L43>)

```go
func (s *Sma[T]) Compute(c <-chan T) <-chan T
Expand Down
94 changes: 94 additions & 0 deletions trend/cci.go
Original file line number Diff line number Diff line change
@@ -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
}
44 changes: 44 additions & 0 deletions trend/cci_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
17 changes: 11 additions & 6 deletions trend/sma.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
//
Expand All @@ -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
Expand Down
3 changes: 1 addition & 2 deletions trend/sma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
1 change: 0 additions & 1 deletion trend/testdata/brk-b.csv

This file was deleted.

Loading

0 comments on commit 6ce0871

Please sign in to comment.