Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
409 changes: 5 additions & 404 deletions README.md

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions strategy/momentum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ The information provided on this project is strictly for informational purposes
- [func \(a \*AwesomeOscillatorStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#AwesomeOscillatorStrategy.Compute>)
- [func \(\*AwesomeOscillatorStrategy\) Name\(\) string](<#AwesomeOscillatorStrategy.Name>)
- [func \(a \*AwesomeOscillatorStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#AwesomeOscillatorStrategy.Report>)
- [type IchimokuCloudStrategy](<#IchimokuCloudStrategy>)
- [func NewIchimokuCloudStrategy\(\) \*IchimokuCloudStrategy](<#NewIchimokuCloudStrategy>)
- [func \(i \*IchimokuCloudStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#IchimokuCloudStrategy.Compute>)
- [func \(\*IchimokuCloudStrategy\) Name\(\) string](<#IchimokuCloudStrategy.Name>)
- [func \(i \*IchimokuCloudStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#IchimokuCloudStrategy.Report>)
- [type RsiStrategy](<#RsiStrategy>)
- [func NewRsiStrategy\(\) \*RsiStrategy](<#NewRsiStrategy>)
- [func NewRsiStrategyWith\(buyAt, sellAt float64\) \*RsiStrategy](<#NewRsiStrategyWith>)
Expand Down Expand Up @@ -159,6 +164,54 @@ func (a *AwesomeOscillatorStrategy) Report(c <-chan *asset.Snapshot) *helper.Rep

Report processes the provided asset snapshots and generates a report annotated with the recommended actions.

<a name="IchimokuCloudStrategy"></a>
## type [IchimokuCloudStrategy](<https://github.com/cinar/indicator/blob/master/strategy/momentum/ichimoku_cloud_strategy.go#L15-L18>)

IchimokuCloudStrategy represents the configuration parameters for calculating the Ichimoku Cloud strategy.

```go
type IchimokuCloudStrategy struct {
// IchimokuCloud represents the configuration parameters for calculating the Ichimoku Cloud.
IchimokuCloud *momentum.IchimokuCloud[float64]
}
```

<a name="NewIchimokuCloudStrategy"></a>
### func [NewIchimokuCloudStrategy](<https://github.com/cinar/indicator/blob/master/strategy/momentum/ichimoku_cloud_strategy.go#L21>)

```go
func NewIchimokuCloudStrategy() *IchimokuCloudStrategy
```

NewIchimokuCloudStrategy function initializes a new Ichimoku Cloud strategy with the default parameters.

<a name="IchimokuCloudStrategy.Compute"></a>
### func \(\*IchimokuCloudStrategy\) [Compute](<https://github.com/cinar/indicator/blob/master/strategy/momentum/ichimoku_cloud_strategy.go#L33>)

```go
func (i *IchimokuCloudStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action
```

Compute processes the provided asset snapshots and generates a stream of actionable recommendations.

<a name="IchimokuCloudStrategy.Name"></a>
### func \(\*IchimokuCloudStrategy\) [Name](<https://github.com/cinar/indicator/blob/master/strategy/momentum/ichimoku_cloud_strategy.go#L28>)

```go
func (*IchimokuCloudStrategy) Name() string
```

Name returns the name of the strategy.

<a name="IchimokuCloudStrategy.Report"></a>
### func \(\*IchimokuCloudStrategy\) [Report](<https://github.com/cinar/indicator/blob/master/strategy/momentum/ichimoku_cloud_strategy.go#L71>)

```go
func (i *IchimokuCloudStrategy) Report(c <-chan *asset.Snapshot) *helper.Report
```

Report processes the provided asset snapshots and generates a report annotated with the recommended actions.

<a name="RsiStrategy"></a>
## type [RsiStrategy](<https://github.com/cinar/indicator/blob/master/strategy/momentum/rsi_strategy.go#L25-L34>)

Expand Down
107 changes: 107 additions & 0 deletions strategy/momentum/ichimoku_cloud_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) 2021-2026 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator

package momentum

import (
"github.com/cinar/indicator/v2/asset"
"github.com/cinar/indicator/v2/helper"
"github.com/cinar/indicator/v2/momentum"
"github.com/cinar/indicator/v2/strategy"
)

// IchimokuCloudStrategy represents the configuration parameters for calculating the Ichimoku Cloud strategy.
type IchimokuCloudStrategy struct {
// IchimokuCloud represents the configuration parameters for calculating the Ichimoku Cloud.
IchimokuCloud *momentum.IchimokuCloud[float64]
}

// NewIchimokuCloudStrategy function initializes a new Ichimoku Cloud strategy with the default parameters.
func NewIchimokuCloudStrategy() *IchimokuCloudStrategy {
return &IchimokuCloudStrategy{
IchimokuCloud: momentum.NewIchimokuCloud[float64](),
}
}

// Name returns the name of the strategy.
func (*IchimokuCloudStrategy) Name() string {
return "Ichimoku Cloud Strategy"
}

// Compute processes the provided asset snapshots and generates a stream of actionable recommendations.
func (i *IchimokuCloudStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action {
snapshotsSplice := helper.Duplicate(snapshots, 3)

highs := asset.SnapshotsAsHighs(snapshotsSplice[0])
lows := asset.SnapshotsAsLows(snapshotsSplice[1])
closings := asset.SnapshotsAsClosings(snapshotsSplice[2])

closingsSplice := helper.Duplicate(closings, 2)

cl, bl, lsa, lsb, ll := i.IchimokuCloud.Compute(highs, lows, closingsSplice[0])

// Lagging line is not used in the core logic, drain it to prevent blocking
go helper.Drain(ll)

actions := helper.Operate5(
helper.Skip(closingsSplice[1], i.IchimokuCloud.IdlePeriod()),
cl,
bl,
lsa,
lsb,
func(c, conversion, base, spanA, spanB float64) strategy.Action {
if c > spanA && c > spanB && conversion > base && spanA > spanB {
return strategy.Buy
}

if c < spanA && c < spanB && conversion < base && spanA < spanB {
return strategy.Sell
}

return strategy.Hold
},
)

// Shift the actions to account for the idle period
return helper.Shift(actions, i.IchimokuCloud.IdlePeriod(), strategy.Hold)
}

// Report processes the provided asset snapshots and generates a report annotated with the recommended actions.
func (i *IchimokuCloudStrategy) Report(c <-chan *asset.Snapshot) *helper.Report {
snapshots := helper.Duplicate(c, 6)

dates := asset.SnapshotsAsDates(snapshots[0])
closings := asset.SnapshotsAsClosings(snapshots[2])
highs := asset.SnapshotsAsHighs(snapshots[3])
lows := asset.SnapshotsAsLows(snapshots[4])
closingsForCloud := asset.SnapshotsAsClosings(snapshots[5])

cl, bl, lsa, lsb, ll := i.IchimokuCloud.Compute(highs, lows, closingsForCloud)

// Lagging line is not used in the report right now, drain it.
go helper.Drain(ll)

clShifted := helper.Shift(cl, i.IchimokuCloud.IdlePeriod(), 0)
blShifted := helper.Shift(bl, i.IchimokuCloud.IdlePeriod(), 0)
lsaShifted := helper.Shift(lsa, i.IchimokuCloud.IdlePeriod(), 0)
lsbShifted := helper.Shift(lsb, i.IchimokuCloud.IdlePeriod(), 0)

actions, outcomes := strategy.ComputeWithOutcome(i, snapshots[1])
annotations := strategy.ActionsToAnnotations(actions)
outcomes = helper.MultiplyBy(outcomes, 100)

report := helper.NewReport(i.Name(), dates)
report.AddChart()

report.AddColumn(helper.NewNumericReportColumn("Close", closings))
report.AddColumn(helper.NewNumericReportColumn("Conversion Line", clShifted))
report.AddColumn(helper.NewNumericReportColumn("Base Line", blShifted))
report.AddColumn(helper.NewNumericReportColumn("Leading Span A", lsaShifted))
report.AddColumn(helper.NewNumericReportColumn("Leading Span B", lsbShifted))
report.AddColumn(helper.NewAnnotationReportColumn(annotations), 0, 1)

report.AddColumn(helper.NewNumericReportColumn("Outcome", outcomes), 1)

return report
}
55 changes: 55 additions & 0 deletions strategy/momentum/ichimoku_cloud_strategy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2021-2026 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator

package momentum_test

import (
"testing"

"github.com/cinar/indicator/v2/asset"
"github.com/cinar/indicator/v2/helper"
"github.com/cinar/indicator/v2/strategy"
"github.com/cinar/indicator/v2/strategy/momentum"
)

func TestIchimokuCloudStrategy(t *testing.T) {
snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv")
if err != nil {
t.Fatal(err)
}

results, err := helper.ReadFromCsvFile[strategy.Result]("testdata/ichimoku_cloud_strategy.csv")
if err != nil {
t.Fatal(err)
}

expected := helper.Map(results, func(r *strategy.Result) strategy.Action { return r.Action })

ic := momentum.NewIchimokuCloudStrategy()
actual := ic.Compute(snapshots)

err = helper.CheckEquals(actual, expected)
if err != nil {
t.Fatal(err)
}
}

func TestIchimokuCloudStrategyReport(t *testing.T) {
snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv")
if err != nil {
t.Fatal(err)
}

ic := momentum.NewIchimokuCloudStrategy()

report := ic.Report(snapshots)

fileName := "ichimoku_cloud_strategy.html"
defer helper.Remove(t, fileName)

err = report.WriteToFile(fileName)
if err != nil {
t.Fatal(err)
}
}
1 change: 1 addition & 0 deletions strategy/momentum/momentum.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import "github.com/cinar/indicator/v2/strategy"
func AllStrategies() []strategy.Strategy {
return []strategy.Strategy{
NewAwesomeOscillatorStrategy(),
NewIchimokuCloudStrategy(),
NewRsiStrategy(),
NewStochasticRsiStrategy(),
NewTripleRsiStrategy(),
Expand Down
Loading
Loading