Skip to content

Commit

Permalink
Bollinger Bands strategy added.
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar committed Dec 30, 2023
1 parent 875fdc9 commit f269d97
Show file tree
Hide file tree
Showing 7 changed files with 549 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ The following list of strategies are currently supported by this package:

### 🎢 Volatility Strategies

- Bollinger Bands Strategy
- [Bollinger Bands Strategy](strategy/volatility/README.md#type-bollingerbandsstrategy)
- Projection Oscillator Strategy

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

# volatility

```go
import "github.com/cinar/indicator/strategy/volatility"
```

Package volatility contains the volatility 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-2023 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

- [func AllStrategies\(\) \[\]strategy.Strategy](<#AllStrategies>)
- [type BollingerBandsStrategy](<#BollingerBandsStrategy>)
- [func NewBollingerBandsStrategy\(\) \*BollingerBandsStrategy](<#NewBollingerBandsStrategy>)
- [func \(b \*BollingerBandsStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#BollingerBandsStrategy.Compute>)
- [func \(\*BollingerBandsStrategy\) Name\(\) string](<#BollingerBandsStrategy.Name>)
- [func \(b \*BollingerBandsStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#BollingerBandsStrategy.Report>)


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

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

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

<a name="BollingerBandsStrategy"></a>
## type [BollingerBandsStrategy](<https://github.com/cinar/indicator/blob/v2/strategy/volatility/bollinger_bands_strategy.go#L17-L22>)

BollingerBandsStrategy represents the configuration parameters for calculating the Bollinger Bands strategy. A closing value crossing above the upper band suggets a Buy signal, while crossing below the lower band indivates a Sell signal.

```go
type BollingerBandsStrategy struct {
strategy.Strategy

// BollingerBands represents the configuration parameters for calculating the Bollinger Bands.
BollingerBands *volatility.BollingerBands[float64]
}
```

<a name="NewBollingerBandsStrategy"></a>
### func [NewBollingerBandsStrategy](<https://github.com/cinar/indicator/blob/v2/strategy/volatility/bollinger_bands_strategy.go#L25>)

```go
func NewBollingerBandsStrategy() *BollingerBandsStrategy
```

NewBollingerBandsStrategy function initializes a new Bollinger Bands strategy instance.

<a name="BollingerBandsStrategy.Compute"></a>
### func \(\*BollingerBandsStrategy\) [Compute](<https://github.com/cinar/indicator/blob/v2/strategy/volatility/bollinger_bands_strategy.go#L37>)

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

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

<a name="BollingerBandsStrategy.Name"></a>
### func \(\*BollingerBandsStrategy\) [Name](<https://github.com/cinar/indicator/blob/v2/strategy/volatility/bollinger_bands_strategy.go#L32>)

```go
func (*BollingerBandsStrategy) Name() string
```

Name returns the name of the strategy.

<a name="BollingerBandsStrategy.Report"></a>
### func \(\*BollingerBandsStrategy\) [Report](<https://github.com/cinar/indicator/blob/v2/strategy/volatility/bollinger_bands_strategy.go#L82>)

```go
func (b *BollingerBandsStrategy) 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>)
118 changes: 118 additions & 0 deletions strategy/volatility/bollinger_bands_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright (c) 2021-2023 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator

package volatility

import (
"github.com/cinar/indicator/asset"
"github.com/cinar/indicator/helper"
"github.com/cinar/indicator/strategy"
"github.com/cinar/indicator/volatility"
)

// BollingerBandsStrategy represents the configuration parameters for calculating the Bollinger Bands strategy.
// A closing value crossing above the upper band suggets a Buy signal, while crossing below the lower band
// indivates a Sell signal.
type BollingerBandsStrategy struct {
strategy.Strategy

// BollingerBands represents the configuration parameters for calculating the Bollinger Bands.
BollingerBands *volatility.BollingerBands[float64]
}

// NewBollingerBandsStrategy function initializes a new Bollinger Bands strategy instance.
func NewBollingerBandsStrategy() *BollingerBandsStrategy {
return &BollingerBandsStrategy{
BollingerBands: volatility.NewBollingerBands[float64](),
}
}

// Name returns the name of the strategy.
func (*BollingerBandsStrategy) Name() string {
return "Bollinger Bands Strategy"
}

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

uppers, middles, lowers := b.BollingerBands.Compute(closings[0])

go helper.Drain(middles)

closings[1] = helper.Skip(closings[1], b.BollingerBands.IdlePeriod())

aboveUppers := helper.Subtract(
closings[1],
uppers,
)

closings[2] = helper.Skip(closings[2], b.BollingerBands.IdlePeriod())

belowLowers := helper.Subtract(
closings[2],
lowers,
)

actions := helper.Operate(aboveUppers, belowLowers, func(aboveUpper, belowLower float64) strategy.Action {
if aboveUpper > 0 {
return strategy.Buy
}

if belowLower < 0 {
return strategy.Sell
}

return strategy.Hold
})

actions = strategy.NormalizeActions(actions)

// Bollinger Bands starts only after a full period.
actions = helper.Shift(actions, b.BollingerBands.IdlePeriod(), strategy.Hold)

return actions
}

// Report processes the provided asset snapshots and generates a report annotated with the recommended actions.
func (b *BollingerBandsStrategy) Report(c <-chan *asset.Snapshot) *helper.Report {
//
// snapshots[0] -> dates
// snapshots[1] -> closings[0] -> closings
// closings[1] -> upper
// -> middle
// -> lower
// snapshots[2] -> actions -> annotations
// -> outcomes
//
snapshots := helper.Duplicate(c, 3)

dates := asset.SnapshotsAsDates(snapshots[0])
closings := helper.Duplicate(asset.SnapshotsAsClosings(snapshots[1]), 2)

uppers, middles, lowers := b.BollingerBands.Compute(closings[0])
uppers = helper.Shift(uppers, b.BollingerBands.IdlePeriod(), 0)
middles = helper.Shift(middles, b.BollingerBands.IdlePeriod(), 0)
lowers = helper.Shift(lowers, b.BollingerBands.IdlePeriod(), 0)

actions, outcomes := strategy.ComputeWithOutcome(b, snapshots[2])
annotations := strategy.ActionsToAnnotations(actions)
outcomes = helper.MultiplyBy(outcomes, 100)

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

report.AddColumn(helper.NewNumericReportColumn("Close", closings[1]))
report.AddColumn(helper.NewNumericReportColumn("Upper", uppers))
report.AddColumn(helper.NewNumericReportColumn("Middle", middles))
report.AddColumn(helper.NewNumericReportColumn("Lower", lowers))
report.AddColumn(helper.NewAnnotationReportColumn(annotations))

report.AddColumn(helper.NewNumericReportColumn("Outcome", outcomes), 1)

return report
}
55 changes: 55 additions & 0 deletions strategy/volatility/bollinger_bands_strategy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2021-2023 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator

package volatility_test

import (
"os"
"testing"

"github.com/cinar/indicator/asset"
"github.com/cinar/indicator/helper"
"github.com/cinar/indicator/strategy"
"github.com/cinar/indicator/strategy/volatility"
)

func TestBollingerBandsStrategy(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/bollinger_bands_strategy.csv", true)
if err != nil {
t.Fatal(err)
}

bb := volatility.NewBollingerBandsStrategy()
actions, outcomes := strategy.ComputeWithOutcome(bb, snapshots)
outcomes = helper.RoundDigits(outcomes, 2)

err = strategy.CheckResults(results, actions, outcomes)
if err != nil {
t.Fatal(err)
}
}

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

bb := volatility.NewBollingerBandsStrategy()

report := bb.Report(snapshots)

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

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

0 comments on commit f269d97

Please sign in to comment.