go-chart/candlestick_series.go

158 lines
4.0 KiB
Go
Raw Normal View History

2017-05-13 22:00:35 +02:00
package chart
import (
"fmt"
"time"
2017-05-14 22:34:46 +02:00
"math"
"github.com/wcharczuk/go-chart/util"
2017-05-13 22:00:35 +02:00
)
2017-05-14 22:34:46 +02:00
// CandleValue is a day's data for a candlestick plot.
2017-05-13 22:00:35 +02:00
type CandleValue struct {
Timestamp time.Time
High float64
Low float64
Open float64
Close float64
}
2017-05-15 01:33:48 +02:00
// String returns a string value for the candle value.
func (cv CandleValue) String() string {
return fmt.Sprintf("candle %s high: %.2f low: %.2f open: %.2f close: %.2f", cv.Timestamp.Format("2006-01-02"), cv.High, cv.Low, cv.Open, cv.Close)
}
2017-05-14 22:34:46 +02:00
// IsZero returns if the value is zero or not.
func (cv CandleValue) IsZero() bool {
return cv.Timestamp.IsZero()
}
2017-05-13 22:00:35 +02:00
// CandlestickSeries is a special type of series that takes a norma value provider
// and maps it to day value stats (high, low, open, close).
type CandlestickSeries struct {
2017-05-15 01:33:48 +02:00
Name string
Style Style
YAxis YAxisType
2017-05-17 02:50:17 +02:00
// CandleValues will be used in place of creating them from the `InnerSeries`.
CandleValues []CandleValue
// InnerSeries is used if the `CandleValues` are not set.
InnerSeries ValuesProvider
2017-05-13 22:00:35 +02:00
}
// GetName implements Series.GetName.
2017-05-17 02:50:17 +02:00
func (cs *CandlestickSeries) GetName() string {
2017-05-13 22:00:35 +02:00
return cs.Name
}
// GetStyle implements Series.GetStyle.
2017-05-17 02:50:17 +02:00
func (cs *CandlestickSeries) GetStyle() Style {
2017-05-13 22:00:35 +02:00
return cs.Style
}
// GetYAxis returns which yaxis the series is mapped to.
2017-05-17 02:50:17 +02:00
func (cs *CandlestickSeries) GetYAxis() YAxisType {
2017-05-13 22:00:35 +02:00
return cs.YAxis
}
2017-05-15 01:33:48 +02:00
// Len returns the length of the series.
2017-05-17 02:50:17 +02:00
func (cs *CandlestickSeries) Len() int {
return len(cs.GetCandleValues())
2017-05-15 01:33:48 +02:00
}
2017-05-17 02:50:17 +02:00
// GetBoundedValues returns the bounded values at a given index.
func (cs *CandlestickSeries) GetBoundedValues(index int) (x, y0, y1 float64) {
value := cs.GetCandleValues()[index]
return util.Time.ToFloat64(value.Timestamp), value.Low, value.High
}
2017-05-17 02:50:17 +02:00
// GetCandleValues returns the candle values.
func (cs CandlestickSeries) GetCandleValues() []CandleValue {
if cs.CandleValues == nil {
cs.CandleValues = cs.GenerateCandleValues()
}
return cs.CandleValues
2017-05-15 01:33:48 +02:00
}
2017-05-17 02:50:17 +02:00
// GenerateCandleValues returns the candlestick values for each day represented by the inner series.
func (cs CandlestickSeries) GenerateCandleValues() []CandleValue {
2017-05-17 04:16:26 +02:00
if cs.InnerSeries == nil {
return nil
}
2017-05-17 02:50:17 +02:00
totalValues := cs.InnerSeries.Len()
2017-05-14 22:34:46 +02:00
if totalValues == 0 {
return nil
}
2017-05-13 22:00:35 +02:00
var values []CandleValue
2017-05-14 22:34:46 +02:00
var lastYear, lastMonth, lastDay int
var year, month, day int
var t time.Time
2017-05-17 02:50:17 +02:00
var tv, lv, v float64
2017-05-13 22:00:35 +02:00
2017-05-17 02:50:17 +02:00
tv, v = cs.InnerSeries.GetValues(0)
t = util.Time.FromFloat64(tv)
2017-05-14 22:34:46 +02:00
year, month, day = t.Year(), int(t.Month()), t.Day()
2017-05-15 01:33:48 +02:00
lastYear, lastMonth, lastDay = year, month, day
value := CandleValue{
Timestamp: cs.newTimestamp(year, month, day),
Open: v,
Low: v,
High: v,
}
lv = v
2017-05-14 22:34:46 +02:00
for i := 1; i < totalValues; i++ {
2017-05-17 04:16:26 +02:00
tv, v = cs.InnerSeries.GetValues(i)
2017-05-17 02:50:17 +02:00
t = util.Time.FromFloat64(tv)
2017-05-14 22:34:46 +02:00
year, month, day = t.Year(), int(t.Month()), t.Day()
// if we've transitioned to a new day or we're on the last value
if lastYear != year || lastMonth != month || lastDay != day || i == (totalValues-1) {
value.Close = lv
values = append(values, value)
value = CandleValue{
Timestamp: cs.newTimestamp(year, month, day),
2017-05-15 01:33:48 +02:00
Open: v,
High: v,
Low: v,
2017-05-14 22:34:46 +02:00
}
2017-05-15 01:33:48 +02:00
lastYear = year
lastMonth = month
lastDay = day
} else {
value.Low = math.Min(value.Low, v)
value.High = math.Max(value.High, v)
}
2017-05-14 22:34:46 +02:00
lv = v
2017-05-13 22:00:35 +02:00
}
return values
}
2017-05-14 22:34:46 +02:00
func (cs CandlestickSeries) newTimestamp(year, month, day int) time.Time {
return time.Date(year, time.Month(month), day, 12, 0, 0, 0, util.Date.Eastern())
}
2017-05-13 22:00:35 +02:00
// Render implements Series.Render.
func (cs CandlestickSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
2017-05-15 01:33:48 +02:00
style := cs.Style.InheritFrom(defaults)
Draw.CandlestickSeries(r, canvasBox, xrange, yrange, style, cs)
2017-05-13 22:00:35 +02:00
}
// Validate validates the series.
func (cs CandlestickSeries) Validate() error {
2017-05-17 02:50:17 +02:00
if cs.CandleValues == nil && cs.InnerSeries == nil {
return fmt.Errorf("candlestick series requires either `CandleValues` or `InnerSeries` to be set")
2017-05-13 22:00:35 +02:00
}
return nil
}