Skip to content

Commit

Permalink
Chaikin Money Flow Strategy is added.
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar committed Oct 16, 2024
1 parent 1cf5812 commit 91bb407
Show file tree
Hide file tree
Showing 7 changed files with 495 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ The following list of strategies are currently supported by this package:

### 📢 Volume Strategies

- Chaikin Money Flow Strategy
- [Chaikin Money Flow Strategy](strategy/volume/README.md#type-chaikinmoneyflowstrategy)
- Ease of Movement Strategy
- Force Index Strategy
- [Money Flow Index Strategy](strategy/volume/README.md#type-moneyflowindexstrategy)
Expand Down
63 changes: 63 additions & 0 deletions strategy/volume/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ The information provided on this project is strictly for informational purposes

- [Constants](<#constants>)
- [func AllStrategies\(\) \[\]strategy.Strategy](<#AllStrategies>)
- [type ChaikinMoneyFlowStrategy](<#ChaikinMoneyFlowStrategy>)
- [func NewChaikinMoneyFlowStrategy\(\) \*ChaikinMoneyFlowStrategy](<#NewChaikinMoneyFlowStrategy>)
- [func NewChaikinMoneyFlowStrategyWith\(period int\) \*ChaikinMoneyFlowStrategy](<#NewChaikinMoneyFlowStrategyWith>)
- [func \(c \*ChaikinMoneyFlowStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#ChaikinMoneyFlowStrategy.Compute>)
- [func \(c \*ChaikinMoneyFlowStrategy\) Name\(\) string](<#ChaikinMoneyFlowStrategy.Name>)
- [func \(c \*ChaikinMoneyFlowStrategy\) Report\(snapshots \<\-chan \*asset.Snapshot\) \*helper.Report](<#ChaikinMoneyFlowStrategy.Report>)
- [type MoneyFlowIndexStrategy](<#MoneyFlowIndexStrategy>)
- [func NewMoneyFlowIndexStrategy\(\) \*MoneyFlowIndexStrategy](<#NewMoneyFlowIndexStrategy>)
- [func NewMoneyFlowIndexStrategyWith\(sellAt, buyAt float64\) \*MoneyFlowIndexStrategy](<#NewMoneyFlowIndexStrategyWith>)
Expand Down Expand Up @@ -57,6 +63,63 @@ func AllStrategies() []strategy.Strategy

AllStrategies returns a slice containing references to all available volume strategies.

<a name="ChaikinMoneyFlowStrategy"></a>
## type [ChaikinMoneyFlowStrategy](<https://github.com/cinar/indicator/blob/master/strategy/volume/chaikin_money_flow_strategy.go#L18-L21>)

ChaikinMoneyFlowStrategy represents the configuration parameters for calculating the Chaikin Money Flow strategy. Recommends a Sell action when it crosses above 0, and recommends a Buy action when it crosses below 0.

```go
type ChaikinMoneyFlowStrategy struct {
// ChaikinMoneyFlow is the Chaikin Money Flow indicator instance.
ChaikinMoneyFlow *volume.Cmf[float64]
}
```

<a name="NewChaikinMoneyFlowStrategy"></a>
### func [NewChaikinMoneyFlowStrategy](<https://github.com/cinar/indicator/blob/master/strategy/volume/chaikin_money_flow_strategy.go#L25>)

```go
func NewChaikinMoneyFlowStrategy() *ChaikinMoneyFlowStrategy
```

NewChaikinMoneyFlowStrategy function initializes a new Money Flow Index strategy instance with the default parameters.

<a name="NewChaikinMoneyFlowStrategyWith"></a>
### func [NewChaikinMoneyFlowStrategyWith](<https://github.com/cinar/indicator/blob/master/strategy/volume/chaikin_money_flow_strategy.go#L33>)

```go
func NewChaikinMoneyFlowStrategyWith(period int) *ChaikinMoneyFlowStrategy
```

NewChaikinMoneyFlowStrategyWith function initializes a new Money Flow Index strategy instance with the given parameters.

<a name="ChaikinMoneyFlowStrategy.Compute"></a>
### func \(\*ChaikinMoneyFlowStrategy\) [Compute](<https://github.com/cinar/indicator/blob/master/strategy/volume/chaikin_money_flow_strategy.go#L45>)

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

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

<a name="ChaikinMoneyFlowStrategy.Name"></a>
### func \(\*ChaikinMoneyFlowStrategy\) [Name](<https://github.com/cinar/indicator/blob/master/strategy/volume/chaikin_money_flow_strategy.go#L40>)

```go
func (c *ChaikinMoneyFlowStrategy) Name() string
```

Name returns the name of the strategy.

<a name="ChaikinMoneyFlowStrategy.Report"></a>
### func \(\*ChaikinMoneyFlowStrategy\) [Report](<https://github.com/cinar/indicator/blob/master/strategy/volume/chaikin_money_flow_strategy.go#L74>)

```go
func (c *ChaikinMoneyFlowStrategy) Report(snapshots <-chan *asset.Snapshot) *helper.Report
```

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

<a name="MoneyFlowIndexStrategy"></a>
## type [MoneyFlowIndexStrategy](<https://github.com/cinar/indicator/blob/master/strategy/volume/money_flow_index_strategy.go#L26-L35>)

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

package volume

import (
"fmt"

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

// ChaikinMoneyFlowStrategy represents the configuration parameters for calculating the Chaikin Money Flow strategy.
// Recommends a Sell action when it crosses above 0, and recommends a Buy action when it crosses below 0.
type ChaikinMoneyFlowStrategy struct {
// ChaikinMoneyFlow is the Chaikin Money Flow indicator instance.
ChaikinMoneyFlow *volume.Cmf[float64]
}

// NewChaikinMoneyFlowStrategy function initializes a new Money Flow Index strategy instance with the
// default parameters.
func NewChaikinMoneyFlowStrategy() *ChaikinMoneyFlowStrategy {
return NewChaikinMoneyFlowStrategyWith(
volume.DefaultCmfPeriod,
)
}

// NewChaikinMoneyFlowStrategyWith function initializes a new Money Flow Index strategy instance with the
// given parameters.
func NewChaikinMoneyFlowStrategyWith(period int) *ChaikinMoneyFlowStrategy {
return &ChaikinMoneyFlowStrategy{
ChaikinMoneyFlow: volume.NewCmfWithPeriod[float64](period),
}
}

// Name returns the name of the strategy.
func (c *ChaikinMoneyFlowStrategy) Name() string {
return fmt.Sprintf("Chaikin Money Flow Strategy (%d)", c.ChaikinMoneyFlow.IdlePeriod()+1)
}

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

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

cmfs := c.ChaikinMoneyFlow.Compute(highs, lows, closings, volumes)

actions := helper.Map(cmfs, func(cmf float64) strategy.Action {
if cmf < 0 {
return strategy.Buy
}

if cmf > 0 {
return strategy.Sell
}

return strategy.Hold
})

// Chaikin Money Flow starts only after a full period.
actions = helper.Shift(actions, c.ChaikinMoneyFlow.IdlePeriod(), strategy.Hold)

return actions
}

// Report processes the provided asset snapshots and generates a report annotated with the recommended actions.
func (c *ChaikinMoneyFlowStrategy) Report(snapshots <-chan *asset.Snapshot) *helper.Report {
//
// snapshots[0] -> dates
// snapshots[1] -> highs |
// snapshots[2] -> lows |
// snapshots[3] -> closings[0] -> closings
// closings[1] -> chaikin money flow
// snapshots[4] -> volumes
// snapshots[5] -> actions -> annotations
// -> outcomes
//
snapshotsSplice := helper.Duplicate(snapshots, 6)

dates := helper.Skip(
asset.SnapshotsAsDates(snapshotsSplice[0]),
c.ChaikinMoneyFlow.IdlePeriod(),
)

highs := asset.SnapshotsAsHighs(snapshotsSplice[1])
lows := asset.SnapshotsAsLows(snapshotsSplice[2])
closingsSplice := helper.Duplicate(
asset.SnapshotsAsClosings(snapshotsSplice[3]),
2,
)
volumes := asset.SnapshotsAsVolumes(snapshotsSplice[4])

cmfs := c.ChaikinMoneyFlow.Compute(highs, lows, closingsSplice[0], volumes)
closingsSplice[1] = helper.Skip(closingsSplice[1], c.ChaikinMoneyFlow.IdlePeriod())

actions, outcomes := strategy.ComputeWithOutcome(c, snapshotsSplice[5])
actions = helper.Skip(actions, c.ChaikinMoneyFlow.IdlePeriod())
outcomes = helper.Skip(outcomes, c.ChaikinMoneyFlow.IdlePeriod())

annotations := strategy.ActionsToAnnotations(actions)
outcomes = helper.MultiplyBy(outcomes, 100)

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

report.AddColumn(helper.NewNumericReportColumn("Close", closingsSplice[1]))
report.AddColumn(helper.NewNumericReportColumn("Chaikin Money Flow", cmfs), 1)
report.AddColumn(helper.NewAnnotationReportColumn(annotations), 0, 1)

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

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

package volume_test

import (
"os"
"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/volume"
)

func TestChaikinMoneyFlowStrategy(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/chaikin_money_flow_strategy.csv", true)
if err != nil {
t.Fatal(err)
}

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

cmfs := volume.NewChaikinMoneyFlowStrategy()
actual := cmfs.Compute(snapshots)

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

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

cmfs := volume.NewChaikinMoneyFlowStrategy()
report := cmfs.Report(snapshots)

fileName := "chaikin_money_flow_strategy.html"
defer os.Remove(fileName)

err = report.WriteToFile(fileName)
if err != nil {
t.Fatal(err)
}
}
4 changes: 2 additions & 2 deletions strategy/volume/money_flow_index_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func NewMoneyFlowIndexStrategyWith(sellAt, buyAt float64) *MoneyFlowIndexStrateg

// Name returns the name of the strategy.
func (m *MoneyFlowIndexStrategy) Name() string {
return fmt.Sprintf("Money Flow Index Strategy (%f,%f)", m.SellAt, m.BuyAt)
return fmt.Sprintf("Money Flow Index Strategy (%.2f,%.2f)", m.SellAt, m.BuyAt)
}

// Compute processes the provided asset snapshots and generates a stream of actionable recommendations.
Expand Down Expand Up @@ -93,7 +93,7 @@ func (m *MoneyFlowIndexStrategy) Report(c <-chan *asset.Snapshot) *helper.Report
// snapshots[1] -> highs |
// snapshots[2] -> lows |
// snapshots[3] -> closings[0] -> closings
// closings[1] -> superTrend
// closings[1] -> money flow index
// snapshots[4] -> volumes
// snapshots[5] -> actions -> annotations
// -> outcomes
Expand Down
Loading

0 comments on commit 91bb407

Please sign in to comment.