package chart const ( // DefaultEMAPeriod is the default EMA period used in the sigma calculation. DefaultEMAPeriod = 12 ) // EMASeries is a computed series. type EMASeries struct { Name string Style Style YAxis YAxisType Period int InnerSeries ValueProvider cache []float64 } // GetName returns the name of the time series. func (ema EMASeries) GetName() string { return ema.Name } // GetStyle returns the line style. func (ema EMASeries) GetStyle() Style { return ema.Style } // GetYAxis returns which YAxis the series draws on. func (ema EMASeries) GetYAxis() YAxisType { return ema.YAxis } // GetPeriod returns the window size. func (ema EMASeries) GetPeriod() int { if ema.Period == 0 { return DefaultEMAPeriod } return ema.Period } // Len returns the number of elements in the series. func (ema EMASeries) Len() int { return ema.InnerSeries.Len() } // GetSigma returns the smoothing factor for the serise. func (ema EMASeries) GetSigma() float64 { return 2.0 / (float64(ema.GetPeriod()) + 1) } // GetValue gets a value at a given index. func (ema *EMASeries) GetValue(index int) (x, y float64) { if ema.InnerSeries == nil { return } if len(ema.cache) == 0 { ema.ensureCachedValues() } vx, _ := ema.InnerSeries.GetValue(index) x = vx y = ema.cache[index] return } // GetLastValue computes the last moving average value but walking back window size samples, // and recomputing the last moving average chunk. func (ema *EMASeries) GetLastValue() (x, y float64) { if ema.InnerSeries == nil { return } if len(ema.cache) == 0 { ema.ensureCachedValues() } lastIndex := ema.InnerSeries.Len() - 1 x, _ = ema.InnerSeries.GetValue(lastIndex) y = ema.cache[lastIndex] return } func (ema *EMASeries) ensureCachedValues() { seriesLength := ema.InnerSeries.Len() ema.cache = make([]float64, seriesLength) sigma := ema.GetSigma() for x := 0; x < seriesLength; x++ { _, y := ema.InnerSeries.GetValue(x) if x == 0 { ema.cache[x] = y continue } previousEMA := ema.cache[x-1] ema.cache[x] = ((y - previousEMA) * sigma) + previousEMA } } // Render renders the series. func (ema *EMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) { style := ema.Style.InheritFrom(defaults) Draw.LineSeries(r, canvasBox, xrange, yrange, style, ema) }