diff --git a/README.md b/README.md index 64ab278..55c6687 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ The following list of strategies are currently supported by this package: - [Balance of Power (BoP) Strategy](strategy/trend/README.md#type-bopstrategy) - [Double Exponential Moving Average (DEMA) Strategy](strategy/trend/README.md#type-demastrategy) - Chande Forecast Oscillator Strategy +- [Community Channel Index (CCI) Strategy](strategy/trend/README.md#type-ccistrategy) - [Random Index (KDJ) Strategy](strategy/trend/README.md#type-kdjstrategy) - [Moving Average Convergence Divergence (MACD) Strategy](strategy/trend/README.md#type-macdstrategy) - [Qstick Strategy](strategy/trend/README.md#type-qstickstrategy) diff --git a/strategy/trend/README.md b/strategy/trend/README.md index d228b3d..92220b1 100644 --- a/strategy/trend/README.md +++ b/strategy/trend/README.md @@ -41,6 +41,11 @@ The information provided on this project is strictly for informational purposes - [func \(b \*BopStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#BopStrategy.Compute>) - [func \(\*BopStrategy\) Name\(\) string](<#BopStrategy.Name>) - [func \(b \*BopStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#BopStrategy.Report>) +- [type CciStrategy](<#CciStrategy>) + - [func NewCciStrategy\(\) \*CciStrategy](<#NewCciStrategy>) + - [func \(t \*CciStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#CciStrategy.Compute>) + - [func \(\*CciStrategy\) Name\(\) string](<#CciStrategy.Name>) + - [func \(t \*CciStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#CciStrategy.Report>) - [type DemaStrategy](<#DemaStrategy>) - [func NewDemaStrategy\(\) \*DemaStrategy](<#NewDemaStrategy>) - [func \(d \*DemaStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#DemaStrategy.Compute>) @@ -274,6 +279,56 @@ func (b *BopStrategy) Report(c <-chan *asset.Snapshot) *helper.Report Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + +## type [CciStrategy]() + +CciStrategy represents the configuration parameters for calculating the CCI strategy. A CCI value crossing above the 100\+ suggests a bullish trend, while crossing below the 100\- indicates a bearish trend. + +```go +type CciStrategy struct { + strategy.Strategy + + // Cci represents the configuration parameters for calculating the CCI. + Cci *trend.Cci[float64] +} +``` + + +### func [NewCciStrategy]() + +```go +func NewCciStrategy() *CciStrategy +``` + +NewCciStrategy function initializes a new CCI strategy instance. + + +### func \(\*CciStrategy\) [Compute]() + +```go +func (t *CciStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*CciStrategy\) [Name]() + +```go +func (*CciStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*CciStrategy\) [Report]() + +```go +func (t *CciStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + ## type [DemaStrategy]() diff --git a/strategy/trend/cci_strategy.go b/strategy/trend/cci_strategy.go new file mode 100644 index 0000000..f4f67ed --- /dev/null +++ b/strategy/trend/cci_strategy.go @@ -0,0 +1,101 @@ +// 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/asset" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/trend" +) + +// CciStrategy represents the configuration parameters for calculating the CCI strategy. +// A CCI value crossing above the 100+ suggests a bullish trend, while crossing below +// the 100- indicates a bearish trend. +type CciStrategy struct { + strategy.Strategy + + // Cci represents the configuration parameters for calculating the CCI. + Cci *trend.Cci[float64] +} + +// NewCciStrategy function initializes a new CCI strategy instance. +func NewCciStrategy() *CciStrategy { + return &CciStrategy{ + Cci: trend.NewCci[float64](), + } +} + +// Name returns the name of the strategy. +func (*CciStrategy) Name() string { + return "CCI Strategy" +} + +// Compute processes the provided asset snapshots and generates a stream of actionable recommendations. +func (t *CciStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action { + snapshots := helper.Duplicate(c, 3) + highs := asset.SnapshotsAsHighs(snapshots[0]) + lows := asset.SnapshotsAsHighs(snapshots[1]) + closings := asset.SnapshotsAsHighs(snapshots[2]) + + ccis := t.Cci.Compute(highs, lows, closings) + + actions := helper.Map(ccis, func(cci float64) strategy.Action { + if cci >= 100 { + return strategy.Buy + } + + if cci <= -100 { + return strategy.Sell + } + + return strategy.Hold + }) + + actions = strategy.NormalizeActions(actions) + + // CCI starts only after a full period. + actions = helper.Shift(actions, t.Cci.IdlePeriod(), strategy.Hold) + + return actions +} + +// Report processes the provided asset snapshots and generates a report annotated with the recommended actions. +func (t *CciStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { + // + // snapshots[0] -> dates + // snapshots[1] -> highs | + // snapshots[2] -> lows | + // snapshots[3] -> closings[1] |> ccis + // closings[0] -> closings + // snapshots[4] -> actions -> annotations + // -> outcomes + // + snapshots := helper.Duplicate(c, 5) + + dates := asset.SnapshotsAsDates(snapshots[0]) + highs := asset.SnapshotsAsHighs(snapshots[1]) + lows := asset.SnapshotsAsHighs(snapshots[2]) + closings := helper.Duplicate(asset.SnapshotsAsHighs(snapshots[3]), 2) + + ccis := t.Cci.Compute(highs, lows, closings[1]) + ccis = helper.Shift(ccis, t.Cci.IdlePeriod(), 0) + + actions, outcomes := strategy.ComputeWithOutcome(t, snapshots[4]) + annotations := strategy.ActionsToAnnotations(actions) + outcomes = helper.MultiplyBy(outcomes, 100) + + report := helper.NewReport(t.Name(), dates) + report.AddChart() + report.AddChart() + + report.AddColumn(helper.NewNumericReportColumn("Close", closings[0])) + report.AddColumn(helper.NewNumericReportColumn("CCI", ccis), 1) + report.AddColumn(helper.NewAnnotationReportColumn(annotations), 0, 1) + + report.AddColumn(helper.NewNumericReportColumn("Outcome", outcomes), 2) + + return report +} diff --git a/strategy/trend/cci_strategy_test.go b/strategy/trend/cci_strategy_test.go new file mode 100644 index 0000000..041a3b5 --- /dev/null +++ b/strategy/trend/cci_strategy_test.go @@ -0,0 +1,55 @@ +// Copyright (c) 2021-2023 Onur Cinar. +// The source code is provided under GNU AGPLv3 License. +// https://github.com/cinar/indicator + +package trend_test + +import ( + "os" + "testing" + + "github.com/cinar/indicator/asset" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" +) + +func TestCciStrategy(t *testing.T) { + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) + if err != nil { + t.Fatal(err) + } + + results, err := helper.ReadFromCsvFile[strategy.Result]("testdata/cci_strategy.csv", true) + if err != nil { + t.Fatal(err) + } + + cci := trend.NewCciStrategy() + actions, outcomes := strategy.ComputeWithOutcome(cci, snapshots) + outcomes = helper.RoundDigits(outcomes, 2) + + err = strategy.CheckResults(results, actions, outcomes) + if err != nil { + t.Fatal(err) + } +} + +func TestCciStrategyReport(t *testing.T) { + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) + if err != nil { + t.Fatal(err) + } + + cci := trend.NewCciStrategy() + + report := cci.Report(snapshots) + + fileName := "cci_strategy.html" + defer os.Remove(fileName) + + err = report.WriteToFile(fileName) + if err != nil { + t.Fatal(err) + } +} diff --git a/strategy/trend/testdata/cci_strategy.csv b/strategy/trend/testdata/cci_strategy.csv new file mode 100644 index 0000000..a7148b3 --- /dev/null +++ b/strategy/trend/testdata/cci_strategy.csv @@ -0,0 +1,252 @@ +Action,Outcome +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +1,0 +0,-0.02 +0,-0.02 +0,-0.04 +0,-0.04 +0,-0.04 +0,-0.04 +-1,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +0,-0.06 +1,-0.06 +0,-0.06 +0,-0.06 +0,-0.05 +0,-0.04 +0,-0.04 +0,-0.03 +0,-0.02 +0,-0.03 +0,-0.03 +0,-0.02 +0,-0.02 +0,-0.02 +0,-0.04 +0,-0.02 +0,-0.01 +0,-0.01 +0,-0.02 +0,-0.03 +0,-0.04 +0,-0.03 +0,-0.02 +0,-0.02 +0,-0.03 +0,-0.03 +0,-0.03 +0,-0.03 +0,-0.03 +0,-0.02 +0,-0.01 +0,-0.01 +0,-0.01 +0,-0.03 +0,-0.04 +-1,-0.04 +0,-0.04 +0,-0.04 +0,-0.04 +0,-0.04 +0,-0.04 +0,-0.04 +1,-0.04 +0,-0.03 +0,-0.03 +0,-0.04 +0,-0.04 +0,-0.03 +0,-0.03 +0,-0.02 +0,-0.03 +0,-0.03 +0,-0.03 +0,-0.03 +0,-0.04 +0,-0.04 +0,-0.04 +0,-0.04 +0,-0.03 +0,-0.02 +0,-0.02 +0,-0.02 +0,-0.02 +0,-0.02 +0,-0.02 +0,-0.01 +0,-0.01 +0,-0.01 +0,-0.02 +0,-0.01 +0,-0.01 +0,-0.01 +0,-0 +0,-0.01 +0,0.01 +0,0 +0,0.01 +0,0.01 +0,0.01 +0,0.01 +0,0.01 +0,0.01 +0,0.02 +0,0.01 +0,0.04 +0,0.05 +0,0.03 +0,0.03 +0,0.03 +0,0.03 +0,0.02 +0,0.02 +0,0.02 +0,0.01 +0,0.01 +0,0.01 +0,0.02 +0,0.02 +0,0.02 +0,0.02 +0,0.03 +0,0.04 +0,0.04 +0,0.04 +0,0.04 +0,0.04 +0,0.04 +0,0.04 +0,0.05 +0,0.06 +0,0.06 +0,0.06 +0,0.06 +0,0.07 +0,0.07 +0,0.06 +0,0.05 +0,0.04 +0,0.04 +0,0.03 +0,0.03 +0,0.03 +0,0.01 +-1,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +0,0 +1,0 +0,0.01 +0,0.02 +0,0.01 +0,0.02 +0,0.02 +0,0.02 +0,0.03 +0,0.02 +0,0.02 +0,0.01