github.com/influxdata/influxdb/v2@v2.7.6/influxql/query/internal/gota/cmo.go (about) 1 package gota 2 3 // CMO - Chande Momentum Oscillator (https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cmo) 4 type CMO struct { 5 points []cmoPoint 6 sumUp float64 7 sumDown float64 8 count int 9 idx int // index of newest point 10 } 11 12 type cmoPoint struct { 13 price float64 14 diff float64 15 } 16 17 // NewCMO constructs a new CMO. 18 func NewCMO(inTimePeriod int) *CMO { 19 return &CMO{ 20 points: make([]cmoPoint, inTimePeriod-1), 21 } 22 } 23 24 // WarmCount returns the number of samples that must be provided for the algorithm to be fully "warmed". 25 func (cmo *CMO) WarmCount() int { 26 return len(cmo.points) 27 } 28 29 // Add adds a new sample value to the algorithm and returns the computed value. 30 func (cmo *CMO) Add(v float64) float64 { 31 idxOldest := cmo.idx + 1 32 if idxOldest == len(cmo.points) { 33 idxOldest = 0 34 } 35 36 var diff float64 37 if cmo.count != 0 { 38 prev := cmo.points[cmo.idx] 39 diff = v - prev.price 40 if diff > 0 { 41 cmo.sumUp += diff 42 } else if diff < 0 { 43 cmo.sumDown -= diff 44 } 45 } 46 47 var outV float64 48 if cmo.sumUp != 0 || cmo.sumDown != 0 { 49 outV = 100.0 * ((cmo.sumUp - cmo.sumDown) / (cmo.sumUp + cmo.sumDown)) 50 } 51 52 oldest := cmo.points[idxOldest] 53 //NOTE: because we're just adding and subtracting the difference, and not recalculating sumUp/sumDown using cmo.points[].price, it's possible for imprecision to creep in over time. Not sure how significant this is going to be, but if we want to fix it, we could recalculate it from scratch every N points. 54 if oldest.diff > 0 { 55 cmo.sumUp -= oldest.diff 56 } else if oldest.diff < 0 { 57 cmo.sumDown += oldest.diff 58 } 59 60 p := cmoPoint{ 61 price: v, 62 diff: diff, 63 } 64 cmo.points[idxOldest] = p 65 cmo.idx = idxOldest 66 67 if !cmo.Warmed() { 68 cmo.count++ 69 } 70 71 return outV 72 } 73 74 // Warmed indicates whether the algorithm has enough data to generate accurate results. 75 func (cmo *CMO) Warmed() bool { 76 return cmo.count == len(cmo.points)+2 77 } 78 79 // CMOS is a smoothed version of the Chande Momentum Oscillator. 80 // This is the version of CMO utilized by ta-lib. 81 type CMOS struct { 82 emaUp EMA 83 emaDown EMA 84 lastV float64 85 } 86 87 // NewCMOS constructs a new CMOS. 88 func NewCMOS(inTimePeriod int, warmType WarmupType) *CMOS { 89 ema := NewEMA(inTimePeriod+1, warmType) 90 ema.alpha = float64(1) / float64(inTimePeriod) 91 return &CMOS{ 92 emaUp: *ema, 93 emaDown: *ema, 94 } 95 } 96 97 // WarmCount returns the number of samples that must be provided for the algorithm to be fully "warmed". 98 func (cmos CMOS) WarmCount() int { 99 return cmos.emaUp.WarmCount() 100 } 101 102 // Warmed indicates whether the algorithm has enough data to generate accurate results. 103 func (cmos CMOS) Warmed() bool { 104 return cmos.emaUp.Warmed() 105 } 106 107 // Last returns the last output value. 108 func (cmos CMOS) Last() float64 { 109 up := cmos.emaUp.Last() 110 down := cmos.emaDown.Last() 111 return 100.0 * ((up - down) / (up + down)) 112 } 113 114 // Add adds a new sample value to the algorithm and returns the computed value. 115 func (cmos *CMOS) Add(v float64) float64 { 116 var up float64 117 var down float64 118 if v > cmos.lastV { 119 up = v - cmos.lastV 120 } else if v < cmos.lastV { 121 down = cmos.lastV - v 122 } 123 cmos.emaUp.Add(up) 124 cmos.emaDown.Add(down) 125 cmos.lastV = v 126 return cmos.Last() 127 }