-
-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Balance of Power (BoP) strategy added.
- Loading branch information
Showing
8 changed files
with
484 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.git/hooks/pre-commit |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// Copyright (c) 2021-2023 Onur Cinar. | ||
// The source code is provided under GNU AGPLv3 License. | ||
// https://github.com/cinar/indicator | ||
|
||
package strategy | ||
|
||
import ( | ||
"github.com/cinar/indicator/asset" | ||
"github.com/cinar/indicator/helper" | ||
"github.com/cinar/indicator/trend" | ||
) | ||
|
||
// BopStrategy gauges the strength of buying and selling forces using the | ||
// Balance of Power (BoP) indicator. A positive BoP value suggests an | ||
// upward trend, while a negative value indicates a downward trend. A | ||
// BoP value of zero implies equilibrium between the two forces. | ||
type BopStrategy struct { | ||
Strategy | ||
|
||
// Bop represents the configuration parameters for calculating the | ||
// Balance of Power (BoP). | ||
Bop *trend.Bop[float64] | ||
} | ||
|
||
// NewBopStrategy function initializes a new BoP strategy instance with the default parameters. | ||
func NewBopStrategy() *BopStrategy { | ||
return &BopStrategy{ | ||
Bop: trend.NewBop[float64](), | ||
} | ||
} | ||
|
||
// Name returns the name of the strategy. | ||
func (*BopStrategy) Name() string { | ||
return "BoP Strategy" | ||
} | ||
|
||
// Compute processes the provided asset snapshots and generates a | ||
// stream of actionable recommendations. | ||
func (b *BopStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { | ||
snapshots := helper.Duplicate(c, 4) | ||
|
||
openings := asset.SnapshotsAsOpenings(snapshots[0]) | ||
highs := asset.SnapshotsAsHighs(snapshots[1]) | ||
lows := asset.SnapshotsAsLows(snapshots[2]) | ||
closings := asset.SnapshotsAsClosings(snapshots[3]) | ||
|
||
bops := b.Bop.Compute(openings, highs, lows, closings) | ||
|
||
return NormalizeActions(helper.Map(bops, func(bop float64) Action { | ||
if bop > 0 { | ||
return Buy | ||
} | ||
|
||
if bop < 0 { | ||
return Sell | ||
} | ||
|
||
return Hold | ||
})) | ||
} | ||
|
||
// Report processes the provided asset snapshots and generates a | ||
// report annotated with the recommended actions. | ||
func (b *BopStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { | ||
// | ||
// snapshots[0] -> dates | ||
// snapshots[1] -> openings | | ||
// snapshots[2] -> highs | | ||
// snapshots[3] -> lows | | ||
// snapshots[4] -> closings[1] |> bop | ||
// closings[0] -> closings | ||
// snapshots[5] -> actions -> annotations | ||
// outcomes | ||
// | ||
snapshots := helper.Duplicate(c, 6) | ||
|
||
dates := asset.SnapshotsAsDates(snapshots[0]) | ||
openings := asset.SnapshotsAsOpenings(snapshots[1]) | ||
highs := asset.SnapshotsAsHighs(snapshots[2]) | ||
lows := asset.SnapshotsAsLows(snapshots[3]) | ||
closings := helper.Duplicate(asset.SnapshotsAsClosings(snapshots[4]), 2) | ||
|
||
bop := b.Bop.Compute(openings, highs, lows, closings[1]) | ||
|
||
actions, outcomes := ComputeWithOutcome(b, snapshots[5]) | ||
annotations := ActionsToAnnotations(actions) | ||
outcomes = helper.MultiplyBy(outcomes, 100) | ||
|
||
report := helper.NewReport(b.Name(), dates) | ||
report.AddChart() | ||
report.AddChart() | ||
|
||
report.AddColumn(helper.NewNumericReportColumn("Close", closings[0])) | ||
report.AddColumn(helper.NewNumericReportColumn("BoP", bop), 1) | ||
report.AddColumn(helper.NewAnnotationReportColumn(annotations), 0, 1) | ||
|
||
report.AddColumn(helper.NewNumericReportColumn("Outcome", outcomes), 2) | ||
|
||
return report | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Copyright (c) 2021-2023 Onur Cinar. | ||
// The source code is provided under GNU AGPLv3 License. | ||
// https://github.com/cinar/indicator | ||
|
||
package strategy_test | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/cinar/indicator/asset" | ||
"github.com/cinar/indicator/helper" | ||
"github.com/cinar/indicator/strategy" | ||
) | ||
|
||
func TestBopStrategy(t *testing.T) { | ||
type Result struct { | ||
Action strategy.Action | ||
Outcome float64 | ||
} | ||
|
||
snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
results, err := helper.ReadFromCsvFile[Result]("testdata/bop_strategy.csv", true) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
bop := strategy.NewBopStrategy() | ||
actions, outcomes := strategy.ComputeWithOutcome(bop, snapshots) | ||
|
||
outcomes = helper.RoundDigits(outcomes, 2) | ||
|
||
for result := range results { | ||
action := <-actions | ||
outcome := <-outcomes | ||
|
||
if action != result.Action { | ||
t.Fatalf("actual %v expected %v", action, result.Action) | ||
} | ||
|
||
if outcome != result.Outcome { | ||
t.Fatalf("actual %v expected %v", outcome, result.Outcome) | ||
} | ||
} | ||
} | ||
|
||
func TestBopStrategyReport(t *testing.T) { | ||
snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
bop := strategy.NewBopStrategy() | ||
|
||
report := bop.Report(snapshots) | ||
|
||
fileName := "bop_strategy.html" | ||
defer os.Remove(fileName) | ||
|
||
err = report.WriteToFile(fileName) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} |
Oops, something went wrong.