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

Ease of Movement Strategy is added. #241

Merged
merged 2 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ The following list of strategies are currently supported by this package:
### 📢 Volume Strategies

- [Chaikin Money Flow Strategy](strategy/volume/README.md#type-chaikinmoneyflowstrategy)
- Ease of Movement Strategy
- [Ease of Movement Strategy](strategy/volume/README.md#type-easeofmovementstrategy)
- [Force Index Strategy](strategy/volume/README.md#type-forceindexstrategy)
- [Money Flow Index Strategy](strategy/volume/README.md#type-moneyflowindexstrategy)
- [Negative Volume Index Strategy](strategy/volume/README.md#type-negativevolumeindexstrategy)
Expand Down
63 changes: 63 additions & 0 deletions strategy/volume/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ The information provided on this project is strictly for informational purposes
- [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 EaseOfMovementStrategy](<#EaseOfMovementStrategy>)
- [func NewEaseOfMovementStrategy\(\) \*EaseOfMovementStrategy](<#NewEaseOfMovementStrategy>)
- [func NewEaseOfMovementStrategyWith\(period int\) \*EaseOfMovementStrategy](<#NewEaseOfMovementStrategyWith>)
- [func \(e \*EaseOfMovementStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#EaseOfMovementStrategy.Compute>)
- [func \(e \*EaseOfMovementStrategy\) Name\(\) string](<#EaseOfMovementStrategy.Name>)
- [func \(e \*EaseOfMovementStrategy\) Report\(snapshots \<\-chan \*asset.Snapshot\) \*helper.Report](<#EaseOfMovementStrategy.Report>)
- [type ForceIndexStrategy](<#ForceIndexStrategy>)
- [func NewForceIndexStrategy\(\) \*ForceIndexStrategy](<#NewForceIndexStrategy>)
- [func NewForceIndexStrategyWith\(period int\) \*ForceIndexStrategy](<#NewForceIndexStrategyWith>)
Expand Down Expand Up @@ -147,6 +153,63 @@ func (c *ChaikinMoneyFlowStrategy) Report(snapshots <-chan *asset.Snapshot) *hel

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

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

EaseOfMovementStrategy represents the configuration parameters for calculating the Ease of Movement strategy. Recommends a Buy action when it crosses above 0, and recommends a Sell action when it crosses below 0.

```go
type EaseOfMovementStrategy struct {
// EaseOfMovement is the Ease of Movement indicator instance.
EaseOfMovement *volume.Emv[float64]
}
```

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

```go
func NewEaseOfMovementStrategy() *EaseOfMovementStrategy
```

NewEaseOfMovementStrategy function initializes a new Ease of Movement strategy instance with the default parameters.

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

```go
func NewEaseOfMovementStrategyWith(period int) *EaseOfMovementStrategy
```

NewEaseOfMovementStrategyWith function initializes a new Ease of Movement strategy instance with the given parameters.

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

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

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

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

```go
func (e *EaseOfMovementStrategy) Name() string
```

Name function returns the name of the strategy.

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

```go
func (e *EaseOfMovementStrategy) Report(snapshots <-chan *asset.Snapshot) *helper.Report
```

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

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

Expand Down
119 changes: 119 additions & 0 deletions strategy/volume/ease_of_movement_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// 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"
)

// EaseOfMovementStrategy represents the configuration parameters for calculating the Ease of Movement strategy.
// Recommends a Buy action when it crosses above 0, and recommends a Sell action when it crosses below 0.
type EaseOfMovementStrategy struct {
// EaseOfMovement is the Ease of Movement indicator instance.
EaseOfMovement *volume.Emv[float64]
}

// NewEaseOfMovementStrategy function initializes a new Ease of Movement strategy instance with the
// default parameters.
func NewEaseOfMovementStrategy() *EaseOfMovementStrategy {
return NewEaseOfMovementStrategyWith(
volume.DefaultEmvPeriod,
)
}

// NewEaseOfMovementStrategyWith function initializes a new Ease of Movement strategy instance with the
// given parameters.
func NewEaseOfMovementStrategyWith(period int) *EaseOfMovementStrategy {
return &EaseOfMovementStrategy{
EaseOfMovement: volume.NewEmvWithPeriod[float64](period),
}
}

// Name function returns the name of the strategy.
func (e *EaseOfMovementStrategy) Name() string {
return fmt.Sprintf("Ease of Movement Strategy (%d)", e.EaseOfMovement.IdlePeriod()+1)
}

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

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

emvs := e.EaseOfMovement.Compute(highs, lows, volumes)

actions := helper.Map(emvs, func(emv float64) strategy.Action {
if emv > 0 {
return strategy.Buy
}

if emv < 0 {
return strategy.Sell
}

return strategy.Hold
})
cinar marked this conversation as resolved.
Show resolved Hide resolved

// Ease of Movement starts only after a full period.
actions = helper.Shift(actions, e.EaseOfMovement.IdlePeriod(), strategy.Hold)

return actions
}

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

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

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

closings := helper.Skip(
asset.SnapshotsAsClosings(snapshotsSplice[4]),
e.EaseOfMovement.IdlePeriod(),
)

emvs := e.EaseOfMovement.Compute(highs, lows, volumes)

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

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

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

report.AddColumn(helper.NewNumericReportColumn("Close", closings))
report.AddColumn(helper.NewNumericReportColumn("Ease of Movement", emvs), 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/ease_of_movement_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 TestEaseOfMovementStrategy(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/ease_of_movement_strategy.csv", true)
if err != nil {
t.Fatal(err)
}

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

emvs := volume.NewEaseOfMovementStrategy()
actual := emvs.Compute(snapshots)

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

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

emvs := volume.NewEaseOfMovementStrategy()
report := emvs.Report(snapshots)

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

err = report.WriteToFile(fileName)
if err != nil {
t.Fatal(err)
}
}
Loading
Loading