Skip to content

Commit

Permalink
Inverse strategy is added. (#200)
Browse files Browse the repository at this point in the history
# Describe Request

Inverse strategy is added.

Fixed # (issue)

# Change Type

New strategy.
  • Loading branch information
cinar authored Aug 31, 2024
1 parent e142e3f commit 0e0cfb5
Show file tree
Hide file tree
Showing 5 changed files with 442 additions and 1 deletion.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,17 @@ The following list of strategies are currently supported by this package:
Compound strategies merge multiple strategies to produce integrated recommendations. They combine individual strategies' recommendations using various decision-making logic.

- [All Strategy](strategy/README.md#type-allstrategy)
- [Or Strategy](strategy/README.md#type-orstrategy)
- [Inverse Strategy](strategy/README.md#type-inversestrategy)
- [Majority Strategy](strategy/README.md#type-majoritystrategy)
- [MACD-RSI Strategy](strategy/compound/README.md#type-macdrsistrategy)
- [Or Strategy](strategy/README.md#type-orstrategy)
- [Split Strategy](strategy/README.md#type-splitstrategy)

### 🎁 Decorator Strategies

Decorator strategies offer a way to alter the recommendations of other strategies.

- [Inverse Strategy](strategy/decorator/README.md#type-inversestrategy)
- [No Loss Strategy](strategy/decorator/README.md#type-nolossstrategy)

🗃 Repositories
Expand Down
55 changes: 55 additions & 0 deletions strategy/decorator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,68 @@ The information provided on this project is strictly for informational purposes

## Index

- [type InverseStrategy](<#InverseStrategy>)
- [func NewInverseStrategy\(innerStrategy strategy.Strategy\) \*InverseStrategy](<#NewInverseStrategy>)
- [func \(i \*InverseStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#InverseStrategy.Compute>)
- [func \(i \*InverseStrategy\) Name\(\) string](<#InverseStrategy.Name>)
- [func \(i \*InverseStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#InverseStrategy.Report>)
- [type NoLossStrategy](<#NoLossStrategy>)
- [func NewNoLossStrategy\(innerStrategy strategy.Strategy\) \*NoLossStrategy](<#NewNoLossStrategy>)
- [func \(n \*NoLossStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#NoLossStrategy.Compute>)
- [func \(n \*NoLossStrategy\) Name\(\) string](<#NoLossStrategy.Name>)
- [func \(n \*NoLossStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#NoLossStrategy.Report>)


<a name="InverseStrategy"></a>
## type [InverseStrategy](<https://github.com/cinar/indicator/blob/master/strategy/decorator/inverse_strategy.go#L17-L22>)

InverseStrategy reverses the advice of another strategy. For example, if the original strategy suggests buying an asset, InverseStrategy would recommend selling it.

```go
type InverseStrategy struct {
strategy.Strategy

// InnerStrategy is the inner strategy.
InnerStrategy strategy.Strategy
}
```

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

```go
func NewInverseStrategy(innerStrategy strategy.Strategy) *InverseStrategy
```

NewInverseStrategy function initializes a new inverse strategy instance.

<a name="InverseStrategy.Compute"></a>
### func \(\*InverseStrategy\) [Compute](<https://github.com/cinar/indicator/blob/master/strategy/decorator/inverse_strategy.go#L37>)

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

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

<a name="InverseStrategy.Name"></a>
### func \(\*InverseStrategy\) [Name](<https://github.com/cinar/indicator/blob/master/strategy/decorator/inverse_strategy.go#L32>)

```go
func (i *InverseStrategy) Name() string
```

Name returns the name of the strategy.

<a name="InverseStrategy.Report"></a>
### func \(\*InverseStrategy\) [Report](<https://github.com/cinar/indicator/blob/master/strategy/decorator/inverse_strategy.go#L53>)

```go
func (i *InverseStrategy) Report(c <-chan *asset.Snapshot) *helper.Report
```

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

<a name="NoLossStrategy"></a>
## type [NoLossStrategy](<https://github.com/cinar/indicator/blob/master/strategy/decorator/no_loss_strategy.go#L17-L22>)

Expand Down
72 changes: 72 additions & 0 deletions strategy/decorator/inverse_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2021-2024 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator

package decorator

import (
"fmt"

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

// InverseStrategy reverses the advice of another strategy. For example, if the original strategy suggests buying an
// asset, InverseStrategy would recommend selling it.
type InverseStrategy struct {
strategy.Strategy

// InnerStrategy is the inner strategy.
InnerStrategy strategy.Strategy
}

// NewInverseStrategy function initializes a new inverse strategy instance.
func NewInverseStrategy(innerStrategy strategy.Strategy) *InverseStrategy {
return &InverseStrategy{
InnerStrategy: innerStrategy,
}
}

// Name returns the name of the strategy.
func (i *InverseStrategy) Name() string {
return fmt.Sprintf("Inverse Strategy (%s)", i.InnerStrategy.Name())
}

// Compute processes the provided asset snapshots and generates a stream of actionable recommendations.
func (i *InverseStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action {
return helper.Map(i.InnerStrategy.Compute(snapshots), func(action strategy.Action) strategy.Action {
switch action {
case strategy.Buy:
return strategy.Sell

case strategy.Sell:
return strategy.Buy

default:
return strategy.Hold
}
})
}

// Report processes the provided asset snapshots and generates a report annotated with the recommended actions.
func (i *InverseStrategy) Report(c <-chan *asset.Snapshot) *helper.Report {
snapshots := helper.Duplicate(c, 3)

dates := asset.SnapshotsAsDates(snapshots[0])
closings := asset.SnapshotsAsClosings(snapshots[1])

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

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

report.AddColumn(helper.NewNumericReportColumn("Close", closings))
report.AddColumn(helper.NewAnnotationReportColumn(annotations))

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

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

package decorator_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/decorator"
"github.com/cinar/indicator/v2/strategy/trend"
)

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

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

innerStrategy := trend.NewMacdStrategy()
strategy := decorator.NewInverseStrategy(innerStrategy)

actual := strategy.Compute(snapshots)

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

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

innerStrategy := trend.NewMacdStrategy()
strategy := decorator.NewInverseStrategy(innerStrategy)

report := strategy.Report(snapshots)

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

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

0 comments on commit 0e0cfb5

Please sign in to comment.