github.com/influxdata/influxdb/v2@v2.7.6/influxql/query/internal/gota/kama.go (about) 1 package gota 2 3 import ( 4 "math" 5 ) 6 7 // KER - Kaufman's Efficiency Ratio (http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:kaufman_s_adaptive_moving_average#efficiency_ratio_er) 8 type KER struct { 9 points []kerPoint 10 noise float64 11 count int 12 idx int // index of newest point 13 } 14 15 type kerPoint struct { 16 price float64 17 diff float64 18 } 19 20 // NewKER constructs a new KER. 21 func NewKER(inTimePeriod int) *KER { 22 return &KER{ 23 points: make([]kerPoint, inTimePeriod), 24 } 25 } 26 27 // WarmCount returns the number of samples that must be provided for the algorithm to be fully "warmed". 28 func (ker *KER) WarmCount() int { 29 return len(ker.points) 30 } 31 32 // Add adds a new sample value to the algorithm and returns the computed value. 33 func (ker *KER) Add(v float64) float64 { 34 //TODO this does not return a sensible value if not warmed. 35 n := len(ker.points) 36 idxOldest := ker.idx + 1 37 if idxOldest >= n { 38 idxOldest = 0 39 } 40 41 signal := math.Abs(v - ker.points[idxOldest].price) 42 43 kp := kerPoint{ 44 price: v, 45 diff: math.Abs(v - ker.points[ker.idx].price), 46 } 47 ker.noise -= ker.points[idxOldest].diff 48 ker.noise += kp.diff 49 noise := ker.noise 50 51 ker.idx = idxOldest 52 ker.points[ker.idx] = kp 53 54 if !ker.Warmed() { 55 ker.count++ 56 } 57 58 if signal == 0 || noise == 0 { 59 return 0 60 } 61 return signal / noise 62 } 63 64 // Warmed indicates whether the algorithm has enough data to generate accurate results. 65 func (ker *KER) Warmed() bool { 66 return ker.count == len(ker.points)+1 67 } 68 69 // KAMA - Kaufman's Adaptive Moving Average (http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:kaufman_s_adaptive_moving_average) 70 type KAMA struct { 71 ker KER 72 last float64 73 } 74 75 // NewKAMA constructs a new KAMA. 76 func NewKAMA(inTimePeriod int) *KAMA { 77 ker := NewKER(inTimePeriod) 78 return &KAMA{ 79 ker: *ker, 80 } 81 } 82 83 // WarmCount returns the number of samples that must be provided for the algorithm to be fully "warmed". 84 func (kama *KAMA) WarmCount() int { 85 return kama.ker.WarmCount() 86 } 87 88 // Add adds a new sample value to the algorithm and returns the computed value. 89 func (kama *KAMA) Add(v float64) float64 { 90 if !kama.Warmed() { 91 /* 92 // initialize with a simple moving average 93 kama.last = 0 94 for _, v := range kama.ker.points[:kama.ker.count] { 95 kama.last += v 96 } 97 kama.last /= float64(kama.ker.count + 1) 98 */ 99 // initialize with the last value 100 kama.last = kama.ker.points[kama.ker.idx].price 101 } 102 103 er := kama.ker.Add(v) 104 sc := math.Pow(er*(2.0/(2.0+1.0)-2.0/(30.0+1.0))+2.0/(30.0+1.0), 2) 105 106 kama.last = kama.last + sc*(v-kama.last) 107 return kama.last 108 } 109 110 // Warmed indicates whether the algorithm has enough data to generate accurate results. 111 func (kama *KAMA) Warmed() bool { 112 return kama.ker.Warmed() 113 }