exp moving average, renaming moving average to simple moving average.
This commit is contained in:
parent
9ad15b3288
commit
8bd5cdfe17
5 changed files with 158 additions and 19 deletions
88
exp_moving_average_series.go
Normal file
88
exp_moving_average_series.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package chart
|
||||
|
||||
const (
|
||||
// DefaultExponentialMovingAverageSigma is the default exponential smoothing factor.
|
||||
DefaultExponentialMovingAverageSigma = 0.25
|
||||
)
|
||||
|
||||
// ExponentialMovingAverageSeries is a computed series.
|
||||
type ExponentialMovingAverageSeries struct {
|
||||
Name string
|
||||
Style Style
|
||||
YAxis YAxisType
|
||||
|
||||
// Sigma is the 'smoothing factor' parameter.
|
||||
Sigma float64
|
||||
InnerSeries ValueProvider
|
||||
|
||||
valueBuffer []float64
|
||||
}
|
||||
|
||||
// GetName returns the name of the time series.
|
||||
func (mas ExponentialMovingAverageSeries) GetName() string {
|
||||
return mas.Name
|
||||
}
|
||||
|
||||
// GetStyle returns the line style.
|
||||
func (mas ExponentialMovingAverageSeries) GetStyle() Style {
|
||||
return mas.Style
|
||||
}
|
||||
|
||||
// GetYAxis returns which YAxis the series draws on.
|
||||
func (mas ExponentialMovingAverageSeries) GetYAxis() YAxisType {
|
||||
return mas.YAxis
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the series.
|
||||
func (mas *ExponentialMovingAverageSeries) Len() int {
|
||||
return mas.InnerSeries.Len()
|
||||
}
|
||||
|
||||
// GetSigma returns the smoothing factor for the serise.
|
||||
func (mas ExponentialMovingAverageSeries) GetSigma(defaults ...float64) float64 {
|
||||
if mas.Sigma == 0 {
|
||||
if len(defaults) > 0 {
|
||||
return defaults[0]
|
||||
}
|
||||
return DefaultExponentialMovingAverageSigma
|
||||
}
|
||||
return mas.Sigma
|
||||
}
|
||||
|
||||
// GetValue gets a value at a given index.
|
||||
func (mas *ExponentialMovingAverageSeries) GetValue(index int) (x float64, y float64) {
|
||||
if mas.InnerSeries == nil {
|
||||
return
|
||||
}
|
||||
if mas.valueBuffer == nil || index == 0 {
|
||||
mas.valueBuffer = make([]float64, mas.InnerSeries.Len())
|
||||
}
|
||||
vx, vy := mas.InnerSeries.GetValue(index)
|
||||
x = vx
|
||||
if index == 0 {
|
||||
mas.valueBuffer[0] = vy
|
||||
y = vy
|
||||
return
|
||||
}
|
||||
|
||||
sig := mas.GetSigma()
|
||||
mas.valueBuffer[index] = sig*vy + ((1.0 - sig) * mas.valueBuffer[index-1])
|
||||
y = mas.valueBuffer[index]
|
||||
return
|
||||
}
|
||||
|
||||
// GetLastValue computes the last moving average value but walking back window size samples,
|
||||
// and recomputing the last moving average chunk.
|
||||
func (mas ExponentialMovingAverageSeries) GetLastValue() (x float64, y float64) {
|
||||
if mas.InnerSeries == nil {
|
||||
return
|
||||
}
|
||||
|
||||
return 0.0, 0.0 //this one is going to be a bitch.
|
||||
}
|
||||
|
||||
// Render renders the series.
|
||||
func (mas *ExponentialMovingAverageSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||
style := mas.Style.WithDefaultsFrom(defaults)
|
||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, mas)
|
||||
}
|
31
exp_moving_average_series_test.go
Normal file
31
exp_moving_average_series_test.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package chart
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/blendlabs/go-assert"
|
||||
)
|
||||
|
||||
func TestExponentialMovingAverageSeries(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
mockSeries := mockValueProvider{
|
||||
Seq(1.0, 10.0),
|
||||
Seq(10, 1.0),
|
||||
}
|
||||
assert.Equal(10, mockSeries.Len())
|
||||
|
||||
mas := &ExponentialMovingAverageSeries{
|
||||
InnerSeries: mockSeries,
|
||||
}
|
||||
|
||||
var yvalues []float64
|
||||
for x := 0; x < mas.Len(); x++ {
|
||||
_, y := mas.GetValue(x)
|
||||
yvalues = append(yvalues, y)
|
||||
}
|
||||
|
||||
assert.Equal(10.0, yvalues[0])
|
||||
assert.True(math.Abs(yvalues[9]-3.77) < 0.01)
|
||||
}
|
|
@ -5,8 +5,8 @@ const (
|
|||
DefaultMovingAverageWindowSize = 5
|
||||
)
|
||||
|
||||
// MovingAverageSeries is a computed series.
|
||||
type MovingAverageSeries struct {
|
||||
// SimpleMovingAverageSeries is a computed series.
|
||||
type SimpleMovingAverageSeries struct {
|
||||
Name string
|
||||
Style Style
|
||||
YAxis YAxisType
|
||||
|
@ -18,27 +18,27 @@ type MovingAverageSeries struct {
|
|||
}
|
||||
|
||||
// GetName returns the name of the time series.
|
||||
func (mas MovingAverageSeries) GetName() string {
|
||||
func (mas SimpleMovingAverageSeries) GetName() string {
|
||||
return mas.Name
|
||||
}
|
||||
|
||||
// GetStyle returns the line style.
|
||||
func (mas MovingAverageSeries) GetStyle() Style {
|
||||
func (mas SimpleMovingAverageSeries) GetStyle() Style {
|
||||
return mas.Style
|
||||
}
|
||||
|
||||
// GetYAxis returns which YAxis the series draws on.
|
||||
func (mas MovingAverageSeries) GetYAxis() YAxisType {
|
||||
func (mas SimpleMovingAverageSeries) GetYAxis() YAxisType {
|
||||
return mas.YAxis
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the series.
|
||||
func (mas *MovingAverageSeries) Len() int {
|
||||
func (mas *SimpleMovingAverageSeries) Len() int {
|
||||
return mas.InnerSeries.Len()
|
||||
}
|
||||
|
||||
// GetValue gets a value at a given index.
|
||||
func (mas *MovingAverageSeries) GetValue(index int) (x float64, y float64) {
|
||||
func (mas *SimpleMovingAverageSeries) GetValue(index int) (x float64, y float64) {
|
||||
if mas.InnerSeries == nil {
|
||||
return
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ func (mas *MovingAverageSeries) GetValue(index int) (x float64, y float64) {
|
|||
|
||||
// GetLastValue computes the last moving average value but walking back window size samples,
|
||||
// and recomputing the last moving average chunk.
|
||||
func (mas MovingAverageSeries) GetLastValue() (x float64, y float64) {
|
||||
func (mas SimpleMovingAverageSeries) GetLastValue() (x float64, y float64) {
|
||||
if mas.InnerSeries == nil {
|
||||
return
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ func (mas MovingAverageSeries) GetLastValue() (x float64, y float64) {
|
|||
}
|
||||
|
||||
// GetWindowSize returns the window size.
|
||||
func (mas MovingAverageSeries) GetWindowSize(defaults ...int) int {
|
||||
func (mas SimpleMovingAverageSeries) GetWindowSize(defaults ...int) int {
|
||||
if mas.WindowSize == 0 {
|
||||
if len(defaults) > 0 {
|
||||
return defaults[0]
|
||||
|
@ -88,7 +88,7 @@ func (mas MovingAverageSeries) GetWindowSize(defaults ...int) int {
|
|||
return mas.WindowSize
|
||||
}
|
||||
|
||||
func (mas MovingAverageSeries) getAverage(valueBuffer *RingBuffer) float64 {
|
||||
func (mas SimpleMovingAverageSeries) getAverage(valueBuffer *RingBuffer) float64 {
|
||||
var accum float64
|
||||
valueBuffer.Each(func(v interface{}) {
|
||||
if typed, isTyped := v.(float64); isTyped {
|
||||
|
@ -99,7 +99,7 @@ func (mas MovingAverageSeries) getAverage(valueBuffer *RingBuffer) float64 {
|
|||
}
|
||||
|
||||
// Render renders the series.
|
||||
func (mas *MovingAverageSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||
func (mas *SimpleMovingAverageSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||
style := mas.Style.WithDefaultsFrom(defaults)
|
||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, mas)
|
||||
}
|
|
@ -21,7 +21,7 @@ func (m mockValueProvider) GetValue(index int) (x, y float64) {
|
|||
return
|
||||
}
|
||||
|
||||
func TestMovingAverageSeriesGetValue(t *testing.T) {
|
||||
func TestSimpleMovingAverageSeriesGetValue(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
mockSeries := mockValueProvider{
|
||||
|
@ -30,7 +30,7 @@ func TestMovingAverageSeriesGetValue(t *testing.T) {
|
|||
}
|
||||
assert.Equal(10, mockSeries.Len())
|
||||
|
||||
mas := &MovingAverageSeries{
|
||||
mas := &SimpleMovingAverageSeries{
|
||||
InnerSeries: mockSeries,
|
||||
WindowSize: 10,
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ func TestMovingAverageSeriesGetValue(t *testing.T) {
|
|||
assert.Equal(6.0, yvalues[8])
|
||||
}
|
||||
|
||||
func TestMovingAverageSeriesGetLastValueWindowOverlap(t *testing.T) {
|
||||
func TestSimpleMovingAverageSeriesGetLastValueWindowOverlap(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
mockSeries := mockValueProvider{
|
||||
|
@ -61,7 +61,7 @@ func TestMovingAverageSeriesGetLastValueWindowOverlap(t *testing.T) {
|
|||
}
|
||||
assert.Equal(10, mockSeries.Len())
|
||||
|
||||
mas := &MovingAverageSeries{
|
||||
mas := &SimpleMovingAverageSeries{
|
||||
InnerSeries: mockSeries,
|
||||
WindowSize: 15,
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ func TestMovingAverageSeriesGetLastValueWindowOverlap(t *testing.T) {
|
|||
assert.Equal(yvalues[len(yvalues)-1], ly)
|
||||
}
|
||||
|
||||
func TestMovingAverageSeriesGetLastValue(t *testing.T) {
|
||||
func TestSimpleMovingAverageSeriesGetLastValue(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
mockSeries := mockValueProvider{
|
||||
|
@ -87,7 +87,7 @@ func TestMovingAverageSeriesGetLastValue(t *testing.T) {
|
|||
}
|
||||
assert.Equal(100, mockSeries.Len())
|
||||
|
||||
mas := &MovingAverageSeries{
|
||||
mas := &SimpleMovingAverageSeries{
|
||||
InnerSeries: mockSeries,
|
||||
WindowSize: 10,
|
||||
}
|
|
@ -60,7 +60,7 @@ func chartHandler(rc *web.RequestContext) web.ControllerResult {
|
|||
},
|
||||
}
|
||||
|
||||
s1ma := &chart.BollingerBandsSeries{
|
||||
s1bb := &chart.BollingerBandsSeries{
|
||||
Name: "BBS",
|
||||
Style: chart.Style{
|
||||
Show: true,
|
||||
|
@ -72,6 +72,24 @@ func chartHandler(rc *web.RequestContext) web.ControllerResult {
|
|||
InnerSeries: s1,
|
||||
}
|
||||
|
||||
s1sma := &chart.SimpleMovingAverageSeries{
|
||||
Style: chart.Style{
|
||||
Show: true,
|
||||
StrokeColor: drawing.ColorRed,
|
||||
StrokeDashArray: []float64{5.0, 5.0},
|
||||
},
|
||||
InnerSeries: s1,
|
||||
}
|
||||
|
||||
s1ema := &chart.ExponentialMovingAverageSeries{
|
||||
Style: chart.Style{
|
||||
Show: true,
|
||||
StrokeColor: drawing.ColorBlue,
|
||||
StrokeDashArray: []float64{5.0, 5.0},
|
||||
},
|
||||
InnerSeries: s1,
|
||||
}
|
||||
|
||||
c := chart.Chart{
|
||||
Title: "A Test Chart",
|
||||
TitleStyle: chart.Style{
|
||||
|
@ -102,9 +120,11 @@ func chartHandler(rc *web.RequestContext) web.ControllerResult {
|
|||
},
|
||||
},
|
||||
Series: []chart.Series{
|
||||
s1bb,
|
||||
s1,
|
||||
s1ma,
|
||||
s1lv,
|
||||
s1sma,
|
||||
s1ema,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue