Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kaufman's Adaptive Moving Average (KAMA) strategy is added #152

Merged
merged 3 commits into from
Jun 10, 2024
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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,14 @@ The following list of strategies are currently supported by this package:
- [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)
- [Double Exponential Moving Average (DEMA) Strategy](strategy/trend/README.md#type-demastrategy)
- Chande Forecast Oscillator Strategy
- [Community Channel Index (CCI) Strategy](strategy/trend/README.md#type-ccistrategy)
- [Double Exponential Moving Average (DEMA) Strategy](strategy/trend/README.md#type-demastrategy)
- [Golden Cross Strategy](strategy/trend/README.md#type-goldencrossstrategy)
- [Random Index (KDJ) Strategy](strategy/trend/README.md#type-kdjstrategy)
- [Kaufman's Adaptive Moving Average (KAMA) Strategy](strategy/trend/README.md#type-kamastrategy)
- [Moving Average Convergence Divergence (MACD) Strategy](strategy/trend/README.md#type-macdstrategy)
- [Qstick Strategy](strategy/trend/README.md#type-qstickstrategy)
- [Random Index (KDJ) Strategy](strategy/trend/README.md#type-kdjstrategy)
- [Triangular Moving Average (TRIMA) Strategy](strategy/trend/README.md#type-trimastrategy)
- [Triple Exponential Average (TRIX) Strategy](strategy/trend/README.md#type-trixstrategy)
- [Triple Moving Average Crossover Strategy](strategy/trend/README.md#type-triplemovingaveragecrossoverstrategy)
Expand Down
63 changes: 63 additions & 0 deletions strategy/trend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ The information provided on this project is strictly for informational purposes
- [func \(t \*GoldenCrossStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#GoldenCrossStrategy.Compute>)
- [func \(\*GoldenCrossStrategy\) Name\(\) string](<#GoldenCrossStrategy.Name>)
- [func \(t \*GoldenCrossStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#GoldenCrossStrategy.Report>)
- [type KamaStrategy](<#KamaStrategy>)
- [func NewKamaStrategy\(\) \*KamaStrategy](<#NewKamaStrategy>)
- [func NewKamaStrategyWith\(erPeriod, fastScPeriod, slowScPeriod int\) \*KamaStrategy](<#NewKamaStrategyWith>)
- [func \(k \*KamaStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#KamaStrategy.Compute>)
- [func \(k \*KamaStrategy\) Name\(\) string](<#KamaStrategy.Name>)
- [func \(k \*KamaStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#KamaStrategy.Report>)
- [type KdjStrategy](<#KdjStrategy>)
- [func NewKdjStrategy\(\) \*KdjStrategy](<#NewKdjStrategy>)
- [func \(kdj \*KdjStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#KdjStrategy.Compute>)
Expand Down Expand Up @@ -484,6 +490,63 @@ func (t *GoldenCrossStrategy) Report(c <-chan *asset.Snapshot) *helper.Report

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

<a name="KamaStrategy"></a>
## type [KamaStrategy](<https://github.com/cinar/indicator/blob/master/strategy/trend/kama_strategy.go#L16-L19>)

KamaStrategy represents the configuration parameters for calculating the KAMA strategy. A closing price crossing above the KAMA suggests a bullish trend, while crossing below the KAMA indicats a bearish trend.

```go
type KamaStrategy struct {
// Kama represents the configuration parameters for calculating the Kaufman's Adaptive Moving Average (KAMA).
Kama *trend.Kama[float64]
}
```

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

```go
func NewKamaStrategy() *KamaStrategy
```

NewKamaStrategy function initializes a new KAMA strategy instance.

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

```go
func NewKamaStrategyWith(erPeriod, fastScPeriod, slowScPeriod int) *KamaStrategy
```

NewKamaStrategyWith function initializes a new KAMA strategy instance with the given parameters.

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

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

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

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

```go
func (k *KamaStrategy) Name() string
```

Name returns the name of the strategy.

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

```go
func (k *KamaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report
```

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

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

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

package trend

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

// KamaStrategy represents the configuration parameters for calculating the KAMA strategy. A closing price crossing
// above the KAMA suggests a bullish trend, while crossing below the KAMA indicats a bearish trend.
type KamaStrategy struct {
// Kama represents the configuration parameters for calculating the Kaufman's Adaptive Moving Average (KAMA).
Kama *trend.Kama[float64]
}

// NewKamaStrategy function initializes a new KAMA strategy instance.
func NewKamaStrategy() *KamaStrategy {
return NewKamaStrategyWith(
trend.DefaultKamaErPeriod,
trend.DefaultKamaFastScPeriod,
trend.DefaultKamaSlowScPeriod,
)
}

// NewKamaStrategyWith function initializes a new KAMA strategy instance with the given parameters.
func NewKamaStrategyWith(erPeriod, fastScPeriod, slowScPeriod int) *KamaStrategy {
return &KamaStrategy{
Kama: trend.NewKamaWith[float64](
erPeriod,
fastScPeriod,
slowScPeriod,
),
}
}

// Name returns the name of the strategy.
func (k *KamaStrategy) Name() string {
return k.Kama.String()
}

// Compute processes the provided asset snapshots and generates a stream of actionable recommendations.
func (k *KamaStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action {
closingsSplice := helper.Duplicate(asset.SnapshotsAsClosings(snapshots), 2)
closingsSplice[1] = helper.Skip(closingsSplice[1], k.Kama.IdlePeriod())

kamas := k.Kama.Compute(closingsSplice[0])

actions := helper.Operate(kamas, closingsSplice[1], func(kama, closing float64) strategy.Action {
// A closing price crossing above the KAMA suggests a bullish trend.
if closing > kama {
return strategy.Buy
}

// While crossing below the KAMA indicats a bearish trend.
if closing < kama {
return strategy.Sell
}

return strategy.Hold
})

// KAMA starts only after a full period.
actions = helper.Shift(actions, k.Kama.IdlePeriod(), strategy.Hold)

actions = strategy.NormalizeActions(actions)

return actions
}

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

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

closingsSplice := helper.Duplicate(asset.SnapshotsAsClosings(snapshotsSplice[1]), 2)
closingsSplice[1] = helper.Skip(closingsSplice[1], k.Kama.IdlePeriod())

kamas := k.Kama.Compute(closingsSplice[0])

actions, outcomes := strategy.ComputeWithOutcome(k, snapshotsSplice[2])
actions = helper.Skip(actions, k.Kama.IdlePeriod())
outcomes = helper.Skip(outcomes, k.Kama.IdlePeriod())

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

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

report.AddColumn(helper.NewNumericReportColumn("Close", closingsSplice[1]))
report.AddColumn(helper.NewNumericReportColumn("KAMA", kamas), 1)
report.AddColumn(helper.NewAnnotationReportColumn(annotations), 0, 1)

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

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

package trend_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/trend"
)

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

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

kama := trend.NewKamaStrategy()
actual := kama.Compute(snapshots)

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

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

kama := trend.NewKamaStrategy()

report := kama.Report(snapshots)

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

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