updates + tests
This commit is contained in:
parent
1144b80a46
commit
1555902fc4
13 changed files with 207 additions and 2 deletions
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
37
first_value_annotation.go
Normal 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},
|
||||||
|
}
|
||||||
|
}
|
22
first_value_annotation_test.go
Normal file
22
first_value_annotation_test.go
Normal 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)
|
||||||
|
}
|
22
last_value_annotation_test.go
Normal file
22
last_value_annotation_test.go
Normal 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)
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue