diff --git a/README.md b/README.md
index 273a65e..6ae82fb 100644
--- a/README.md
+++ b/README.md
@@ -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)
diff --git a/helper/README.md b/helper/README.md
index da34873..57369c8 100644
--- a/helper/README.md
+++ b/helper/README.md
@@ -803,7 +803,7 @@ func Remove(t *testing.T, name string)
Remove removes the file with the given name.
-## func [RemoveAll]()
+## func [RemoveAll]()
```go
func RemoveAll(t *testing.T, path string)
diff --git a/strategy/trend/README.md b/strategy/trend/README.md
index cc03efd..6f59dcb 100644
--- a/strategy/trend/README.md
+++ b/strategy/trend/README.md
@@ -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>)
@@ -123,6 +129,21 @@ The information provided on this project is strictly for informational purposes
## Constants
+
+
+```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
+)
+```
+
```go
@@ -213,6 +234,69 @@ func AllStrategies() []strategy.Strategy
AllStrategies returns a slice containing references to all available trend strategies.
+
+## type [AlligatorStrategy]()
+
+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]
+}
+```
+
+
+### func [NewAlligatorStrategy]()
+
+```go
+func NewAlligatorStrategy() *AlligatorStrategy
+```
+
+NewAlligatorStrategy function initializes a new Alligator strategy instance.
+
+
+### func [NewAlligatorStrategyWith]()
+
+```go
+func NewAlligatorStrategyWith(jawPeriod, teethPeriod, lipPeriod int) *AlligatorStrategy
+```
+
+NewAlligatorStrategyWith function initializes a new Alligator strategy instance with the given parameters.
+
+
+### func \(\*AlligatorStrategy\) [Compute]()
+
+```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.
+
+
+### func \(\*AlligatorStrategy\) [Name]()
+
+```go
+func (a *AlligatorStrategy) Name() string
+```
+
+Name returns the name of the strategy.
+
+
+### func \(\*AlligatorStrategy\) [Report]()
+
+```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.
+
## type [ApoStrategy]()
diff --git a/strategy/trend/alligator_strategy.go b/strategy/trend/alligator_strategy.go
new file mode 100644
index 0000000..16d8dd5
--- /dev/null
+++ b/strategy/trend/alligator_strategy.go
@@ -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
+}
diff --git a/strategy/trend/alligator_strategy_test.go b/strategy/trend/alligator_strategy_test.go
new file mode 100644
index 0000000..978efd7
--- /dev/null
+++ b/strategy/trend/alligator_strategy_test.go
@@ -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)
+ }
+}
diff --git a/strategy/trend/testdata/alligator_strategy.csv b/strategy/trend/testdata/alligator_strategy.csv
new file mode 100644
index 0000000..003391d
--- /dev/null
+++ b/strategy/trend/testdata/alligator_strategy.csv
@@ -0,0 +1,253 @@
+Action
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+0
+0
+0
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+1
+1
+1
+1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+0
+-1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+-1
+0
+0
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
diff --git a/strategy/trend/trend.go b/strategy/trend/trend.go
index 141fe79..6e8478c 100644
--- a/strategy/trend/trend.go
+++ b/strategy/trend/trend.go
@@ -23,6 +23,7 @@ import "github.com/cinar/indicator/v2/strategy"
// AllStrategies returns a slice containing references to all available trend strategies.
func AllStrategies() []strategy.Strategy {
return []strategy.Strategy{
+ NewAlligatorStrategy(),
NewApoStrategy(),
NewAroonStrategy(),
NewBopStrategy(),