fix: series not been updated

This commit is contained in:
zenix 2022-04-08 18:48:33 +09:00
parent be0755d755
commit af61952e40
9 changed files with 187 additions and 82 deletions

View File

@ -11,9 +11,9 @@ type MarketDataStore struct {
Symbol string
// KLineWindows stores all loaded klines per interval
KLineWindows map[types.Interval]types.KLineWindow `json:"-"`
KLineWindows map[types.Interval]*types.KLineWindow `json:"-"`
kLineWindowUpdateCallbacks []func(interval types.Interval, kline types.KLineWindow)
kLineWindowUpdateCallbacks []func(interval types.Interval, klines types.KLineWindow)
}
func NewMarketDataStore(symbol string) *MarketDataStore {
@ -21,16 +21,16 @@ func NewMarketDataStore(symbol string) *MarketDataStore {
Symbol: symbol,
// KLineWindows stores all loaded klines per interval
KLineWindows: make(map[types.Interval]types.KLineWindow, len(types.SupportedIntervals)), // 12 interval, 1m,5m,15m,30m,1h,2h,4h,6h,12h,1d,3d,1w
KLineWindows: make(map[types.Interval]*types.KLineWindow, len(types.SupportedIntervals)), // 12 interval, 1m,5m,15m,30m,1h,2h,4h,6h,12h,1d,3d,1w
}
}
func (store *MarketDataStore) SetKLineWindows(windows map[types.Interval]types.KLineWindow) {
func (store *MarketDataStore) SetKLineWindows(windows map[types.Interval]*types.KLineWindow) {
store.KLineWindows = windows
}
// KLinesOfInterval returns the kline window of the given interval
func (store *MarketDataStore) KLinesOfInterval(interval types.Interval) (kLines types.KLineWindow, ok bool) {
func (store *MarketDataStore) KLinesOfInterval(interval types.Interval) (kLines *types.KLineWindow, ok bool) {
kLines, ok = store.KLineWindows[interval]
return kLines, ok
}
@ -50,14 +50,15 @@ func (store *MarketDataStore) handleKLineClosed(kline types.KLine) {
func (store *MarketDataStore) AddKLine(kline types.KLine) {
window, ok := store.KLineWindows[kline.Interval]
if !ok {
window = make(types.KLineWindow, 0, 1000)
var tmp = make(types.KLineWindow, 0, 1000)
store.KLineWindows[kline.Interval] = &tmp
window = &tmp
}
window.Add(kline)
if len(window) > MaxNumOfKLines {
window = window[MaxNumOfKLinesTruncate-1:]
if len(*window) > MaxNumOfKLines {
*window = (*window)[MaxNumOfKLinesTruncate-1:]
}
store.KLineWindows[kline.Interval] = window
store.EmitKLineWindowUpdate(kline.Interval, window)
store.EmitKLineWindowUpdate(kline.Interval, *window)
}

View File

@ -6,12 +6,12 @@ import (
"github.com/c9s/bbgo/pkg/types"
)
func (store *MarketDataStore) OnKLineWindowUpdate(cb func(interval types.Interval, kline types.KLineWindow)) {
func (store *MarketDataStore) OnKLineWindowUpdate(cb func(interval types.Interval, klines types.KLineWindow)) {
store.kLineWindowUpdateCallbacks = append(store.kLineWindowUpdateCallbacks, cb)
}
func (store *MarketDataStore) EmitKLineWindowUpdate(interval types.Interval, kline types.KLineWindow) {
func (store *MarketDataStore) EmitKLineWindowUpdate(interval types.Interval, klines types.KLineWindow) {
for _, cb := range store.kLineWindowUpdateCallbacks {
cb(interval, kline)
cb(interval, klines)
}
}

View File

@ -42,19 +42,19 @@ type BOLL struct {
type BandType int
func (inc *BOLL) GetUpBand() types.Series {
return inc.UpBand
return &inc.UpBand
}
func (inc *BOLL) GetDownBand() types.Series {
return inc.DownBand
return &inc.DownBand
}
func (inc *BOLL) GetSMA() types.Series {
return inc.SMA
return &inc.SMA
}
func (inc *BOLL) GetStdDev() types.Series {
return inc.StdDev
return &inc.StdDev
}
func (inc *BOLL) LastUpBand() float64 {

View File

@ -45,6 +45,16 @@ func (inc *SMA) Length() int {
var _ types.Series = &SMA{}
func (inc *SMA) Update(value float64) {
length := len(inc.Values)
if length == 0 {
inc.Values = append(inc.Values, value)
return
}
newVal := (inc.Values[length-1]*float64(inc.Window-1) + value) / float64(inc.Window)
inc.Values = append(inc.Values, newVal)
}
func (inc *SMA) calculateAndUpdate(kLines []types.KLine) {
if len(kLines) < inc.Window {
return

View File

@ -84,9 +84,9 @@ func (inc *STOCH) Bind(updater KLineWindowUpdater) {
}
func (inc *STOCH) GetD() types.Series {
return inc.D
return &inc.D
}
func (inc *STOCH) GetK() types.Series {
return inc.K
return &inc.K
}

View File

@ -118,22 +118,28 @@ func (s Float64Slice) Dot(other Float64Slice) float64 {
return s.ElementwiseProduct(other).Sum()
}
func (a Float64Slice) Last() float64 {
if len(a) > 0 {
return a[len(a)-1]
func (a *Float64Slice) Last() float64 {
length := len(*a)
if length > 0 {
return (*a)[length-1]
}
return 0.0
}
func (a Float64Slice) Index(i int) float64 {
if len(a)-i < 0 || i < 0 {
func (a *Float64Slice) Index(i int) float64 {
length := len(*a)
if length-i < 0 || i < 0 {
return 0.0
}
return a[len(a)-i-1]
return (*a)[length-i-1]
}
func (a Float64Slice) Length() int {
return len(a)
func (a *Float64Slice) Length() int {
return len(*a)
}
var _ Series = Float64Slice([]float64{})
func (a Float64Slice) Addr() *Float64Slice {
return &a
}
var _ Series = Float64Slice([]float64{}).Addr()

View File

@ -1,7 +1,9 @@
package types
import (
"fmt"
"math"
"reflect"
"gonum.org/v1/gonum/stat"
)
@ -83,6 +85,25 @@ func Abs(a Series) Series {
var _ Series = &AbsResult{}
func Predict(a Series, lookback int, offset ...int) float64 {
if a.Length() < lookback {
lookback = a.Length()
}
x := make([]float64, lookback, lookback)
y := make([]float64, lookback, lookback)
var weights []float64
for i := 0; i < lookback; i++ {
x[i] = float64(i)
y[i] = a.Index(i)
}
alpha, beta := stat.LinearRegression(x, y, weights, false)
o := -1.0
if len(offset) > 0 {
o = -float64(offset[0])
}
return alpha + beta*o
}
// This will make prediction using Linear Regression to get the next cross point
// Return (offset from latest, crossed value, could cross)
// offset from latest should always be positive
@ -96,14 +117,14 @@ func NextCross(a Series, b Series, lookback int) (int, float64, bool) {
if b.Length() < lookback {
lookback = b.Length()
}
x := make([]float64, 0, lookback)
y1 := make([]float64, 0, lookback)
y2 := make([]float64, 0, lookback)
x := make([]float64, lookback, lookback)
y1 := make([]float64, lookback, lookback)
y2 := make([]float64, lookback, lookback)
var weights []float64
for i := 0; i < lookback; i++ {
x = append(x, float64(i))
y1 = append(y1, a.Index(i))
y2 = append(y2, b.Index(i))
x[i] = float64(i)
y1[i] = a.Index(i)
y2[i] = b.Index(i)
}
alpha1, beta1 := stat.LinearRegression(x, y1, weights, false)
alpha2, beta2 := stat.LinearRegression(x, y2, weights, false)
@ -233,12 +254,18 @@ func Add(a interface{}, b interface{}) Series {
aa = NumberSeries(a.(float64))
case Series:
aa = a.(Series)
default:
panic("input should be either *Series or float64")
}
switch b.(type) {
case float64:
bb = NumberSeries(b.(float64))
case Series:
bb = b.(Series)
default:
panic("input should be either *Series or float64")
}
return &AddSeriesResult{aa, bb}
}
@ -269,21 +296,8 @@ type MinusSeriesResult struct {
// Minus two series, result[i] = a[i] - b[i]
func Minus(a interface{}, b interface{}) Series {
var aa Series
var bb Series
switch a.(type) {
case float64:
aa = NumberSeries(a.(float64))
case Series:
aa = a.(Series)
}
switch b.(type) {
case float64:
bb = NumberSeries(b.(float64))
case Series:
bb = b.(Series)
}
aa := switchIface(a)
bb := switchIface(b)
return &MinusSeriesResult{aa, bb}
}
@ -306,26 +320,34 @@ func (a *MinusSeriesResult) Length() int {
var _ Series = &MinusSeriesResult{}
// Divid two series, result[i] = a[i] / b[i]
func Div(a interface{}, b interface{}) Series {
var aa Series
var bb Series
switch a.(type) {
case float64:
aa = NumberSeries(a.(float64))
case Series:
aa = a.(Series)
}
func switchIface(b interface{}) Series {
switch b.(type) {
case float64:
bb = NumberSeries(b.(float64))
if 0 == bb.Last() {
panic("Divid by zero exception")
}
return NumberSeries(b.(float64))
case int32:
return NumberSeries(float64(b.(int32)))
case int64:
return NumberSeries(float64(b.(int64)))
case float32:
return NumberSeries(float64(b.(float32)))
case int:
return NumberSeries(float64(b.(int)))
case Series:
bb = b.(Series)
return b.(Series)
default:
fmt.Println(reflect.TypeOf(b))
panic("input should be either *Series or float64")
}
}
// Divid two series, result[i] = a[i] / b[i]
func Div(a interface{}, b interface{}) Series {
aa := switchIface(a)
if 0 == b {
panic("Divid by zero exception")
}
bb := switchIface(b)
return &DivSeriesResult{aa, bb}
}
@ -364,12 +386,17 @@ func Mul(a interface{}, b interface{}) Series {
aa = NumberSeries(a.(float64))
case Series:
aa = a.(Series)
default:
panic("input should be either Series or float64")
}
switch b.(type) {
case float64:
bb = NumberSeries(b.(float64))
case Series:
bb = b.(Series)
default:
panic("input should be either Series or float64")
}
return &MulSeriesResult{aa, bb}
@ -443,3 +470,43 @@ func ToReverseArray(a Series, limit ...int) (result Float64Slice) {
}
return
}
type ChangeResult struct {
a Series
offset int
}
func (c *ChangeResult) Last() float64 {
if c.offset >= c.a.Length() {
return 0
}
return c.a.Last() - c.a.Index(c.offset)
}
func (c *ChangeResult) Index(i int) float64 {
if i+c.offset >= c.a.Length() {
return 0
}
return c.a.Index(i) - c.a.Index(i+c.offset)
}
func (c *ChangeResult) Length() int {
length := c.a.Length()
if length >= c.offset {
return length - c.offset
}
return 0
}
// Difference between current value and previous, a - a[offset]
// offset: if not given, offset is 1.
func Change(a Series, offset ...int) Series {
o := 1
if len(offset) == 0 {
o = offset[0]
}
return &ChangeResult{a, o}
}
// TODO: ta.linreg

View File

@ -14,7 +14,7 @@ func TestFloat(t *testing.T) {
func TestNextCross(t *testing.T) {
var a Series = NumberSeries(1.2)
var b Series = Float64Slice{100., 80., 60.}
var b Series = &Float64Slice{100., 80., 60.}
// index 2 1 0
// predicted 40 20 0
// offset 1 2 3
@ -24,3 +24,12 @@ func TestNextCross(t *testing.T) {
assert.Equal(t, value, 1.2)
assert.Equal(t, index, 3) // 2.94, ceil
}
func TestFloat64Slice(t *testing.T) {
var a = Float64Slice{1.0, 2.0, 3.0}
var b = Float64Slice{1.0, 2.0, 3.0}
var c Series = Minus(&a, &b)
a = append(a, 4.0)
b = append(b, 3.0)
assert.Equal(t, c.Last(), 1.)
}

View File

@ -516,76 +516,88 @@ const (
kCloseValue
kHighValue
kLowValue
kVolumeValue
)
func (k KLineWindow) High() Series {
func (k *KLineWindow) High() Series {
return &KLineSeries{
lines: k,
kv: kHighValue,
}
}
func (k KLineWindow) Low() Series {
func (k *KLineWindow) Low() Series {
return &KLineSeries{
lines: k,
kv: kLowValue,
}
}
func (k KLineWindow) Open() Series {
func (k *KLineWindow) Open() Series {
return &KLineSeries{
lines: k,
kv: kOpenValue,
}
}
func (k KLineWindow) Close() Series {
func (k *KLineWindow) Close() Series {
return &KLineSeries{
lines: k,
kv: kCloseValue,
}
}
func (k *KLineWindow) Volume() Series {
return &KLineSeries{
lines: k,
kv: kVolumeValue,
}
}
type KLineSeries struct {
lines []KLine
lines *KLineWindow
kv KValueType
}
func (k *KLineSeries) Last() float64 {
length := len(k.lines)
length := len(*k.lines)
switch k.kv {
case kOpenValue:
return k.lines[length-1].GetOpen().Float64()
return (*k.lines)[length-1].GetOpen().Float64()
case kCloseValue:
return k.lines[length-1].GetClose().Float64()
return (*k.lines)[length-1].GetClose().Float64()
case kLowValue:
return k.lines[length-1].GetLow().Float64()
return (*k.lines)[length-1].GetLow().Float64()
case kHighValue:
return k.lines[length-1].GetHigh().Float64()
return (*k.lines)[length-1].GetHigh().Float64()
case kVolumeValue:
return (*k.lines)[length-1].Volume.Float64()
}
return 0
}
func (k *KLineSeries) Index(i int) float64 {
length := len(k.lines)
length := len(*k.lines)
if length == 0 || length-i-1 < 0 {
return 0
}
switch k.kv {
case kOpenValue:
return k.lines[length-i-1].GetOpen().Float64()
return (*k.lines)[length-i-1].GetOpen().Float64()
case kCloseValue:
return k.lines[length-i-1].GetClose().Float64()
return (*k.lines)[length-i-1].GetClose().Float64()
case kLowValue:
return k.lines[length-i-1].GetLow().Float64()
return (*k.lines)[length-i-1].GetLow().Float64()
case kHighValue:
return k.lines[length-i-1].GetHigh().Float64()
return (*k.lines)[length-i-1].GetHigh().Float64()
case kVolumeValue:
return (*k.lines)[length-i-1].Volume.Float64()
}
return 0
}
func (k *KLineSeries) Length() int {
return len(k.lines)
return len(*k.lines)
}
var _ Series = &KLineSeries{}