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  }