-
-
Notifications
You must be signed in to change notification settings - Fork 111
/
Copy pathkeltner_channel.go
84 lines (70 loc) · 2.59 KB
/
keltner_channel.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Copyright (c) 2021-2024 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator
package volatility
import (
"github.com/cinar/indicator/v2/helper"
"github.com/cinar/indicator/v2/trend"
)
const (
// DefaultKeltnerChannelPeriod is the default period for the Keltner Channel.
DefaultKeltnerChannelPeriod = 20
)
// KeltnerChannel represents the configuration parameters for calculating the Keltner Channel (KC). It provides
// volatility-based bands that are placed on either side of an asset's price and can aid in determining the
// direction of a trend.
//
// Middle Line = EMA(period, closings)
// Upper Band = EMA(period, closings) + 2 * ATR(period, highs, lows, closings)
// Lower Band = EMA(period, closings) - 2 * ATR(period, highs, lows, closings)
//
// Example:
//
// dc := volatility.NewKeltnerChannel[float64]()
// result := dc.Compute(highs, lows, closings)
type KeltnerChannel[T helper.Number] struct {
// Atr is the ATR instance.
Atr *Atr[T]
// Ema is the EMA instance.
Ema *trend.Ema[T]
}
// NewKeltnerChannel function initializes a new Keltner Channel instance with the default parameters.
func NewKeltnerChannel[T helper.Number]() *KeltnerChannel[T] {
return NewKeltnerChannelWithPeriod[T](DefaultKeltnerChannelPeriod)
}
// NewKeltnerChannelWithPeriod function initializes a new Keltner Channel instance with the given period.
func NewKeltnerChannelWithPeriod[T helper.Number](period int) *KeltnerChannel[T] {
return &KeltnerChannel[T]{
Atr: NewAtrWithPeriod[T](period),
Ema: trend.NewEmaWithPeriod[T](period),
}
}
// Compute function takes a channel of numbers and computes the Keltner Channel over the specified period.
func (k *KeltnerChannel[T]) Compute(highs, lows, closings <-chan T) (<-chan T, <-chan T, <-chan T) {
closingsSplice := helper.Duplicate(closings, 2)
// 2 * ATR(period, highs, lows, closings)
atrs := helper.Duplicate(
helper.MultiplyBy(
k.Atr.Compute(highs, lows, closingsSplice[0]),
2,
),
2,
)
// Middle Line = EMA(period, closings)
middles := helper.Duplicate(
helper.Skip(
k.Ema.Compute(closingsSplice[1]),
k.Atr.IdlePeriod()-k.Ema.IdlePeriod(),
),
3,
)
// Upper Band = EMA(period, closings) + 2 * ATR(period, highs, lows, closings)
upper := helper.Add(middles[0], atrs[0])
// Lower Band = EMA(period, closings) - 2 * ATR(period, highs, lows, closings)
lower := helper.Subtract(middles[1], atrs[1])
return upper, middles[2], lower
}
// IdlePeriod is the initial period that Keltner Channel won't yield any results.
func (k *KeltnerChannel[T]) IdlePeriod() int {
return k.Atr.IdlePeriod()
}