updates + tests

This commit is contained in:
Will Charczuk 2018-09-07 12:52:30 -07:00
parent 1144b80a46
commit 1555902fc4
13 changed files with 207 additions and 2 deletions

View file

@ -7,6 +7,11 @@ import (
util "github.com/wcharczuk/go-chart/util" util "github.com/wcharczuk/go-chart/util"
) )
// Interface Assertions.
var (
_ Series = (*AnnotationSeries)(nil)
)
// AnnotationSeries is a series of labels on the chart. // AnnotationSeries is a series of labels on the chart.
type AnnotationSeries struct { type AnnotationSeries struct {
Name string Name string

View file

@ -6,6 +6,11 @@ import (
"github.com/wcharczuk/go-chart/seq" "github.com/wcharczuk/go-chart/seq"
) )
// Interface Assertions.
var (
_ Series = (*BollingerBandsSeries)(nil)
)
// BollingerBandsSeries draws bollinger bands for an inner series. // BollingerBandsSeries draws bollinger bands for an inner series.
// Bollinger bands are defined by two lines, one at SMA+k*stddev, one at SMA-k*stdev. // Bollinger bands are defined by two lines, one at SMA+k*stddev, one at SMA-k*stdev.
type BollingerBandsSeries struct { type BollingerBandsSeries struct {

View file

@ -2,6 +2,13 @@ package chart
import "fmt" import "fmt"
// Interface Assertions.
var (
_ Series = (*ContinuousSeries)(nil)
_ FirstValuesProvider = (*ContinuousSeries)(nil)
_ LastValuesProvider = (*ContinuousSeries)(nil)
)
// ContinuousSeries represents a line on a chart. // ContinuousSeries represents a line on a chart.
type ContinuousSeries struct { type ContinuousSeries struct {
Name string Name string
@ -36,6 +43,11 @@ func (cs ContinuousSeries) GetValues(index int) (float64, float64) {
return cs.XValues[index], cs.YValues[index] return cs.XValues[index], cs.YValues[index]
} }
// GetFirstValues gets the first x,y values.
func (cs ContinuousSeries) GetFirstValues() (float64, float64) {
return cs.XValues[0], cs.YValues[0]
}
// GetLastValues gets the last x,y values. // GetLastValues gets the last x,y values.
func (cs ContinuousSeries) GetLastValues() (float64, float64) { func (cs ContinuousSeries) GetLastValues() (float64, float64) {
return cs.XValues[len(cs.XValues)-1], cs.YValues[len(cs.YValues)-1] return cs.XValues[len(cs.XValues)-1], cs.YValues[len(cs.YValues)-1]

View file

@ -7,6 +7,13 @@ const (
DefaultEMAPeriod = 12 DefaultEMAPeriod = 12
) )
// Interface Assertions.
var (
_ Series = (*EMASeries)(nil)
_ FirstValuesProvider = (*EMASeries)(nil)
_ LastValuesProvider = (*EMASeries)(nil)
)
// EMASeries is a computed series. // EMASeries is a computed series.
type EMASeries struct { type EMASeries struct {
Name string Name string
@ -66,6 +73,19 @@ func (ema *EMASeries) GetValues(index int) (x, y float64) {
return return
} }
// GetFirstValues computes the first moving average value.
func (ema *EMASeries) GetFirstValues() (x, y float64) {
if ema.InnerSeries == nil {
return
}
if len(ema.cache) == 0 {
ema.ensureCachedValues()
}
x, _ = ema.InnerSeries.GetValues(0)
y = ema.cache[0]
return
}
// GetLastValues computes the last moving average value but walking back window size samples, // GetLastValues computes the last moving average value but walking back window size samples,
// and recomputing the last moving average chunk. // and recomputing the last moving average chunk.
func (ema *EMASeries) GetLastValues() (x, y float64) { func (ema *EMASeries) GetLastValues() (x, y float64) {

37
first_value_annotation.go Normal file
View file

@ -0,0 +1,37 @@
package chart
import "fmt"
// FirstValueAnnotation returns an annotation series of just the first value of a value provider as an annotation.
func FirstValueAnnotation(innerSeries ValuesProvider, vfs ...ValueFormatter) AnnotationSeries {
var vf ValueFormatter
if len(vfs) > 0 {
vf = vfs[0]
} else if typed, isTyped := innerSeries.(ValueFormatterProvider); isTyped {
_, vf = typed.GetValueFormatters()
} else {
vf = FloatValueFormatter
}
var firstValue Value2
if typed, isTyped := innerSeries.(FirstValuesProvider); isTyped {
firstValue.XValue, firstValue.YValue = typed.GetFirstValues()
firstValue.Label = vf(firstValue.YValue)
} else {
firstValue.XValue, firstValue.YValue = innerSeries.GetValues(0)
firstValue.Label = vf(firstValue.YValue)
}
var seriesName string
var seriesStyle Style
if typed, isTyped := innerSeries.(Series); isTyped {
seriesName = fmt.Sprintf("%s - First Value", typed.GetName())
seriesStyle = typed.GetStyle()
}
return AnnotationSeries{
Name: seriesName,
Style: seriesStyle,
Annotations: []Value2{firstValue},
}
}

View file

@ -0,0 +1,22 @@
package chart
import (
"testing"
"github.com/blend/go-sdk/assert"
)
func TestFirstValueAnnotation(t *testing.T) {
assert := assert.New(t)
series := ContinuousSeries{
XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
YValues: []float64{5.0, 3.0, 3.0, 2.0, 1.0},
}
fva := FirstValueAnnotation(series)
assert.NotEmpty(fva.Annotations)
fvaa := fva.Annotations[0]
assert.Equal(1, fvaa.XValue)
assert.Equal(5, fvaa.YValue)
}

View file

@ -0,0 +1,22 @@
package chart
import (
"testing"
"github.com/blend/go-sdk/assert"
)
func TestLastValueAnnotation(t *testing.T) {
assert := assert.New(t)
series := ContinuousSeries{
XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
YValues: []float64{5.0, 3.0, 3.0, 2.0, 1.0},
}
lva := LastValueAnnotation(series)
assert.NotEmpty(lva.Annotations)
lvaa := lva.Annotations[0]
assert.Equal(5, lvaa.XValue)
assert.Equal(1, lvaa.YValue)
}

View file

@ -7,6 +7,13 @@ import (
util "github.com/wcharczuk/go-chart/util" util "github.com/wcharczuk/go-chart/util"
) )
// Interface Assertions.
var (
_ Series = (*LinearRegressionSeries)(nil)
_ FirstValuesProvider = (*LinearRegressionSeries)(nil)
_ LastValuesProvider = (*LinearRegressionSeries)(nil)
)
// LinearRegressionSeries is a series that plots the n-nearest neighbors // LinearRegressionSeries is a series that plots the n-nearest neighbors
// linear regression for the values. // linear regression for the values.
type LinearRegressionSeries struct { type LinearRegressionSeries struct {
@ -82,6 +89,19 @@ func (lrs *LinearRegressionSeries) GetValues(index int) (x, y float64) {
return return
} }
// GetFirstValues computes the first linear regression value.
func (lrs *LinearRegressionSeries) GetFirstValues() (x, y float64) {
if lrs.InnerSeries == nil || lrs.InnerSeries.Len() == 0 {
return
}
if lrs.m == 0 && lrs.b == 0 {
lrs.computeCoefficients()
}
x, y = lrs.InnerSeries.GetValues(0)
y = (lrs.m * lrs.normalize(x)) + lrs.b
return
}
// GetLastValues computes the last linear regression value. // GetLastValues computes the last linear regression value.
func (lrs *LinearRegressionSeries) GetLastValues() (x, y float64) { func (lrs *LinearRegressionSeries) GetLastValues() (x, y float64) {
if lrs.InnerSeries == nil || lrs.InnerSeries.Len() == 0 { if lrs.InnerSeries == nil || lrs.InnerSeries.Len() == 0 {

View file

@ -8,6 +8,13 @@ import (
util "github.com/wcharczuk/go-chart/util" util "github.com/wcharczuk/go-chart/util"
) )
// Interface Assertions.
var (
_ Series = (*PolynomialRegressionSeries)(nil)
_ FirstValuesProvider = (*PolynomialRegressionSeries)(nil)
_ LastValuesProvider = (*PolynomialRegressionSeries)(nil)
)
// PolynomialRegressionSeries implements a polynomial regression over a given // PolynomialRegressionSeries implements a polynomial regression over a given
// inner series. // inner series.
type PolynomialRegressionSeries struct { type PolynomialRegressionSeries struct {
@ -101,6 +108,23 @@ func (prs *PolynomialRegressionSeries) GetValues(index int) (x, y float64) {
return return
} }
// GetFirstValues computes the first poly regression value.
func (prs *PolynomialRegressionSeries) GetFirstValues() (x, y float64) {
if prs.InnerSeries == nil || prs.InnerSeries.Len() == 0 {
return
}
if prs.coeffs == nil {
coeffs, err := prs.computeCoefficients()
if err != nil {
panic(err)
}
prs.coeffs = coeffs
}
x, y = prs.InnerSeries.GetValues(0)
y = prs.apply(x)
return
}
// GetLastValues computes the last poly regression value. // GetLastValues computes the last poly regression value.
func (prs *PolynomialRegressionSeries) GetLastValues() (x, y float64) { func (prs *PolynomialRegressionSeries) GetLastValues() (x, y float64) {
if prs.InnerSeries == nil || prs.InnerSeries.Len() == 0 { if prs.InnerSeries == nil || prs.InnerSeries.Len() == 0 {

View file

@ -11,6 +11,13 @@ const (
DefaultSimpleMovingAveragePeriod = 16 DefaultSimpleMovingAveragePeriod = 16
) )
// Interface Assertions.
var (
_ Series = (*SMASeries)(nil)
_ FirstValuesProvider = (*SMASeries)(nil)
_ LastValuesProvider = (*SMASeries)(nil)
)
// SMASeries is a computed series. // SMASeries is a computed series.
type SMASeries struct { type SMASeries struct {
Name string Name string
@ -63,6 +70,17 @@ func (sma SMASeries) GetValues(index int) (x, y float64) {
return return
} }
// GetFirstValues computes the first moving average value.
func (sma SMASeries) GetFirstValues() (x, y float64) {
if sma.InnerSeries == nil || sma.InnerSeries.Len() == 0 {
return
}
px, _ := sma.InnerSeries.GetValues(0)
x = px
y = sma.getAverage(0)
return
}
// GetLastValues computes the last moving average value but walking back window size samples, // GetLastValues computes the last moving average value but walking back window size samples,
// and recomputing the last moving average chunk. // and recomputing the last moving average chunk.
func (sma SMASeries) GetLastValues() (x, y float64) { func (sma SMASeries) GetLastValues() (x, y float64) {

View file

@ -7,6 +7,14 @@ import (
util "github.com/wcharczuk/go-chart/util" util "github.com/wcharczuk/go-chart/util"
) )
// Interface Assertions.
var (
_ Series = (*TimeSeries)(nil)
_ FirstValuesProvider = (*TimeSeries)(nil)
_ LastValuesProvider = (*TimeSeries)(nil)
_ ValueFormatterProvider = (*TimeSeries)(nil)
)
// TimeSeries is a line on a chart. // TimeSeries is a line on a chart.
type TimeSeries struct { type TimeSeries struct {
Name string Name string
@ -33,14 +41,21 @@ func (ts TimeSeries) Len() int {
return len(ts.XValues) return len(ts.XValues)
} }
// GetValues gets a value at a given index. // GetValues gets x, y values at a given index.
func (ts TimeSeries) GetValues(index int) (x, y float64) { func (ts TimeSeries) GetValues(index int) (x, y float64) {
x = util.Time.ToFloat64(ts.XValues[index]) x = util.Time.ToFloat64(ts.XValues[index])
y = ts.YValues[index] y = ts.YValues[index]
return return
} }
// GetLastValues gets the last value. // GetFirstValues gets the first values.
func (ts TimeSeries) GetFirstValues() (x, y float64) {
x = util.Time.ToFloat64(ts.XValues[0])
y = ts.YValues[0]
return
}
// GetLastValues gets the last values.
func (ts TimeSeries) GetLastValues() (x, y float64) { func (ts TimeSeries) GetLastValues() (x, y float64) {
x = util.Time.ToFloat64(ts.XValues[len(ts.XValues)-1]) x = util.Time.ToFloat64(ts.XValues[len(ts.XValues)-1])
y = ts.YValues[len(ts.YValues)-1] y = ts.YValues[len(ts.YValues)-1]

View file

@ -14,6 +14,11 @@ type BoundedValuesProvider interface {
GetBoundedValues(index int) (x, y1, y2 float64) GetBoundedValues(index int) (x, y1, y2 float64)
} }
// FirstValuesProvider is a special type of value provider that can return it's (potentially computed) first value.
type FirstValuesProvider interface {
GetFirstValues() (x, y float64)
}
// LastValuesProvider is a special type of value provider that can return it's (potentially computed) last value. // LastValuesProvider is a special type of value provider that can return it's (potentially computed) last value.
type LastValuesProvider interface { type LastValuesProvider interface {
GetLastValues() (x, y float64) GetLastValues() (x, y float64)