Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions asset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ type TiingoEndOfDay struct {
Close float64 `json:"close"`

// Volume is the total volume.
Volume int64 `json:"volume"`
Volume float64 `json:"volume"`

// AdjOpen is the adjusted opening price.
AdjOpen float64 `json:"adjOpen"`
Expand All @@ -578,7 +578,7 @@ type TiingoEndOfDay struct {
AdjClose float64 `json:"adjClose"`

// AdjVolume is the adjusted total volume.
AdjVolume int64 `json:"adjVolume"`
AdjVolume float64 `json:"adjVolume"`

// Dividend is the dividend paid out.
Dividend float64 `json:"divCash"`
Expand Down
81 changes: 81 additions & 0 deletions strategy/momentum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ The information provided on this project is strictly for informational purposes
- [func \(t \*TripleRsiStrategy\) IdlePeriod\(\) int](<#TripleRsiStrategy.IdlePeriod>)
- [func \(t \*TripleRsiStrategy\) Name\(\) string](<#TripleRsiStrategy.Name>)
- [func \(t \*TripleRsiStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#TripleRsiStrategy.Report>)
- [type WilliamsRStrategy](<#WilliamsRStrategy>)
- [func NewWilliamsRStrategy\(\) \*WilliamsRStrategy](<#NewWilliamsRStrategy>)
- [func NewWilliamsRStrategyWith\(buyAt, sellAt float64\) \*WilliamsRStrategy](<#NewWilliamsRStrategyWith>)
- [func \(r \*WilliamsRStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#WilliamsRStrategy.Compute>)
- [func \(r \*WilliamsRStrategy\) Name\(\) string](<#WilliamsRStrategy.Name>)
- [func \(r \*WilliamsRStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#WilliamsRStrategy.Report>)


## Constants
Expand Down Expand Up @@ -107,6 +113,18 @@ const (
)
```

<a name="DefaultWilliamsRStrategyBuyAt"></a>

```go
const (
// DefaultWilliamsRStrategyBuyAt defines the default Williams R level at which a Buy action is generated.
DefaultWilliamsRStrategyBuyAt = -80.0

// DefaultWilliamsRStrategySellAt defines the default Williams R level at which a Sell action is generated.
DefaultWilliamsRStrategySellAt = -20.0
)
```

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

Expand Down Expand Up @@ -425,4 +443,67 @@ func (t *TripleRsiStrategy) Report(c <-chan *asset.Snapshot) *helper.Report

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

<a name="WilliamsRStrategy"></a>
## type [WilliamsRStrategy](<https://github.com/cinar/indicator/blob/master/strategy/momentum/williams_r_strategy.go#L25-L34>)

WilliamsRStrategy represents the configuration parameters for calculating the Williams R strategy.

```go
type WilliamsRStrategy struct {
// WilliamsR represents the configuration parameters for calculating the Williams %R.
WilliamsR *momentum.WilliamsR[float64]

// BuyAt defines the Williams R level at which a Buy action is generated.
BuyAt float64

// SellAt defines the Williams R level at which a Sell action is generated.
SellAt float64
}
```

<a name="NewWilliamsRStrategy"></a>
### func [NewWilliamsRStrategy](<https://github.com/cinar/indicator/blob/master/strategy/momentum/williams_r_strategy.go#L37>)

```go
func NewWilliamsRStrategy() *WilliamsRStrategy
```

NewWilliamsRStrategy function initializes a new Williams R strategy instance with the default parameters.

<a name="NewWilliamsRStrategyWith"></a>
### func [NewWilliamsRStrategyWith](<https://github.com/cinar/indicator/blob/master/strategy/momentum/williams_r_strategy.go#L45>)

```go
func NewWilliamsRStrategyWith(buyAt, sellAt float64) *WilliamsRStrategy
```

NewWilliamsRStrategyWith function initializes a new Williams R strategy instance with the given parameters.

<a name="WilliamsRStrategy.Compute"></a>
### func \(\*WilliamsRStrategy\) [Compute](<https://github.com/cinar/indicator/blob/master/strategy/momentum/williams_r_strategy.go#L59>)

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

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

<a name="WilliamsRStrategy.Name"></a>
### func \(\*WilliamsRStrategy\) [Name](<https://github.com/cinar/indicator/blob/master/strategy/momentum/williams_r_strategy.go#L54>)

```go
func (r *WilliamsRStrategy) Name() string
```

Name returns the name of the strategy.

<a name="WilliamsRStrategy.Report"></a>
### func \(\*WilliamsRStrategy\) [Report](<https://github.com/cinar/indicator/blob/master/strategy/momentum/williams_r_strategy.go#L87>)

```go
func (r *WilliamsRStrategy) 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>)
75 changes: 75 additions & 0 deletions strategy/volume/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ The information provided on this project is strictly for informational purposes
- [func \(n \*NegativeVolumeIndexStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#NegativeVolumeIndexStrategy.Compute>)
- [func \(n \*NegativeVolumeIndexStrategy\) Name\(\) string](<#NegativeVolumeIndexStrategy.Name>)
- [func \(n \*NegativeVolumeIndexStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#NegativeVolumeIndexStrategy.Report>)
- [type ObvStrategy](<#ObvStrategy>)
- [func NewObvStrategy\(\) \*ObvStrategy](<#NewObvStrategy>)
- [func NewObvStrategyWith\(period int\) \*ObvStrategy](<#NewObvStrategyWith>)
- [func \(s \*ObvStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#ObvStrategy.Compute>)
- [func \(s \*ObvStrategy\) Name\(\) string](<#ObvStrategy.Name>)
- [func \(s \*ObvStrategy\) Report\(snapshots \<\-chan \*asset.Snapshot\) \*helper.Report](<#ObvStrategy.Report>)
- [type PercentBandMFIStrategy](<#PercentBandMFIStrategy>)
- [func NewPercentBandMFIStrategy\(\) \*PercentBandMFIStrategy](<#NewPercentBandMFIStrategy>)
- [func NewPercentBandMFIStrategyWith\(sellPercentBAt, buyPercentBAt, sellMfiAt, buyMfiAt float64\) \*PercentBandMFIStrategy](<#NewPercentBandMFIStrategyWith>)
Expand Down Expand Up @@ -111,6 +117,15 @@ const (
)
```

<a name="DefaultObvStrategyPeriod"></a>

```go
const (
// DefaultObvStrategyPeriod is the default OBV strategy period.
DefaultObvStrategyPeriod = 10
)
```

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

Expand Down Expand Up @@ -414,6 +429,66 @@ func (n *NegativeVolumeIndexStrategy) Report(c <-chan *asset.Snapshot) *helper.R

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

<a name="ObvStrategy"></a>
## type [ObvStrategy](<https://github.com/cinar/indicator/blob/master/strategy/volume/obv_strategy.go#L24-L30>)

ObvStrategy represents the configuration parameters for calculating the On\-Balance Volume \(OBV\) strategy. Recommends a Buy action when OBV crosses above its SMA, and recommends a Sell action when OBV crosses below its SMA.

```go
type ObvStrategy struct {
// Obv is the OBV indicator instance.
Obv *volume.Obv[float64]

// Sma is the SMA indicator instance.
Sma *trend.Sma[float64]
}
```

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

```go
func NewObvStrategy() *ObvStrategy
```

NewObvStrategy function initializes a new OBV strategy instance with the default parameters.

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

```go
func NewObvStrategyWith(period int) *ObvStrategy
```

NewObvStrategyWith function initializes a new OBV strategy instance with the given period.

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

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

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

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

```go
func (s *ObvStrategy) Name() string
```

Name function returns the name of the strategy.

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

```go
func (s *ObvStrategy) 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="PercentBandMFIStrategy"></a>
## type [PercentBandMFIStrategy](<https://github.com/cinar/indicator/blob/master/strategy/volume/percent_b_and_mfi_strategy.go#L34-L52>)

Expand Down
140 changes: 140 additions & 0 deletions strategy/volume/obv_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright (c) 2021-2026 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/trend"
"github.com/cinar/indicator/v2/volume"
)

const (
// DefaultObvStrategyPeriod is the default OBV strategy period.
DefaultObvStrategyPeriod = 10
)

// ObvStrategy represents the configuration parameters for calculating the On-Balance Volume (OBV) strategy.
// Recommends a Buy action when OBV crosses above its SMA, and recommends a Sell action when OBV crosses below its SMA.
type ObvStrategy struct {
// Obv is the OBV indicator instance.
Obv *volume.Obv[float64]

// Sma is the SMA indicator instance.
Sma *trend.Sma[float64]
}

// NewObvStrategy function initializes a new OBV strategy instance with the default parameters.
func NewObvStrategy() *ObvStrategy {
return NewObvStrategyWith(
DefaultObvStrategyPeriod,
)
}

// NewObvStrategyWith function initializes a new OBV strategy instance with the given period.
func NewObvStrategyWith(period int) *ObvStrategy {
return &ObvStrategy{
Obv: volume.NewObv[float64](),
Sma: trend.NewSmaWithPeriod[float64](period),
}
}

// Name function returns the name of the strategy.
func (s *ObvStrategy) Name() string {
return fmt.Sprintf("OBV Strategy (%d)", s.Sma.Period)
}

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

closings := asset.SnapshotsAsClosings(snapshotsSplice[0])
volumes := asset.SnapshotsAsVolumes(snapshotsSplice[1])

obvValues := s.Obv.Compute(closings, volumes)
obvSplice := helper.Duplicate(obvValues, 2)

smaValues := s.Sma.Compute(obvSplice[0])

// Align OBV with SMA
obvValuesAligned := helper.Skip(obvSplice[1], s.Sma.IdlePeriod())

actions := helper.Operate(obvValuesAligned, smaValues, func(obv, sma float64) strategy.Action {
if obv > sma {
return strategy.Buy
}

if obv < sma {
return strategy.Sell
}

return strategy.Hold
})

// OBV starts after its idle period (0), but SMA starts after its idle period.
actions = helper.Shift(actions, s.Sma.IdlePeriod(), strategy.Hold)

return actions
}

// Report function processes the provided asset snapshots and generates a report annotated with the recommended actions.
func (s *ObvStrategy) Report(snapshots <-chan *asset.Snapshot) *helper.Report {
//
// snapshots[0] -> dates
// snapshots[1] -> closings (for report)
// snapshots[2] -> closings (for obv)
// snapshots[3] -> volumes (for obv)
// snapshots[4] -> actions / outcomes
//
snapshotsSplice := helper.Duplicate(snapshots, 5)

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

closingsForReport := helper.Duplicate(
helper.Skip(
asset.SnapshotsAsClosings(snapshotsSplice[1]),
s.Sma.IdlePeriod(),
),
2,
)

closingsForObv := asset.SnapshotsAsClosings(snapshotsSplice[2])
volumesForObv := asset.SnapshotsAsVolumes(snapshotsSplice[3])

obvValues := s.Obv.Compute(closingsForObv, volumesForObv)
obvSplice := helper.Duplicate(obvValues, 2)

smaValues := s.Sma.Compute(obvSplice[0])
obvValuesAligned := helper.Skip(obvSplice[1], s.Sma.IdlePeriod())

actions, outcomes := strategy.ComputeWithOutcome(s, snapshotsSplice[4])
actions = helper.Skip(actions, s.Sma.IdlePeriod())
outcomes = helper.Skip(outcomes, s.Sma.IdlePeriod())

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

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

report.AddColumn(helper.NewNumericReportColumn("Close", closingsForReport[0]))

report.AddColumn(helper.NewNumericReportColumn("Close", closingsForReport[1]), 1)
report.AddColumn(helper.NewNumericReportColumn("OBV", obvValuesAligned), 1)
report.AddColumn(helper.NewNumericReportColumn("SMA", smaValues), 1)

report.AddColumn(helper.NewAnnotationReportColumn(annotations), 0, 1)

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

return report
}
Loading
Loading