From 6ce0871886941b664b34d5975f0b55098f3230a2 Mon Sep 17 00:00:00 2001 From: Onur Cinar Date: Fri, 29 Dec 2023 16:50:01 -0800 Subject: [PATCH] Community Channel Index (CCI). --- README.md | 2 +- trend/README.md | 92 +++++++++++++- trend/cci.go | 94 +++++++++++++++ trend/cci_test.go | 44 +++++++ trend/sma.go | 17 ++- trend/sma_test.go | 3 +- trend/testdata/brk-b.csv | 1 - trend/testdata/cci.csv | 252 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 492 insertions(+), 13 deletions(-) create mode 100644 trend/cci.go create mode 100644 trend/cci_test.go delete mode 120000 trend/testdata/brk-b.csv create mode 100644 trend/testdata/cci.csv 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