Skip to content

Commit 7a4b44b

Browse files
authored
Ease of Movement Strategy is added. (#241)
# Describe Request Ease of Movement Strategy is added. # Change Type New strategy. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced the `EaseOfMovementStrategy`, providing buy and sell recommendations based on market data. - Enhanced the documentation with a comprehensive overview of version 2 features, including improved configurability and backtesting capabilities. - Expanded and reorganized the lists of indicators and strategies. - **Documentation** - Updated the `README.md` to clarify installation, usage, contributing guidelines, and licensing information. - Added a section detailing repository implementations and synchronization functionality. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent b63416f commit 7a4b44b

File tree

6 files changed

+491
-1
lines changed

6 files changed

+491
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ The following list of strategies are currently supported by this package:
142142
### 📢 Volume Strategies
143143

144144
- [Chaikin Money Flow Strategy](strategy/volume/README.md#type-chaikinmoneyflowstrategy)
145-
- Ease of Movement Strategy
145+
- [Ease of Movement Strategy](strategy/volume/README.md#type-easeofmovementstrategy)
146146
- [Force Index Strategy](strategy/volume/README.md#type-forceindexstrategy)
147147
- [Money Flow Index Strategy](strategy/volume/README.md#type-moneyflowindexstrategy)
148148
- [Negative Volume Index Strategy](strategy/volume/README.md#type-negativevolumeindexstrategy)

strategy/volume/README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ The information provided on this project is strictly for informational purposes
3232
- [func \(c \*ChaikinMoneyFlowStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#ChaikinMoneyFlowStrategy.Compute>)
3333
- [func \(c \*ChaikinMoneyFlowStrategy\) Name\(\) string](<#ChaikinMoneyFlowStrategy.Name>)
3434
- [func \(c \*ChaikinMoneyFlowStrategy\) Report\(snapshots \<\-chan \*asset.Snapshot\) \*helper.Report](<#ChaikinMoneyFlowStrategy.Report>)
35+
- [type EaseOfMovementStrategy](<#EaseOfMovementStrategy>)
36+
- [func NewEaseOfMovementStrategy\(\) \*EaseOfMovementStrategy](<#NewEaseOfMovementStrategy>)
37+
- [func NewEaseOfMovementStrategyWith\(period int\) \*EaseOfMovementStrategy](<#NewEaseOfMovementStrategyWith>)
38+
- [func \(e \*EaseOfMovementStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#EaseOfMovementStrategy.Compute>)
39+
- [func \(e \*EaseOfMovementStrategy\) Name\(\) string](<#EaseOfMovementStrategy.Name>)
40+
- [func \(e \*EaseOfMovementStrategy\) Report\(snapshots \<\-chan \*asset.Snapshot\) \*helper.Report](<#EaseOfMovementStrategy.Report>)
3541
- [type ForceIndexStrategy](<#ForceIndexStrategy>)
3642
- [func NewForceIndexStrategy\(\) \*ForceIndexStrategy](<#NewForceIndexStrategy>)
3743
- [func NewForceIndexStrategyWith\(period int\) \*ForceIndexStrategy](<#NewForceIndexStrategyWith>)
@@ -147,6 +153,63 @@ func (c *ChaikinMoneyFlowStrategy) Report(snapshots <-chan *asset.Snapshot) *hel
147153

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

156+
<a name="EaseOfMovementStrategy"></a>
157+
## type [EaseOfMovementStrategy](<https://github.com/cinar/indicator/blob/master/strategy/volume/ease_of_movement_strategy.go#L18-L21>)
158+
159+
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.
160+
161+
```go
162+
type EaseOfMovementStrategy struct {
163+
// EaseOfMovement is the Ease of Movement indicator instance.
164+
EaseOfMovement *volume.Emv[float64]
165+
}
166+
```
167+
168+
<a name="NewEaseOfMovementStrategy"></a>
169+
### func [NewEaseOfMovementStrategy](<https://github.com/cinar/indicator/blob/master/strategy/volume/ease_of_movement_strategy.go#L25>)
170+
171+
```go
172+
func NewEaseOfMovementStrategy() *EaseOfMovementStrategy
173+
```
174+
175+
NewEaseOfMovementStrategy function initializes a new Ease of Movement strategy instance with the default parameters.
176+
177+
<a name="NewEaseOfMovementStrategyWith"></a>
178+
### func [NewEaseOfMovementStrategyWith](<https://github.com/cinar/indicator/blob/master/strategy/volume/ease_of_movement_strategy.go#L33>)
179+
180+
```go
181+
func NewEaseOfMovementStrategyWith(period int) *EaseOfMovementStrategy
182+
```
183+
184+
NewEaseOfMovementStrategyWith function initializes a new Ease of Movement strategy instance with the given parameters.
185+
186+
<a name="EaseOfMovementStrategy.Compute"></a>
187+
### func \(\*EaseOfMovementStrategy\) [Compute](<https://github.com/cinar/indicator/blob/master/strategy/volume/ease_of_movement_strategy.go#L45>)
188+
189+
```go
190+
func (e *EaseOfMovementStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action
191+
```
192+
193+
Compute function processes the provided asset snapshots and generates a stream of actionable recommendations.
194+
195+
<a name="EaseOfMovementStrategy.Name"></a>
196+
### func \(\*EaseOfMovementStrategy\) [Name](<https://github.com/cinar/indicator/blob/master/strategy/volume/ease_of_movement_strategy.go#L40>)
197+
198+
```go
199+
func (e *EaseOfMovementStrategy) Name() string
200+
```
201+
202+
Name function returns the name of the strategy.
203+
204+
<a name="EaseOfMovementStrategy.Report"></a>
205+
### func \(\*EaseOfMovementStrategy\) [Report](<https://github.com/cinar/indicator/blob/master/strategy/volume/ease_of_movement_strategy.go#L73>)
206+
207+
```go
208+
func (e *EaseOfMovementStrategy) Report(snapshots <-chan *asset.Snapshot) *helper.Report
209+
```
210+
211+
Report function processes the provided asset snapshots and generates a report annotated with the recommended actions.
212+
150213
<a name="ForceIndexStrategy"></a>
151214
## type [ForceIndexStrategy](<https://github.com/cinar/indicator/blob/master/strategy/volume/force_index_strategy.go#L18-L21>)
152215

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright (c) 2021-2024 Onur Cinar.
2+
// The source code is provided under GNU AGPLv3 License.
3+
// https://github.com/cinar/indicator
4+
5+
package volume
6+
7+
import (
8+
"fmt"
9+
10+
"github.com/cinar/indicator/v2/asset"
11+
"github.com/cinar/indicator/v2/helper"
12+
"github.com/cinar/indicator/v2/strategy"
13+
"github.com/cinar/indicator/v2/volume"
14+
)
15+
16+
// EaseOfMovementStrategy represents the configuration parameters for calculating the Ease of Movement strategy.
17+
// Recommends a Buy action when it crosses above 0, and recommends a Sell action when it crosses below 0.
18+
type EaseOfMovementStrategy struct {
19+
// EaseOfMovement is the Ease of Movement indicator instance.
20+
EaseOfMovement *volume.Emv[float64]
21+
}
22+
23+
// NewEaseOfMovementStrategy function initializes a new Ease of Movement strategy instance with the
24+
// default parameters.
25+
func NewEaseOfMovementStrategy() *EaseOfMovementStrategy {
26+
return NewEaseOfMovementStrategyWith(
27+
volume.DefaultEmvPeriod,
28+
)
29+
}
30+
31+
// NewEaseOfMovementStrategyWith function initializes a new Ease of Movement strategy instance with the
32+
// given parameters.
33+
func NewEaseOfMovementStrategyWith(period int) *EaseOfMovementStrategy {
34+
return &EaseOfMovementStrategy{
35+
EaseOfMovement: volume.NewEmvWithPeriod[float64](period),
36+
}
37+
}
38+
39+
// Name function returns the name of the strategy.
40+
func (e *EaseOfMovementStrategy) Name() string {
41+
return fmt.Sprintf("Ease of Movement Strategy (%d)", e.EaseOfMovement.IdlePeriod()+1)
42+
}
43+
44+
// Compute function processes the provided asset snapshots and generates a stream of actionable recommendations.
45+
func (e *EaseOfMovementStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action {
46+
snapshotsSplice := helper.Duplicate(snapshots, 3)
47+
48+
highs := asset.SnapshotsAsHighs(snapshotsSplice[0])
49+
lows := asset.SnapshotsAsLows(snapshotsSplice[1])
50+
volumes := asset.SnapshotsAsVolumes(snapshotsSplice[2])
51+
52+
emvs := e.EaseOfMovement.Compute(highs, lows, volumes)
53+
54+
actions := helper.Map(emvs, func(emv float64) strategy.Action {
55+
if emv > 0 {
56+
return strategy.Buy
57+
}
58+
59+
if emv < 0 {
60+
return strategy.Sell
61+
}
62+
63+
return strategy.Hold
64+
})
65+
66+
// Ease of Movement starts only after a full period.
67+
actions = helper.Shift(actions, e.EaseOfMovement.IdlePeriod(), strategy.Hold)
68+
69+
return actions
70+
}
71+
72+
// Report function processes the provided asset snapshots and generates a report annotated with the recommended actions.
73+
func (e *EaseOfMovementStrategy) Report(snapshots <-chan *asset.Snapshot) *helper.Report {
74+
//
75+
// snapshots[0] -> dates
76+
// snapshots[1] -> highs |
77+
// snapshots[2] -> lows |
78+
// snapshots[3] -> volumes -> emv
79+
// snapshots[4] -> closings
80+
// snapshots[5] -> actions -> annotations
81+
// -> outcomes
82+
//
83+
snapshotsSplice := helper.Duplicate(snapshots, 6)
84+
85+
dates := helper.Skip(
86+
asset.SnapshotsAsDates(snapshotsSplice[0]),
87+
e.EaseOfMovement.IdlePeriod(),
88+
)
89+
90+
highs := asset.SnapshotsAsHighs(snapshotsSplice[1])
91+
lows := asset.SnapshotsAsLows(snapshotsSplice[2])
92+
volumes := asset.SnapshotsAsVolumes(snapshotsSplice[3])
93+
94+
closings := helper.Skip(
95+
asset.SnapshotsAsClosings(snapshotsSplice[4]),
96+
e.EaseOfMovement.IdlePeriod(),
97+
)
98+
99+
emvs := e.EaseOfMovement.Compute(highs, lows, volumes)
100+
101+
actions, outcomes := strategy.ComputeWithOutcome(e, snapshotsSplice[5])
102+
actions = helper.Skip(actions, e.EaseOfMovement.IdlePeriod())
103+
outcomes = helper.Skip(outcomes, e.EaseOfMovement.IdlePeriod())
104+
105+
annotations := strategy.ActionsToAnnotations(actions)
106+
outcomes = helper.MultiplyBy(outcomes, 100)
107+
108+
report := helper.NewReport(e.Name(), dates)
109+
report.AddChart()
110+
report.AddChart()
111+
112+
report.AddColumn(helper.NewNumericReportColumn("Close", closings))
113+
report.AddColumn(helper.NewNumericReportColumn("Ease of Movement", emvs), 1)
114+
report.AddColumn(helper.NewAnnotationReportColumn(annotations), 0, 1)
115+
116+
report.AddColumn(helper.NewNumericReportColumn("Outcome", outcomes), 2)
117+
118+
return report
119+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) 2021-2024 Onur Cinar.
2+
// The source code is provided under GNU AGPLv3 License.
3+
// https://github.com/cinar/indicator
4+
5+
package volume_test
6+
7+
import (
8+
"os"
9+
"testing"
10+
11+
"github.com/cinar/indicator/v2/asset"
12+
"github.com/cinar/indicator/v2/helper"
13+
"github.com/cinar/indicator/v2/strategy"
14+
"github.com/cinar/indicator/v2/strategy/volume"
15+
)
16+
17+
func TestEaseOfMovementStrategy(t *testing.T) {
18+
snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true)
19+
if err != nil {
20+
t.Fatal(err)
21+
}
22+
23+
results, err := helper.ReadFromCsvFile[strategy.Result]("testdata/ease_of_movement_strategy.csv", true)
24+
if err != nil {
25+
t.Fatal(err)
26+
}
27+
28+
expected := helper.Map(results, func(r *strategy.Result) strategy.Action { return r.Action })
29+
30+
emvs := volume.NewEaseOfMovementStrategy()
31+
actual := emvs.Compute(snapshots)
32+
33+
err = helper.CheckEquals(actual, expected)
34+
if err != nil {
35+
t.Fatal(err)
36+
}
37+
}
38+
39+
func TestEaseOfMovementStrategyReport(t *testing.T) {
40+
snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true)
41+
if err != nil {
42+
t.Fatal(err)
43+
}
44+
45+
emvs := volume.NewEaseOfMovementStrategy()
46+
report := emvs.Report(snapshots)
47+
48+
fileName := "ease_of_movement_strategy.html"
49+
defer os.Remove(fileName)
50+
51+
err = report.WriteToFile(fileName)
52+
if err != nil {
53+
t.Fatal(err)
54+
}
55+
}

0 commit comments

Comments
 (0)