Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Money Flow Index Strategy #234

Merged
merged 7 commits into from
Oct 15, 2024
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ The following list of strategies are currently supported by this package:
- Chande Forecast Oscillator Strategy
- [Community Channel Index (CCI) Strategy](strategy/trend/README.md#type-ccistrategy)
- [Double Exponential Moving Average (DEMA) Strategy](strategy/trend/README.md#type-demastrategy)
- [Envelope Strategy](strategy/trend/README.md#type-envelope)
- [Golden Cross Strategy](strategy/trend/README.md#type-goldencrossstrategy)
- [Kaufman's Adaptive Moving Average (KAMA) Strategy](strategy/trend/README.md#type-kamastrategy)
- [Moving Average Convergence Divergence (MACD) Strategy](strategy/trend/README.md#type-macdstrategy)
Expand Down Expand Up @@ -139,7 +140,7 @@ The following list of strategies are currently supported by this package:
- Chaikin Money Flow Strategy
- Ease of Movement Strategy
- Force Index Strategy
- Money Flow Index Strategy
- [Money Flow Index Strategy](strategy/volume/README.md#type-moneyflowindexstrategy)
- Negative Volume Index Strategy
- Volume Weighted Average Price Strategy

Expand Down
123 changes: 123 additions & 0 deletions strategy/volume/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->

# volume

```go
import "github.com/cinar/indicator/v2/strategy/volume"
```

Package volume contains the volume strategy functions.

This package belongs to the Indicator project. Indicator is a Golang module that supplies a variety of technical indicators, strategies, and a backtesting framework for analysis.

### License

```
Copyright (c) 2021-2024 Onur Cinar.
The source code is provided under GNU AGPLv3 License.
https://github.com/cinar/indicator
```

### Disclaimer

The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security.

## Index

- [Constants](<#constants>)
- [func AllStrategies\(\) \[\]strategy.Strategy](<#AllStrategies>)
- [type MoneyFlowIndexStrategy](<#MoneyFlowIndexStrategy>)
- [func NewMoneyFlowIndexStrategy\(\) \*MoneyFlowIndexStrategy](<#NewMoneyFlowIndexStrategy>)
- [func NewMoneyFlowIndexStrategyWith\(sellAt, buyAt float64\) \*MoneyFlowIndexStrategy](<#NewMoneyFlowIndexStrategyWith>)
- [func \(m \*MoneyFlowIndexStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#MoneyFlowIndexStrategy.Compute>)
- [func \(m \*MoneyFlowIndexStrategy\) Name\(\) string](<#MoneyFlowIndexStrategy.Name>)
- [func \(m \*MoneyFlowIndexStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#MoneyFlowIndexStrategy.Report>)


## Constants

<a name="DefaultMoneyFlowIndexStrategySellAt"></a>

```go
const (
// DefaultMoneyFlowIndexStrategySellAt is the default sell at of 80.
DefaultMoneyFlowIndexStrategySellAt = 80

// DefaultMoneyFlowIndexStrategyBuyAt is the default buy at of 20.
DefaultMoneyFlowIndexStrategyBuyAt = 20
)
```

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

```go
func AllStrategies() []strategy.Strategy
```

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

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

MoneyFlowIndexStrategy represents the configuration parameters for calculating the Money Flow Index strategy. Recommends a Sell action when it crosses over 80, and recommends a Buy action when it crosses below 20.

```go
type MoneyFlowIndexStrategy struct {
// MoneyFlowIndex is the Money Flow Index indicator instance.
MoneyFlowIndex *volume.Mfi[float64]

// SellAt is the sell at value.
SellAt float64

// BuyAt is the buy at value.
BuyAt float64
}
```

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

```go
func NewMoneyFlowIndexStrategy() *MoneyFlowIndexStrategy
```

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

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

```go
func NewMoneyFlowIndexStrategyWith(sellAt, buyAt float64) *MoneyFlowIndexStrategy
```

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

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

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

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

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

```go
func (m *MoneyFlowIndexStrategy) Name() string
```

Name returns the name of the strategy.

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

```go
func (m *MoneyFlowIndexStrategy) Report(c <-chan *asset.Snapshot) *helper.Report
```

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

Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)
137 changes: 137 additions & 0 deletions strategy/volume/money_flow_index_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// 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"
)

const (
// DefaultMoneyFlowIndexStrategySellAt is the default sell at of 80.
DefaultMoneyFlowIndexStrategySellAt = 80

// DefaultMoneyFlowIndexStrategyBuyAt is the default buy at of 20.
DefaultMoneyFlowIndexStrategyBuyAt = 20
)

// MoneyFlowIndexStrategy represents the configuration parameters for calculating the Money Flow Index strategy.
// Recommends a Sell action when it crosses over 80, and recommends a Buy action when it crosses below 20.
type MoneyFlowIndexStrategy struct {
// MoneyFlowIndex is the Money Flow Index indicator instance.
MoneyFlowIndex *volume.Mfi[float64]

// SellAt is the sell at value.
SellAt float64

// BuyAt is the buy at value.
BuyAt float64
}

// NewMoneyFlowIndexStrategy function initializes a new Money Flow Index strategy instance with the default parameters.
func NewMoneyFlowIndexStrategy() *MoneyFlowIndexStrategy {
return NewMoneyFlowIndexStrategyWith(
DefaultMoneyFlowIndexStrategySellAt,
DefaultMoneyFlowIndexStrategyBuyAt,
)
}

// NewMoneyFlowIndexStrategyWith function initializes a new Money Flow Index strategy instance with the
// given parameters.
func NewMoneyFlowIndexStrategyWith(sellAt, buyAt float64) *MoneyFlowIndexStrategy {
return &MoneyFlowIndexStrategy{
MoneyFlowIndex: volume.NewMfi[float64](),
SellAt: sellAt,
BuyAt: buyAt,
}
}
cinar marked this conversation as resolved.
Show resolved Hide resolved

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

// Compute processes the provided asset snapshots and generates a stream of actionable recommendations.
func (m *MoneyFlowIndexStrategy) 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])

mfis := m.MoneyFlowIndex.Compute(highs, lows, closings, volumes)

actions := helper.Map(mfis, func(mfi float64) strategy.Action {
if mfi >= m.SellAt {
return strategy.Sell
}

if mfi <= m.BuyAt {
return strategy.Buy
}

return strategy.Hold
})

// Money Flow Index starts only after a full period.
actions = helper.Shift(actions, m.MoneyFlowIndex.IdlePeriod(), strategy.Hold)

return actions
}

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

dates := helper.Skip(
asset.SnapshotsAsDates(snapshots[0]),
m.MoneyFlowIndex.IdlePeriod(),
)

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

mfis := m.MoneyFlowIndex.Compute(highs, lows, closingsSplice[0], volumes)
closingsSplice[1] = helper.Skip(closingsSplice[1], m.MoneyFlowIndex.IdlePeriod())

actions, outcomes := strategy.ComputeWithOutcome(m, snapshots[5])
actions = helper.Skip(actions, m.MoneyFlowIndex.IdlePeriod())
outcomes = helper.Skip(outcomes, m.MoneyFlowIndex.IdlePeriod())

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

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

report.AddColumn(helper.NewNumericReportColumn("Close", closingsSplice[1]))
report.AddColumn(helper.NewNumericReportColumn("Money Flow Index", mfis), 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/money_flow_index_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 TestMoneyFlowIndexStrategy(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/money_flow_index_strategy.csv", true)
if err != nil {
t.Fatal(err)
}

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

mfis := volume.NewMoneyFlowIndexStrategy()
actual := mfis.Compute(snapshots)

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

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

mfis := volume.NewMoneyFlowIndexStrategy()
report := mfis.Report(snapshots)

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

err = report.WriteToFile(fileName)
if err != nil {
t.Fatal(err)
}
}
cinar marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions strategy/volume/testdata/brk-b.csv
Loading
Loading