histogram still needs tweaking.

This commit is contained in:
Will Charczuk 2016-07-17 01:42:31 -07:00
parent d455b775da
commit 7858457772
4 changed files with 233 additions and 1 deletions

View file

@ -1,6 +1,10 @@
package chart
import "github.com/wcharczuk/go-chart/drawing"
import (
"math"
"github.com/wcharczuk/go-chart/drawing"
)
// DrawLineSeries draws a line series with a renderer.
func DrawLineSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style, vs ValueProvider) {
@ -102,6 +106,38 @@ func DrawBoundedSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style,
r.FillStroke()
}
// DrawHistogramSeries draws a value provider as boxes from 0.
func DrawHistogramSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Style, vs ValueProvider, barWidths ...int) {
if vs.Len() == 0 {
return
}
//calculate bar width?
seriesLength := vs.Len()
barWidth := int(math.Floor(float64(xrange.Domain) / float64(seriesLength)))
if len(barWidths) > 0 {
barWidth = barWidths[0]
}
cb := canvasBox.Bottom
cl := canvasBox.Left
//foreach datapoint, draw a box.
for index := 0; index < seriesLength; index++ {
vx, vy := vs.GetValue(index)
y0 := yrange.Translate(0)
x := cl + xrange.Translate(vx)
y := yrange.Translate(vy)
DrawBox(r, Box{
Top: cb - y0,
Left: x - (barWidth >> 1),
Right: x + (barWidth >> 1),
Bottom: cb - y,
}, s)
}
}
// MeasureAnnotation measures how big an annotation would be.
func MeasureAnnotation(r Renderer, canvasBox Box, s Style, lx, ly int, label string) Box {
r.SetFillColor(s.GetFillColor(DefaultAnnotationFillColor))

57
histogram_series.go Normal file
View file

@ -0,0 +1,57 @@
package chart
// HistogramSeries is a special type of series that draws as a histogram.
// Some peculiarities; it will always be lower bounded at 0 (at the very least).
// This may alter ranges a bit and generally you want to put a histogram series on it's own y-axis.
type HistogramSeries struct {
Name string
Style Style
YAxis YAxisType
InnerSeries ValueProvider
}
// GetName implements Series.GetName.
func (hs HistogramSeries) GetName() string {
return hs.Name
}
// GetStyle implements Series.GetStyle.
func (hs HistogramSeries) GetStyle() Style {
return hs.Style
}
// GetYAxis returns which yaxis the series is mapped to.
func (hs HistogramSeries) GetYAxis() YAxisType {
return hs.YAxis
}
// Len implements BoundedValueProvider.Len.
func (hs HistogramSeries) Len() int {
return hs.InnerSeries.Len()
}
// GetValue implements ValueProvider.GetValue.
func (hs HistogramSeries) GetValue(index int) (x, y float64) {
return hs.InnerSeries.GetValue(index)
}
// GetBoundedValue implements BoundedValueProvider.GetBoundedValue
func (hs HistogramSeries) GetBoundedValue(index int) (x, y1, y2 float64) {
vx, vy := hs.InnerSeries.GetValue(index)
x = vx
if vy > 0 {
y1 = vy
return
}
y2 = vy
return
}
// Render implements Series.Render.
func (hs HistogramSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
style := hs.Style.WithDefaultsFrom(defaults)
DrawHistogramSeries(r, canvasBox, xrange, yrange, style, hs)
}

110
macd_series.go Normal file
View file

@ -0,0 +1,110 @@
package chart
const (
// DefaultMACDWindowPrimary is the long window.
DefaultMACDWindowPrimary = 26
// DefaultMACDWindowSecondary is the short window.
DefaultMACDWindowSecondary = 12
)
// MACDSeries (or Moving Average Convergence Divergence) is a special type of series that
// computes the difference between two different EMA values for a given index, as denoted by WindowPrimary(26) and WindowSecondary(12).
type MACDSeries struct {
Name string
Style Style
YAxis YAxisType
InnerSeries ValueProvider
WindowPrimary int
WindowSecondary int
Sigma float64
}
// GetWindows returns the primary and secondary window sizes.
func (macd MACDSeries) GetWindows() (w1, w2 int) {
if macd.WindowPrimary == 0 {
w1 = DefaultMACDWindowPrimary
} else {
w1 = macd.WindowPrimary
}
if macd.WindowSecondary == 0 {
w2 = DefaultMACDWindowSecondary
} else {
w2 = macd.WindowSecondary
}
return
}
// GetSigma returns the smoothing factor for the serise.
func (macd MACDSeries) GetSigma(defaults ...float64) float64 {
if macd.Sigma == 0 {
if len(defaults) > 0 {
return defaults[0]
}
return DefaultExponentialMovingAverageSigma
}
return macd.Sigma
}
// GetName returns the name of the time series.
func (macd MACDSeries) GetName() string {
return macd.Name
}
// GetStyle returns the line style.
func (macd MACDSeries) GetStyle() Style {
return macd.Style
}
// GetYAxis returns which YAxis the series draws on.
func (macd MACDSeries) GetYAxis() YAxisType {
return macd.YAxis
}
// Len returns the number of elements in the series.
func (macd MACDSeries) Len() int {
if macd.InnerSeries == nil {
return 0
}
w1, _ := macd.GetWindows()
innerLen := macd.InnerSeries.Len()
if innerLen > w1 {
return innerLen - w1
}
return 0
}
// GetValue gets a value at a given index.
func (macd MACDSeries) GetValue(index int) (x float64, y float64) {
if macd.InnerSeries == nil {
return
}
w1, w2 := macd.GetWindows()
effectiveIndex := index + w1
x, _ = macd.InnerSeries.GetValue(effectiveIndex)
ema1 := macd.computeEMA(w1, effectiveIndex)
ema2 := macd.computeEMA(w2, effectiveIndex)
y = ema1 - ema2
return
}
func (macd MACDSeries) computeEMA(windowSize int, index int) float64 {
_, v := macd.InnerSeries.GetValue(index)
if windowSize == 1 {
return v
}
sig := macd.GetSigma()
return sig*v + ((1.0 - sig) * macd.computeEMA(windowSize-1, index-1))
}
// Render renders the series.
func (macd MACDSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
style := macd.Style.WithDefaultsFrom(defaults)
DrawLineSeries(r, canvasBox, xrange, yrange, style, macd)
}

29
macd_series_test.go Normal file
View file

@ -0,0 +1,29 @@
package chart
import (
"testing"
"github.com/blendlabs/go-assert"
)
func TestMACDSeries(t *testing.T) {
assert := assert.New(t)
mockSeries := mockValueProvider{
Seq(1.0, 100.0),
SeqRand(100.0, 256),
}
assert.Equal(100, mockSeries.Len())
mas := &MACDSeries{
InnerSeries: mockSeries,
}
var yvalues []float64
for x := 0; x < mas.Len(); x++ {
_, y := mas.GetValue(x)
yvalues = append(yvalues, y)
}
assert.NotEmpty(yvalues)
}