Skip to content

Commit

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

Alligator strategy is added.

Fixed #252 

# Change Type

New strategy.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced the "Alligator Strategy" in the "Trend Strategies" section.
- Added two new strategies: "Inverse Strategy" and "No Loss Strategy" in
the "Decorator Strategies" section.
- Enhanced the `AllStrategies` function to include the Alligator
strategy.

- **Bug Fixes**
- Updated method signature for `RemoveAll` in the helper package
documentation.

- **Tests**
- Added tests for the Alligator Strategy's computation and reporting
functionalities.

- **Documentation**
	- Updated README files to reflect new strategies and their details.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
cinar authored Dec 24, 2024
1 parent 69b490f commit 780ba99
Show file tree
Hide file tree
Showing 7 changed files with 540 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ The following list of strategies are currently supported by this package:

### 📈 Trend Strategies

- [Alligator Strategy](strategy/trend/README.md#type-alligatorstrategy)
- [Absolute Price Oscillator (APO) Strategy](strategy/trend/README.md#type-apostrategy)
- [Aroon Strategy](strategy/trend/README.md#type-aroonstrategy)
- [Balance of Power (BoP) Strategy](strategy/trend/README.md#type-bopstrategy)
Expand Down
2 changes: 1 addition & 1 deletion helper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ func Remove(t *testing.T, name string)
Remove removes the file with the given name.

<a name="RemoveAll"></a>
## func [RemoveAll](<https://github.com/cinar/indicator/blob/master/helper/remove.go#L21>)
## func [RemoveAll](<https://github.com/cinar/indicator/blob/master/helper/remove.go#L23>)

```go
func RemoveAll(t *testing.T, path string)
Expand Down
84 changes: 84 additions & 0 deletions strategy/trend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ The information provided on this project is strictly for informational purposes

- [Constants](<#constants>)
- [func AllStrategies\(\) \[\]strategy.Strategy](<#AllStrategies>)
- [type AlligatorStrategy](<#AlligatorStrategy>)
- [func NewAlligatorStrategy\(\) \*AlligatorStrategy](<#NewAlligatorStrategy>)
- [func NewAlligatorStrategyWith\(jawPeriod, teethPeriod, lipPeriod int\) \*AlligatorStrategy](<#NewAlligatorStrategyWith>)
- [func \(a \*AlligatorStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#AlligatorStrategy.Compute>)
- [func \(a \*AlligatorStrategy\) Name\(\) string](<#AlligatorStrategy.Name>)
- [func \(a \*AlligatorStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#AlligatorStrategy.Report>)
- [type ApoStrategy](<#ApoStrategy>)
- [func NewApoStrategy\(\) \*ApoStrategy](<#NewApoStrategy>)
- [func \(a \*ApoStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#ApoStrategy.Compute>)
Expand Down Expand Up @@ -123,6 +129,21 @@ The information provided on this project is strictly for informational purposes

## Constants

<a name="DefaultAlligatorStrategyJawPeriod"></a>

```go
const (
// DefaultAlligatorStrategyJawPeriod is the default jaw period of 13.
DefaultAlligatorStrategyJawPeriod = 13

// DefaultAlligatorStrategyTeethPeriod is the default teeth period of 8.
DefaultAlligatorStrategyTeethPeriod = 8

// DefaultAlligatorStrategyLipPeriod is the default lip period of 5.
DefaultAlligatorStrategyLipPeriod = 5
)
```

<a name="DefaultDemaStrategyPeriod1"></a>

```go
Expand Down Expand Up @@ -213,6 +234,69 @@ func AllStrategies() []strategy.Strategy

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

<a name="AlligatorStrategy"></a>
## type [AlligatorStrategy](<https://github.com/cinar/indicator/blob/master/strategy/trend/alligator_strategy.go#L30-L39>)

AlligatorStrategy represents the configuration parameters for calculating the Alligator strategy. It is a technical indicator to help identify the presence and the direction of the trend. It uses three Smooted Moving Averges \(SMMAs\).

```go
type AlligatorStrategy struct {
// Jaw represents the slowest moving aveage.
Jaw *trend.Smma[float64]

// Teeth represents the medium moving average.
Teeth *trend.Smma[float64]

// Lip represents the fastest moving average.
Lip *trend.Smma[float64]
}
```

<a name="NewAlligatorStrategy"></a>
### func [NewAlligatorStrategy](<https://github.com/cinar/indicator/blob/master/strategy/trend/alligator_strategy.go#L42>)

```go
func NewAlligatorStrategy() *AlligatorStrategy
```

NewAlligatorStrategy function initializes a new Alligator strategy instance.

<a name="NewAlligatorStrategyWith"></a>
### func [NewAlligatorStrategyWith](<https://github.com/cinar/indicator/blob/master/strategy/trend/alligator_strategy.go#L51>)

```go
func NewAlligatorStrategyWith(jawPeriod, teethPeriod, lipPeriod int) *AlligatorStrategy
```

NewAlligatorStrategyWith function initializes a new Alligator strategy instance with the given parameters.

<a name="AlligatorStrategy.Compute"></a>
### func \(\*AlligatorStrategy\) [Compute](<https://github.com/cinar/indicator/blob/master/strategy/trend/alligator_strategy.go#L69>)

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

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

<a name="AlligatorStrategy.Name"></a>
### func \(\*AlligatorStrategy\) [Name](<https://github.com/cinar/indicator/blob/master/strategy/trend/alligator_strategy.go#L60>)

```go
func (a *AlligatorStrategy) Name() string
```

Name returns the name of the strategy.

<a name="AlligatorStrategy.Report"></a>
### func \(\*AlligatorStrategy\) [Report](<https://github.com/cinar/indicator/blob/master/strategy/trend/alligator_strategy.go#L101>)

```go
func (a *AlligatorStrategy) Report(c <-chan *asset.Snapshot) *helper.Report
```

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

<a name="ApoStrategy"></a>
## type [ApoStrategy](<https://github.com/cinar/indicator/blob/master/strategy/trend/apo_strategy.go#L18-L22>)

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

package trend

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"
)

const (
// DefaultAlligatorStrategyJawPeriod is the default jaw period of 13.
DefaultAlligatorStrategyJawPeriod = 13

// DefaultAlligatorStrategyTeethPeriod is the default teeth period of 8.
DefaultAlligatorStrategyTeethPeriod = 8

// DefaultAlligatorStrategyLipPeriod is the default lip period of 5.
DefaultAlligatorStrategyLipPeriod = 5
)

// AlligatorStrategy represents the configuration parameters for calculating the
// Alligator strategy. It is a technical indicator to help identify the presence
// and the direction of the trend. It uses three Smooted Moving Averges (SMMAs).
type AlligatorStrategy struct {
// Jaw represents the slowest moving aveage.
Jaw *trend.Smma[float64]

// Teeth represents the medium moving average.
Teeth *trend.Smma[float64]

// Lip represents the fastest moving average.
Lip *trend.Smma[float64]
}

// NewAlligatorStrategy function initializes a new Alligator strategy instance.
func NewAlligatorStrategy() *AlligatorStrategy {
return NewAlligatorStrategyWith(
DefaultAlligatorStrategyJawPeriod,
DefaultAlligatorStrategyTeethPeriod,
DefaultAlligatorStrategyLipPeriod,
)
}

// NewAlligatorStrategyWith function initializes a new Alligator strategy instance with the given parameters.
func NewAlligatorStrategyWith(jawPeriod, teethPeriod, lipPeriod int) *AlligatorStrategy {
return &AlligatorStrategy{
Jaw: trend.NewSmmaWithPeriod[float64](jawPeriod),
Teeth: trend.NewSmmaWithPeriod[float64](teethPeriod),
Lip: trend.NewSmmaWithPeriod[float64](lipPeriod),
}
}

// Name returns the name of the strategy.
func (a *AlligatorStrategy) Name() string {
return fmt.Sprintf("Alligator Strategy (%d,%d,%d)",
a.Jaw.Period,
a.Teeth.Period,
a.Lip.Period,
)
}

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

jaws := a.Jaw.Compute(closingsSplice[0])
teeths := a.Teeth.Compute(closingsSplice[1])
lips := a.Lip.Compute(closingsSplice[2])

commonPeriod := helper.CommonPeriod(a.Jaw.Period, a.Teeth.Period, a.Lip.Period)
jaws = helper.SyncPeriod(commonPeriod, a.Jaw.Period, jaws)
teeths = helper.SyncPeriod(commonPeriod, a.Teeth.Period, teeths)
lips = helper.SyncPeriod(commonPeriod, a.Lip.Period, lips)

actions := helper.Operate3(jaws, teeths, lips, func(jaw, teeth, lip float64) strategy.Action {
if lip > teeth && lip > jaw {
return strategy.Buy
}

if lip < teeth && lip < jaw {
return strategy.Sell
}

return strategy.Hold
})

// Alligator strategy starts only after a full period.
actions = helper.Shift(actions, commonPeriod, strategy.Hold)

return actions
}

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

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

jaws := a.Jaw.Compute(closingsSplice[1])
teeths := a.Teeth.Compute(closingsSplice[2])
lips := a.Lip.Compute(closingsSplice[3])

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

commonPeriod := helper.CommonPeriod(a.Jaw.Period, a.Teeth.Period, a.Lip.Period)
dates = helper.SyncPeriod(commonPeriod, 0, dates)
closingsSplice[0] = helper.Skip(closingsSplice[0], commonPeriod)
jaws = helper.SyncPeriod(commonPeriod, a.Jaw.Period, jaws)
teeths = helper.SyncPeriod(commonPeriod, a.Teeth.Period, teeths)
lips = helper.SyncPeriod(commonPeriod, a.Lip.Period, lips)
annotations = helper.Skip(annotations, commonPeriod)
outcomes = helper.Skip(outcomes, commonPeriod)

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

report.AddColumn(helper.NewNumericReportColumn("Close", closingsSplice[0]))
report.AddColumn(helper.NewNumericReportColumn("Jaw", jaws), 1)
report.AddColumn(helper.NewNumericReportColumn("Teeth", teeths), 1)
report.AddColumn(helper.NewNumericReportColumn("Lip", lips), 1)
report.AddColumn(helper.NewAnnotationReportColumn(annotations), 0, 1)

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

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

package trend_test

import (
"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/trend"
)

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

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

alligator := trend.NewAlligatorStrategy()
actual := alligator.Compute(snapshots)

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

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

alligator := trend.NewAlligatorStrategy()
report := alligator.Report(snapshots)

fileName := "alligator_strategy.html"
defer helper.Remove(t, fileName)

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

0 comments on commit 780ba99

Please sign in to comment.