Skip to content

Commit

Permalink
Money Flow Index Strategy added.
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar committed Oct 15, 2024
1 parent 2b2c79a commit 9a79f90
Show file tree
Hide file tree
Showing 7 changed files with 598 additions and 1 deletion.
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,
}
}

// 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)
}
}
1 change: 1 addition & 0 deletions strategy/volume/testdata/brk-b.csv
Loading

0 comments on commit 9a79f90

Please sign in to comment.