-
-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Kaufman's Adaptive Moving Average (KAMA) indicator is added (#151)
# Describe Request Kaufman's Adaptive Moving Average (KAMA) indicator is added. Fixed #24 # Change Type Trend indicator.
- Loading branch information
Showing
5 changed files
with
347 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// Copyright (c) 2021-2024 Onur Cinar. | ||
// The source code is provided under GNU AGPLv3 License. | ||
// https://github.com/cinar/indicator | ||
|
||
package trend | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/cinar/indicator/v2/helper" | ||
) | ||
|
||
const ( | ||
// DefaultKamaErPeriod is the default Efficiency Ratio (ER) period of 10. | ||
DefaultKamaErPeriod = 10 | ||
|
||
// DefaultKamaFastScPeriod is the default Fast Smoothing Constant (SC) period of 2. | ||
DefaultKamaFastScPeriod = 2 | ||
|
||
// DefaultKamaSlowScPeriod is the default Slow Smoothing Constant (SC) period of 30. | ||
DefaultKamaSlowScPeriod = 30 | ||
) | ||
|
||
// Kama represents the parameters for calculating the Kaufman's Adaptive Moving Average (KAMA). | ||
// It is a type of moving average that adapts to market noise or volatility. It tracks prices | ||
// closely during periods of small price swings and low noise. | ||
// | ||
// Direction = Abs(Close - Previous Close Period Ago) | ||
// Volatility = MovingSum(Period, Abs(Close - Previous Close)) | ||
// Efficiency Ratio (ER) = Direction / Volatility | ||
// Smoothing Constant (SC) = (ER * (2/(Fast + 1) - 2/(Slow + 1)) + (2/(Slow + 1)))^2 | ||
// KAMA = Previous KAMA + SC * (Price - Previous KAMA) | ||
// | ||
// Example: | ||
// | ||
// kama := trend.NewKama[float64]() | ||
// result := kama.Compute(c) | ||
type Kama[T helper.Number] struct { | ||
// ErPeriod is the Efficiency Ratio time period. | ||
ErPeriod int | ||
|
||
// FastScPeriod is the Fast Smoothing Constant time period. | ||
FastScPeriod int | ||
|
||
// SlowScPeriod is the Slow Smoothing Constant time period. | ||
SlowScPeriod int | ||
} | ||
|
||
// NewKama function initializes a new KAMA instance with the default parameters. | ||
func NewKama[T helper.Number]() *Kama[T] { | ||
return &Kama[T]{ | ||
ErPeriod: DefaultKamaErPeriod, | ||
FastScPeriod: DefaultKamaFastScPeriod, | ||
SlowScPeriod: DefaultKamaSlowScPeriod, | ||
} | ||
} | ||
|
||
// Compute function takes a channel of numbers and computes the KAMA over the specified period. | ||
func (k *Kama[T]) Compute(closings <-chan T) <-chan T { | ||
closingsSplice := helper.Duplicate(closings, 3) | ||
|
||
// Direction = Abs(Close - Previous Close Period Ago) | ||
directions := helper.Abs( | ||
helper.Change(closingsSplice[0], k.ErPeriod), | ||
) | ||
|
||
// Volatility = MovingSum(Period, Abs(Close - Previous Close)) | ||
movingSum := NewMovingSumWithPeriod[T](k.ErPeriod) | ||
volatilitys := movingSum.Compute( | ||
helper.Abs( | ||
helper.Change(closingsSplice[1], 1), | ||
), | ||
) | ||
|
||
// Efficiency Ratio (ER) = Direction / Volatility | ||
ers := helper.Divide(directions, volatilitys) | ||
|
||
// Smoothing Constant (SC) = (ER * (2/(Slow + 1) - 2/(Fast + 1)) + (2/(Slow + 1)))^2 | ||
fastSc := T(2.0) / T(k.FastScPeriod+1) | ||
slowSc := T(2.0) / T(k.SlowScPeriod+1) | ||
|
||
log.Printf("fastSc=%v", fastSc) | ||
log.Printf("slowSc=%v", slowSc) | ||
|
||
scs := helper.Pow( | ||
helper.IncrementBy( | ||
helper.MultiplyBy( | ||
ers, | ||
(fastSc-slowSc), | ||
), | ||
slowSc, | ||
), | ||
2, | ||
) | ||
|
||
// KAMA = Previous KAMA + SC * (Price - Previous KAMA) | ||
closingsSplice[2] = helper.Skip(closingsSplice[2], k.ErPeriod-1) | ||
|
||
kama := make(chan T) | ||
|
||
go func() { | ||
defer close(kama) | ||
defer helper.Drain(scs) | ||
defer helper.Drain(closingsSplice[2]) | ||
|
||
prevKama, ok := <-closingsSplice[2] | ||
if !ok { | ||
return | ||
} | ||
|
||
for { | ||
closing, ok := <-closingsSplice[2] | ||
if !ok { | ||
break | ||
} | ||
|
||
sc := <-scs | ||
|
||
prevKama = prevKama + sc*(closing-prevKama) | ||
kama <- prevKama | ||
} | ||
}() | ||
|
||
return kama | ||
} | ||
|
||
// IdlePeriod is the initial period that KAMA yield any results. | ||
func (k *Kama[T]) IdlePeriod() int { | ||
return k.ErPeriod | ||
} | ||
|
||
// String is the string representation of the KAMA. | ||
func (k *Kama[T]) String() string { | ||
return fmt.Sprintf("KAMA(%d,%d,%d)", | ||
k.ErPeriod, | ||
k.FastScPeriod, | ||
k.SlowScPeriod, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// 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/helper" | ||
"github.com/cinar/indicator/v2/trend" | ||
) | ||
|
||
func TestKama(t *testing.T) { | ||
type Data struct { | ||
Close float64 | ||
Kama float64 | ||
} | ||
|
||
input, err := helper.ReadFromCsvFile[Data]("testdata/kama.csv", true) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
inputs := helper.Duplicate(input, 2) | ||
closing := helper.Map(inputs[0], func(d *Data) float64 { return d.Close }) | ||
expected := helper.Map(inputs[1], func(d *Data) float64 { return d.Kama }) | ||
|
||
kama := trend.NewKama[float64]() | ||
actual := kama.Compute(closing) | ||
|
||
actual = helper.RoundDigits(actual, 2) | ||
expected = helper.Skip(expected, kama.IdlePeriod()) | ||
|
||
err = helper.CheckEquals(actual, expected) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
func TestKamaEmpty(t *testing.T) { | ||
input := helper.SliceToChan([]float64{}) | ||
expected := helper.SliceToChan([]float64{}) | ||
|
||
kama := trend.NewKama[float64]() | ||
actual := kama.Compute(input) | ||
|
||
err := helper.CheckEquals(actual, expected) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
func TestKamaString(t *testing.T) { | ||
expected := "KAMA(1,2,3)" | ||
|
||
kama := trend.NewKama[float64]() | ||
kama.ErPeriod = 1 | ||
kama.FastScPeriod = 2 | ||
kama.SlowScPeriod = 3 | ||
|
||
actual := kama.String() | ||
|
||
if actual != expected { | ||
t.Fatalf("actual %v expected %v", actual, expected) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
Close,Kama | ||
110.46,0 | ||
109.80,0 | ||
110.17,0 | ||
109.82,0 | ||
110.15,0 | ||
109.31,0 | ||
109.05,0 | ||
107.94,0 | ||
107.76,0 | ||
109.24,0 | ||
109.40,109.24 | ||
108.50,109.22 | ||
107.96,109.12 | ||
108.55,109.10 | ||
108.85,109.09 | ||
110.44,109.12 | ||
109.89,109.14 | ||
110.70,109.28 | ||
110.79,109.44 | ||
110.22,109.46 | ||
110.00,109.47 | ||
109.27,109.46 | ||
106.69,109.39 | ||
107.07,109.32 | ||
107.92,109.29 | ||
107.95,109.18 | ||
107.70,109.08 | ||
107.97,108.95 | ||
106.09,108.42 | ||
106.03,108.02 | ||
107.65,108.00 | ||
109.54,108.01 | ||
110.26,108.26 | ||
110.38,108.48 | ||
111.94,108.91 | ||
113.59,109.67 | ||
113.98,110.49 | ||
113.91,111.11 | ||
112.62,111.46 | ||
112.20,111.61 | ||
111.10,111.57 | ||
110.18,111.55 | ||
111.13,111.54 | ||
111.55,111.54 | ||
112.08,111.55 | ||
111.95,111.57 | ||
111.60,111.57 | ||
111.39,111.55 | ||
112.25,111.56 |