Merge pull request #1228 from c9s/c9s/move-v2-indicator

REFACTOR: move v2 indicators to the indicator/v2 package
This commit is contained in:
c9s 2023-07-10 17:44:16 +08:00 committed by GitHub
commit c640d5c132
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 443 additions and 396 deletions

View File

@ -3,7 +3,7 @@ package bbgo
import ( import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/indicator" "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
@ -16,8 +16,8 @@ type IndicatorSet struct {
store *MarketDataStore store *MarketDataStore
// caches // caches
kLines map[types.Interval]*indicator.KLineStream kLines map[types.Interval]*indicatorv2.KLineStream
closePrices map[types.Interval]*indicator.PriceStream closePrices map[types.Interval]*indicatorv2.PriceStream
} }
func NewIndicatorSet(symbol string, stream types.Stream, store *MarketDataStore) *IndicatorSet { func NewIndicatorSet(symbol string, stream types.Stream, store *MarketDataStore) *IndicatorSet {
@ -26,17 +26,17 @@ func NewIndicatorSet(symbol string, stream types.Stream, store *MarketDataStore)
store: store, store: store,
stream: stream, stream: stream,
kLines: make(map[types.Interval]*indicator.KLineStream), kLines: make(map[types.Interval]*indicatorv2.KLineStream),
closePrices: make(map[types.Interval]*indicator.PriceStream), closePrices: make(map[types.Interval]*indicatorv2.PriceStream),
} }
} }
func (i *IndicatorSet) KLines(interval types.Interval) *indicator.KLineStream { func (i *IndicatorSet) KLines(interval types.Interval) *indicatorv2.KLineStream {
if kLines, ok := i.kLines[interval]; ok { if kLines, ok := i.kLines[interval]; ok {
return kLines return kLines
} }
kLines := indicator.KLines(i.stream, i.Symbol, interval) kLines := indicatorv2.KLines(i.stream, i.Symbol, interval)
if kLinesWindow, ok := i.store.KLinesOfInterval(interval); ok { if kLinesWindow, ok := i.store.KLinesOfInterval(interval); ok {
kLines.BackFill(*kLinesWindow) kLines.BackFill(*kLinesWindow)
} else { } else {
@ -47,60 +47,60 @@ func (i *IndicatorSet) KLines(interval types.Interval) *indicator.KLineStream {
return kLines return kLines
} }
func (i *IndicatorSet) OPEN(interval types.Interval) *indicator.PriceStream { func (i *IndicatorSet) OPEN(interval types.Interval) *indicatorv2.PriceStream {
return indicator.OpenPrices(i.KLines(interval)) return indicatorv2.OpenPrices(i.KLines(interval))
} }
func (i *IndicatorSet) HIGH(interval types.Interval) *indicator.PriceStream { func (i *IndicatorSet) HIGH(interval types.Interval) *indicatorv2.PriceStream {
return indicator.HighPrices(i.KLines(interval)) return indicatorv2.HighPrices(i.KLines(interval))
} }
func (i *IndicatorSet) LOW(interval types.Interval) *indicator.PriceStream { func (i *IndicatorSet) LOW(interval types.Interval) *indicatorv2.PriceStream {
return indicator.LowPrices(i.KLines(interval)) return indicatorv2.LowPrices(i.KLines(interval))
} }
func (i *IndicatorSet) CLOSE(interval types.Interval) *indicator.PriceStream { func (i *IndicatorSet) CLOSE(interval types.Interval) *indicatorv2.PriceStream {
if closePrices, ok := i.closePrices[interval]; ok { if closePrices, ok := i.closePrices[interval]; ok {
return closePrices return closePrices
} }
closePrices := indicator.ClosePrices(i.KLines(interval)) closePrices := indicatorv2.ClosePrices(i.KLines(interval))
i.closePrices[interval] = closePrices i.closePrices[interval] = closePrices
return closePrices return closePrices
} }
func (i *IndicatorSet) VOLUME(interval types.Interval) *indicator.PriceStream { func (i *IndicatorSet) VOLUME(interval types.Interval) *indicatorv2.PriceStream {
return indicator.Volumes(i.KLines(interval)) return indicatorv2.Volumes(i.KLines(interval))
} }
func (i *IndicatorSet) RSI(iw types.IntervalWindow) *indicator.RSIStream { func (i *IndicatorSet) RSI(iw types.IntervalWindow) *indicatorv2.RSIStream {
return indicator.RSI2(i.CLOSE(iw.Interval), iw.Window) return indicatorv2.RSI2(i.CLOSE(iw.Interval), iw.Window)
} }
func (i *IndicatorSet) EMA(iw types.IntervalWindow) *indicator.EWMAStream { func (i *IndicatorSet) EMA(iw types.IntervalWindow) *indicatorv2.EWMAStream {
return i.EWMA(iw) return i.EWMA(iw)
} }
func (i *IndicatorSet) EWMA(iw types.IntervalWindow) *indicator.EWMAStream { func (i *IndicatorSet) EWMA(iw types.IntervalWindow) *indicatorv2.EWMAStream {
return indicator.EWMA2(i.CLOSE(iw.Interval), iw.Window) return indicatorv2.EWMA2(i.CLOSE(iw.Interval), iw.Window)
} }
func (i *IndicatorSet) STOCH(iw types.IntervalWindow, dPeriod int) *indicator.StochStream { func (i *IndicatorSet) STOCH(iw types.IntervalWindow, dPeriod int) *indicatorv2.StochStream {
return indicator.Stoch2(i.KLines(iw.Interval), iw.Window, dPeriod) return indicatorv2.Stoch(i.KLines(iw.Interval), iw.Window, dPeriod)
} }
func (i *IndicatorSet) BOLL(iw types.IntervalWindow, k float64) *indicator.BOLLStream { func (i *IndicatorSet) BOLL(iw types.IntervalWindow, k float64) *indicatorv2.BOLLStream {
return indicator.BOLL2(i.CLOSE(iw.Interval), iw.Window, k) return indicatorv2.BOLL(i.CLOSE(iw.Interval), iw.Window, k)
} }
func (i *IndicatorSet) MACD(interval types.Interval, shortWindow, longWindow, signalWindow int) *indicator.MACDStream { func (i *IndicatorSet) MACD(interval types.Interval, shortWindow, longWindow, signalWindow int) *indicatorv2.MACDStream {
return indicator.MACD2(i.CLOSE(interval), shortWindow, longWindow, signalWindow) return indicatorv2.MACD2(i.CLOSE(interval), shortWindow, longWindow, signalWindow)
} }
func (i *IndicatorSet) ATR(interval types.Interval, window int) *indicator.ATRStream { func (i *IndicatorSet) ATR(interval types.Interval, window int) *indicatorv2.ATRStream {
return indicator.ATR2(i.KLines(interval), window) return indicatorv2.ATR2(i.KLines(interval), window)
} }
func (i *IndicatorSet) ATRP(interval types.Interval, window int) *indicator.ATRPStream { func (i *IndicatorSet) ATRP(interval types.Interval, window int) *indicatorv2.ATRPStream {
return indicator.ATRP2(i.KLines(interval), window) return indicatorv2.ATRP2(i.KLines(interval), window)
} }

View File

@ -76,9 +76,9 @@ func (inc *EWMA) PushK(k types.KLine) {
inc.EmitUpdate(inc.Last(0)) inc.EmitUpdate(inc.Last(0))
} }
func CalculateKLinesEMA(allKLines []types.KLine, priceF KLineValueMapper, window int) float64 { func CalculateKLinesEMA(allKLines []types.KLine, priceF types.KLineValueMapper, window int) float64 {
var multiplier = 2.0 / (float64(window) + 1) var multiplier = 2.0 / (float64(window) + 1)
return ewma(MapKLinePrice(allKLines, priceF), multiplier) return ewma(types.MapKLinePrice(allKLines, priceF), multiplier)
} }
// see https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp // see https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp

View File

@ -1027,7 +1027,7 @@ func buildKLines(prices []fixedpoint.Value) (klines []types.KLine) {
func Test_calculateEWMA(t *testing.T) { func Test_calculateEWMA(t *testing.T) {
type args struct { type args struct {
allKLines []types.KLine allKLines []types.KLine
priceF KLineValueMapper priceF types.KLineValueMapper
window int window int
} }
var input []fixedpoint.Value var input []fixedpoint.Value
@ -1043,7 +1043,7 @@ func Test_calculateEWMA(t *testing.T) {
name: "ETHUSDT EMA 7", name: "ETHUSDT EMA 7",
args: args{ args: args{
allKLines: buildKLines(input), allKLines: buildKLines(input),
priceF: KLineClosePriceMapper, priceF: types.KLineClosePriceMapper,
window: 7, window: 7,
}, },
want: 571.72, // with open price, binance desktop returns 571.45, trading view returns 570.8957, for close price, binance mobile returns 571.72 want: 571.72, // with open price, binance desktop returns 571.45, trading view returns 570.8957, for close price, binance mobile returns 571.72
@ -1052,7 +1052,7 @@ func Test_calculateEWMA(t *testing.T) {
name: "ETHUSDT EMA 25", name: "ETHUSDT EMA 25",
args: args{ args: args{
allKLines: buildKLines(input), allKLines: buildKLines(input),
priceF: KLineClosePriceMapper, priceF: types.KLineClosePriceMapper,
window: 25, window: 25,
}, },
want: 571.30, want: 571.30,
@ -1061,7 +1061,7 @@ func Test_calculateEWMA(t *testing.T) {
name: "ETHUSDT EMA 99", name: "ETHUSDT EMA 99",
args: args{ args: args{
allKLines: buildKLines(input), allKLines: buildKLines(input),
priceF: KLineClosePriceMapper, priceF: types.KLineClosePriceMapper,
window: 99, window: 99,
}, },
want: 577.62, // binance mobile uses 577.58 want: 577.62, // binance mobile uses 577.58

View File

@ -1,15 +0,0 @@
// Code generated by "callbackgen -type Float64Updater"; DO NOT EDIT.
package indicator
import ()
func (f *Float64Updater) OnUpdate(cb func(v float64)) {
f.updateCallbacks = append(f.updateCallbacks, cb)
}
func (f *Float64Updater) EmitUpdate(v float64) {
for _, cb := range f.updateCallbacks {
cb(v)
}
}

View File

@ -6070,7 +6070,7 @@ func Test_GHFilter(t *testing.T) {
func Test_GHFilterEstimationAccurate(t *testing.T) { func Test_GHFilterEstimationAccurate(t *testing.T) {
type args struct { type args struct {
allKLines []types.KLine allKLines []types.KLine
priceF KLineValueMapper priceF types.KLineValueMapper
window int window int
} }
var klines []types.KLine var klines []types.KLine

View File

@ -1,33 +0,0 @@
package indicator
import "github.com/c9s/bbgo/pkg/types"
type KLineValueMapper func(k types.KLine) float64
func KLineOpenPriceMapper(k types.KLine) float64 {
return k.Open.Float64()
}
func KLineClosePriceMapper(k types.KLine) float64 {
return k.Close.Float64()
}
func KLineTypicalPriceMapper(k types.KLine) float64 {
return (k.High.Float64() + k.Low.Float64() + k.Close.Float64()) / 3.
}
func KLinePriceVolumeMapper(k types.KLine) float64 {
return k.Close.Mul(k.Volume).Float64()
}
func KLineVolumeMapper(k types.KLine) float64 {
return k.Volume.Float64()
}
func MapKLinePrice(kLines []types.KLine, f KLineValueMapper) (prices []float64) {
for _, k := range kLines {
prices = append(prices, f(k))
}
return prices
}

View File

@ -52,7 +52,7 @@ func (inc *Pivot) CalculateAndUpdate(klines []types.KLine) {
recentT := klines[end-(inc.Window-1) : end+1] recentT := klines[end-(inc.Window-1) : end+1]
l, h, err := calculatePivot(recentT, inc.Window, KLineLowPriceMapper, KLineHighPriceMapper) l, h, err := calculatePivot(recentT, inc.Window, types.KLineLowPriceMapper, types.KLineHighPriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate pivots") log.WithError(err).Error("can not calculate pivots")
return return
@ -90,7 +90,7 @@ func (inc *Pivot) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate) updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
} }
func calculatePivot(klines []types.KLine, window int, valLow KLineValueMapper, valHigh KLineValueMapper) (float64, float64, error) { func calculatePivot(klines []types.KLine, window int, valLow types.KLineValueMapper, valHigh types.KLineValueMapper) (float64, float64, error) {
length := len(klines) length := len(klines)
if length == 0 || length < window { if length == 0 || length < window {
return 0., 0., fmt.Errorf("insufficient elements for calculating with window = %d", window) return 0., 0., fmt.Errorf("insufficient elements for calculating with window = %d", window)
@ -115,11 +115,3 @@ func calculatePivot(klines []types.KLine, window int, valLow KLineValueMapper, v
return pl, ph, nil return pl, ph, nil
} }
func KLineLowPriceMapper(k types.KLine) float64 {
return k.Low.Float64()
}
func KLineHighPriceMapper(k types.KLine) float64 {
return k.High.Float64()
}

View File

@ -1,22 +0,0 @@
package indicator
import "github.com/c9s/bbgo/pkg/types"
type Float64Calculator interface {
Calculate(x float64) float64
PushAndEmit(x float64)
}
type Float64Source interface {
types.Series
OnUpdate(f func(v float64))
}
type Float64Subscription interface {
types.Series
AddSubscriber(f func(v float64))
}
type Float64Truncator interface {
Truncate()
}

View File

@ -7,7 +7,7 @@ func max(x, y int) int {
return y return y
} }
func min(x, y int) int { func Min(x, y int) int {
if x < y { if x < y {
return x return x
} }

View File

@ -1,19 +0,0 @@
package indicator
/*
NEW INDICATOR DESIGN:
klines := kLines(marketDataStream)
closePrices := closePrices(klines)
macd := MACD(klines, {Fast: 12, Slow: 10})
equals to:
klines := KLines(marketDataStream)
closePrices := ClosePrice(klines)
fastEMA := EMA(closePrices, 7)
slowEMA := EMA(closePrices, 25)
macd := Subtract(fastEMA, slowEMA)
signal := EMA(macd, 16)
histogram := Subtract(macd, signal)
*/

View File

@ -1,4 +1,4 @@
package indicator package indicatorv2
type ATRStream struct { type ATRStream struct {
// embedded struct // embedded struct

View File

@ -1,4 +1,4 @@
package indicator package indicatorv2
import ( import (
"encoding/json" "encoding/json"

View File

@ -1,12 +1,14 @@
package indicator package indicatorv2
import "github.com/c9s/bbgo/pkg/types"
type ATRPStream struct { type ATRPStream struct {
*Float64Series *types.Float64Series
} }
func ATRP2(source KLineSubscription, window int) *ATRPStream { func ATRP2(source KLineSubscription, window int) *ATRPStream {
s := &ATRPStream{ s := &ATRPStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
} }
tr := TR2(source) tr := TR2(source)
atr := RMA2(tr, window, true) atr := RMA2(tr, window, true)

View File

@ -1,10 +1,14 @@
package indicator package indicatorv2
import (
"github.com/c9s/bbgo/pkg/types"
)
type BOLLStream struct { type BOLLStream struct {
// the band series // the band series
*Float64Series *types.Float64Series
UpBand, DownBand *Float64Series UpBand, DownBand *types.Float64Series
window int window int
k float64 k float64
@ -20,15 +24,15 @@ type BOLLStream struct {
// //
// -> calculate SMA // -> calculate SMA
// -> calculate stdDev -> calculate bandWidth -> get latest SMA -> upBand, downBand // -> calculate stdDev -> calculate bandWidth -> get latest SMA -> upBand, downBand
func BOLL2(source Float64Source, window int, k float64) *BOLLStream { func BOLL(source types.Float64Source, window int, k float64) *BOLLStream {
// bind these indicators before our main calculator // bind these indicators before our main calculator
sma := SMA2(source, window) sma := SMA2(source, window)
stdDev := StdDev2(source, window) stdDev := StdDev(source, window)
s := &BOLLStream{ s := &BOLLStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
UpBand: NewFloat64Series(), UpBand: types.NewFloat64Series(),
DownBand: NewFloat64Series(), DownBand: types.NewFloat64Series(),
window: window, window: window,
k: k, k: k,
SMA: sma, SMA: sma,

28
pkg/indicator/v2/cma.go Normal file
View File

@ -0,0 +1,28 @@
package indicatorv2
import (
"github.com/c9s/bbgo/pkg/indicator"
"github.com/c9s/bbgo/pkg/types"
)
type CMAStream struct {
*types.Float64Series
}
func CMA2(source types.Float64Source) *CMAStream {
s := &CMAStream{
Float64Series: types.NewFloat64Series(),
}
s.Bind(source, s)
return s
}
func (s *CMAStream) Calculate(x float64) float64 {
l := float64(s.Slice.Length())
cma := (s.Slice.Last(0)*l + x) / (l + 1.)
return cma
}
func (s *CMAStream) Truncate() {
s.Slice.Truncate(indicator.MaxNumOfEWMA)
}

View File

@ -1,7 +1,8 @@
package indicator package indicatorv2
import ( import (
"github.com/c9s/bbgo/pkg/datatype/floats" "github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
) )
type CrossType float64 type CrossType float64
@ -13,7 +14,7 @@ const (
// CrossStream subscribes 2 upstreams, and calculate the cross signal // CrossStream subscribes 2 upstreams, and calculate the cross signal
type CrossStream struct { type CrossStream struct {
*Float64Series *types.Float64Series
a, b floats.Slice a, b floats.Slice
} }
@ -21,9 +22,9 @@ type CrossStream struct {
// Cross creates the CrossStream object: // Cross creates the CrossStream object:
// //
// cross := Cross(fastEWMA, slowEWMA) // cross := Cross(fastEWMA, slowEWMA)
func Cross(a, b Float64Source) *CrossStream { func Cross(a, b types.Float64Source) *CrossStream {
s := &CrossStream{ s := &CrossStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
} }
a.OnUpdate(func(v float64) { a.OnUpdate(func(v float64) {
s.a.Push(v) s.a.Push(v)

View File

@ -1,15 +1,17 @@
package indicator package indicatorv2
import "github.com/c9s/bbgo/pkg/types"
type EWMAStream struct { type EWMAStream struct {
*Float64Series *types.Float64Series
window int window int
multiplier float64 multiplier float64
} }
func EWMA2(source Float64Source, window int) *EWMAStream { func EWMA2(source types.Float64Source, window int) *EWMAStream {
s := &EWMAStream{ s := &EWMAStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
window: window, window: window,
multiplier: 2.0 / float64(1+window), multiplier: 2.0 / float64(1+window),
} }
@ -18,7 +20,7 @@ func EWMA2(source Float64Source, window int) *EWMAStream {
} }
func (s *EWMAStream) Calculate(v float64) float64 { func (s *EWMAStream) Calculate(v float64) float64 {
last := s.slice.Last(0) last := s.Slice.Last(0)
if last == 0.0 { if last == 0.0 {
return v return v
} }

View File

@ -1,4 +1,4 @@
package indicator package indicatorv2
import "github.com/c9s/bbgo/pkg/types" import "github.com/c9s/bbgo/pkg/types"
@ -60,3 +60,9 @@ func KLines(source types.Stream, symbol string, interval types.Interval) *KLineS
return s return s
} }
type KLineSubscription interface {
AddSubscriber(f func(k types.KLine))
Length() int
Last(i int) *types.KLine
}

View File

@ -1,6 +1,6 @@
// Code generated by "callbackgen -type KLineStream"; DO NOT EDIT. // Code generated by "callbackgen -type KLineStream"; DO NOT EDIT.
package indicator package indicatorv2
import ( import (
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"

View File

@ -1,4 +1,8 @@
package indicator package indicatorv2
import (
"github.com/c9s/bbgo/pkg/types"
)
type MACDStream struct { type MACDStream struct {
*SubtractStream *SubtractStream
@ -9,7 +13,7 @@ type MACDStream struct {
Histogram *SubtractStream Histogram *SubtractStream
} }
func MACD2(source Float64Source, shortWindow, longWindow, signalWindow int) *MACDStream { func MACD2(source types.Float64Source, shortWindow, longWindow, signalWindow int) *MACDStream {
// bind and calculate these first // bind and calculate these first
fastEWMA := EWMA2(source, shortWindow) fastEWMA := EWMA2(source, shortWindow)
slowEWMA := EWMA2(source, longWindow) slowEWMA := EWMA2(source, longWindow)

View File

@ -1,4 +1,4 @@
package indicator package indicatorv2
import ( import (
"encoding/json" "encoding/json"
@ -21,6 +21,14 @@ fast = s.ewm(span=12, adjust=False).mean()
print(fast - slow) print(fast - slow)
*/ */
func buildKLines(prices []fixedpoint.Value) (klines []types.KLine) {
for _, p := range prices {
klines = append(klines, types.KLine{Close: p})
}
return klines
}
func Test_MACD2(t *testing.T) { func Test_MACD2(t *testing.T) {
var randomPrices = []byte(`[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`) var randomPrices = []byte(`[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`)
var input []fixedpoint.Value var input []fixedpoint.Value

View File

@ -1,15 +1,18 @@
package indicator package indicatorv2
import "github.com/c9s/bbgo/pkg/datatype/floats" import (
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
)
type MultiplyStream struct { type MultiplyStream struct {
*Float64Series *types.Float64Series
a, b floats.Slice a, b floats.Slice
} }
func Multiply(a, b Float64Source) *MultiplyStream { func Multiply(a, b types.Float64Source) *MultiplyStream {
s := &MultiplyStream{ s := &MultiplyStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
} }
a.OnUpdate(func(v float64) { a.OnUpdate(func(v float64) {
@ -29,13 +32,13 @@ func (s *MultiplyStream) calculate() {
return return
} }
if s.a.Length() > s.slice.Length() { if s.a.Length() > s.Slice.Length() {
var numNewElems = s.a.Length() - s.slice.Length() var numNewElems = s.a.Length() - s.Slice.Length()
var tailA = s.a.Tail(numNewElems) var tailA = s.a.Tail(numNewElems)
var tailB = s.b.Tail(numNewElems) var tailB = s.b.Tail(numNewElems)
var tailC = tailA.Mul(tailB) var tailC = tailA.Mul(tailB)
for _, f := range tailC { for _, f := range tailC {
s.slice.Push(f) s.Slice.Push(f)
s.EmitUpdate(f) s.EmitUpdate(f)
} }
} }

View File

@ -0,0 +1,34 @@
package indicatorv2
import (
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
)
type PivotHighStream struct {
*types.Float64Series
rawValues floats.Slice
window, rightWindow int
}
func PivotHigh2(source types.Float64Source, window, rightWindow int) *PivotHighStream {
s := &PivotHighStream{
Float64Series: types.NewFloat64Series(),
window: window,
rightWindow: rightWindow,
}
s.Subscribe(source, func(x float64) {
s.rawValues.Push(x)
if low, ok := s.calculatePivotHigh(s.rawValues, s.window, s.rightWindow); ok {
s.PushAndEmit(low)
}
})
return s
}
func (s *PivotHighStream) calculatePivotHigh(highs floats.Slice, left, right int) (float64, bool) {
return floats.FindPivot(highs, left, right, func(a, pivot float64) bool {
return a < pivot
})
}

View File

@ -0,0 +1,34 @@
package indicatorv2
import (
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
)
type PivotLowStream struct {
*types.Float64Series
rawValues floats.Slice
window, rightWindow int
}
func PivotLow(source types.Float64Source, window, rightWindow int) *PivotLowStream {
s := &PivotLowStream{
Float64Series: types.NewFloat64Series(),
window: window,
rightWindow: rightWindow,
}
s.Subscribe(source, func(x float64) {
s.rawValues.Push(x)
if low, ok := s.calculatePivotLow(s.rawValues, s.window, s.rightWindow); ok {
s.PushAndEmit(low)
}
})
return s
}
func (s *PivotLowStream) calculatePivotLow(lows floats.Slice, left, right int) (float64, bool) {
return floats.FindPivot(lows, left, right, func(a, pivot float64) bool {
return a > pivot
})
}

View File

@ -1,24 +1,18 @@
package indicator package indicatorv2
import ( import (
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
type KLineSubscription interface {
AddSubscriber(f func(k types.KLine))
Length() int
Last(i int) *types.KLine
}
type PriceStream struct { type PriceStream struct {
*Float64Series *types.Float64Series
mapper KLineValueMapper mapper types.KLineValueMapper
} }
func Price(source KLineSubscription, mapper KLineValueMapper) *PriceStream { func Price(source KLineSubscription, mapper types.KLineValueMapper) *PriceStream {
s := &PriceStream{ s := &PriceStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
mapper: mapper, mapper: mapper,
} }
@ -37,37 +31,37 @@ func Price(source KLineSubscription, mapper KLineValueMapper) *PriceStream {
func (s *PriceStream) AddSubscriber(f func(v float64)) { func (s *PriceStream) AddSubscriber(f func(v float64)) {
s.OnUpdate(f) s.OnUpdate(f)
if len(s.slice) == 0 { if len(s.Slice) == 0 {
return return
} }
// push historical value to the subscriber // push historical value to the subscriber
for _, v := range s.slice { for _, v := range s.Slice {
f(v) f(v)
} }
} }
func (s *PriceStream) PushAndEmit(v float64) { func (s *PriceStream) PushAndEmit(v float64) {
s.slice.Push(v) s.Slice.Push(v)
s.EmitUpdate(v) s.EmitUpdate(v)
} }
func ClosePrices(source KLineSubscription) *PriceStream { func ClosePrices(source KLineSubscription) *PriceStream {
return Price(source, KLineClosePriceMapper) return Price(source, types.KLineClosePriceMapper)
} }
func LowPrices(source KLineSubscription) *PriceStream { func LowPrices(source KLineSubscription) *PriceStream {
return Price(source, KLineLowPriceMapper) return Price(source, types.KLineLowPriceMapper)
} }
func HighPrices(source KLineSubscription) *PriceStream { func HighPrices(source KLineSubscription) *PriceStream {
return Price(source, KLineHighPriceMapper) return Price(source, types.KLineHighPriceMapper)
} }
func OpenPrices(source KLineSubscription) *PriceStream { func OpenPrices(source KLineSubscription) *PriceStream {
return Price(source, KLineOpenPriceMapper) return Price(source, types.KLineOpenPriceMapper)
} }
func Volumes(source KLineSubscription) *PriceStream { func Volumes(source KLineSubscription) *PriceStream {
return Price(source, KLineVolumeMapper) return Price(source, types.KLineVolumeMapper)
} }

View File

@ -1,8 +1,15 @@
package indicator package indicatorv2
import (
"github.com/c9s/bbgo/pkg/types"
)
const MaxNumOfRMA = 1000
const MaxNumOfRMATruncateSize = 500
type RMAStream struct { type RMAStream struct {
// embedded structs // embedded structs
*Float64Series *types.Float64Series
// config fields // config fields
Adjust bool Adjust bool
@ -12,9 +19,9 @@ type RMAStream struct {
sum, previous float64 sum, previous float64
} }
func RMA2(source Float64Source, window int, adjust bool) *RMAStream { func RMA2(source types.Float64Source, window int, adjust bool) *RMAStream {
s := &RMAStream{ s := &RMAStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
window: window, window: window,
Adjust: adjust, Adjust: adjust,
} }
@ -41,17 +48,17 @@ func (s *RMAStream) Calculate(x float64) float64 {
if s.counter < s.window { if s.counter < s.window {
// we can use x, but we need to use 0. to make the same behavior as the result from python pandas_ta // we can use x, but we need to use 0. to make the same behavior as the result from python pandas_ta
s.slice.Push(0) s.Slice.Push(0)
} }
s.slice.Push(tmp) s.Slice.Push(tmp)
s.previous = tmp s.previous = tmp
return tmp return tmp
} }
func (s *RMAStream) Truncate() { func (s *RMAStream) Truncate() {
if len(s.slice) > MaxNumOfRMA { if len(s.Slice) > MaxNumOfRMA {
s.slice = s.slice[MaxNumOfRMATruncateSize-1:] s.Slice = s.Slice[MaxNumOfRMATruncateSize-1:]
} }
} }

View File

@ -1,20 +1,24 @@
package indicator package indicatorv2
import (
"github.com/c9s/bbgo/pkg/types"
)
type RSIStream struct { type RSIStream struct {
// embedded structs // embedded structs
*Float64Series *types.Float64Series
// config fields // config fields
window int window int
// private states // private states
source Float64Source source types.Float64Source
} }
func RSI2(source Float64Source, window int) *RSIStream { func RSI2(source types.Float64Source, window int) *RSIStream {
s := &RSIStream{ s := &RSIStream{
source: source, source: source,
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
window: window, window: window,
} }
s.Bind(source, s) s.Bind(source, s)
@ -42,3 +46,17 @@ func (s *RSIStream) Calculate(_ float64) float64 {
rsi := 100.0 - (100.0 / (1.0 + rs)) rsi := 100.0 - (100.0 / (1.0 + rs))
return rsi return rsi
} }
func max(x, y int) int {
if x > y {
return x
}
return y
}
func min(x, y int) int {
if x < y {
return x
}
return y
}

View File

@ -1,4 +1,4 @@
package indicator package indicatorv2
import ( import (
"encoding/json" "encoding/json"
@ -75,11 +75,11 @@ func Test_RSI2(t *testing.T) {
prices.PushAndEmit(price) prices.PushAndEmit(price)
} }
assert.Equal(t, floats.Slice(tt.values), prices.slice) assert.Equal(t, floats.Slice(tt.values), prices.Slice)
if assert.Equal(t, len(tt.want), len(rsi.slice)) { if assert.Equal(t, len(tt.want), len(rsi.Slice)) {
for i, v := range tt.want { for i, v := range tt.want {
assert.InDelta(t, v, rsi.slice[i], 0.000001, "Expected rsi.slice[%d] to be %v, but got %v", i, v, rsi.slice[i]) assert.InDelta(t, v, rsi.Slice[i], 0.000001, "Expected rsi.slice[%d] to be %v, but got %v", i, v, rsi.Slice[i])
} }
} }
}) })

View File

@ -1,16 +1,20 @@
package indicator package indicatorv2
import "github.com/c9s/bbgo/pkg/types" import (
"github.com/c9s/bbgo/pkg/types"
)
const MaxNumOfSMA = 5_000
type SMAStream struct { type SMAStream struct {
*Float64Series *types.Float64Series
window int window int
rawValues *types.Queue rawValues *types.Queue
} }
func SMA2(source Float64Source, window int) *SMAStream { func SMA2(source types.Float64Source, window int) *SMAStream {
s := &SMAStream{ s := &SMAStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
window: window, window: window,
rawValues: types.NewQueue(window), rawValues: types.NewQueue(window),
} }
@ -25,5 +29,5 @@ func (s *SMAStream) Calculate(v float64) float64 {
} }
func (s *SMAStream) Truncate() { func (s *SMAStream) Truncate() {
s.slice = s.slice.Truncate(MaxNumOfSMA) s.Slice = s.Slice.Truncate(MaxNumOfSMA)
} }

View File

@ -1,9 +1,9 @@
package indicator package indicatorv2
import "github.com/c9s/bbgo/pkg/types" import "github.com/c9s/bbgo/pkg/types"
type StdDevStream struct { type StdDevStream struct {
*Float64Series *types.Float64Series
rawValues *types.Queue rawValues *types.Queue
@ -11,9 +11,9 @@ type StdDevStream struct {
multiplier float64 multiplier float64
} }
func StdDev2(source Float64Source, window int) *StdDevStream { func StdDev(source types.Float64Source, window int) *StdDevStream {
s := &StdDevStream{ s := &StdDevStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
rawValues: types.NewQueue(window), rawValues: types.NewQueue(window),
window: window, window: window,
} }

View File

@ -1,10 +1,12 @@
package indicator package indicatorv2
import ( import (
"github.com/c9s/bbgo/pkg/datatype/floats" "github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
const DPeriod int = 3
// Stochastic Oscillator // Stochastic Oscillator
// - https://www.investopedia.com/terms/s/stochasticoscillator.asp // - https://www.investopedia.com/terms/s/stochasticoscillator.asp
// //
@ -30,7 +32,7 @@ type StochStream struct {
} }
// Stochastic Oscillator // Stochastic Oscillator
func Stoch2(source KLineSubscription, window, dPeriod int) *StochStream { func Stoch(source KLineSubscription, window, dPeriod int) *StochStream {
highPrices := HighPrices(source) highPrices := HighPrices(source)
lowPrices := LowPrices(source) lowPrices := LowPrices(source)
@ -42,8 +44,8 @@ func Stoch2(source KLineSubscription, window, dPeriod int) *StochStream {
} }
source.AddSubscriber(func(kLine types.KLine) { source.AddSubscriber(func(kLine types.KLine) {
lowest := s.lowPrices.slice.Tail(s.window).Min() lowest := s.lowPrices.Slice.Tail(s.window).Min()
highest := s.highPrices.slice.Tail(s.window).Max() highest := s.highPrices.Slice.Tail(s.window).Max()
var k float64 = 50.0 var k float64 = 50.0
var d float64 = 0.0 var d float64 = 0.0
@ -53,7 +55,7 @@ func Stoch2(source KLineSubscription, window, dPeriod int) *StochStream {
} }
s.K.Push(k) s.K.Push(k)
d = s.K.Tail(s.dPeriod).Mean() d = s.K.Tail(s.dPeriod).Mean()
s.D.Push(d) s.D.Push(d)
s.EmitUpdate(k, d) s.EmitUpdate(k, d)

View File

@ -1,4 +1,4 @@
package indicator package indicatorv2
import ( import (
"encoding/json" "encoding/json"
@ -58,7 +58,7 @@ func TestSTOCH2_update(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
stream := &types.StandardStream{} stream := &types.StandardStream{}
kLines := KLines(stream, "", "") kLines := KLines(stream, "", "")
kd := Stoch2(kLines, tt.window, DPeriod) kd := Stoch(kLines, tt.window, DPeriod)
for _, k := range tt.kLines { for _, k := range tt.kLines {
stream.EmitKLineClosed(k) stream.EmitKLineClosed(k)

View File

@ -1,6 +1,6 @@
// Code generated by "callbackgen -type StochStream"; DO NOT EDIT. // Code generated by "callbackgen -type StochStream"; DO NOT EDIT.
package indicator package indicatorv2
import () import ()

View File

@ -1,12 +1,13 @@
package indicator package indicatorv2
import ( import (
"github.com/c9s/bbgo/pkg/datatype/floats" "github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
) )
// SubtractStream subscribes 2 upstream data, and then subtract these 2 values // SubtractStream subscribes 2 upstream data, and then subtract these 2 values
type SubtractStream struct { type SubtractStream struct {
*Float64Series *types.Float64Series
a, b floats.Slice a, b floats.Slice
i int i int
@ -14,9 +15,9 @@ type SubtractStream struct {
// Subtract creates the SubtractStream object // Subtract creates the SubtractStream object
// subtract := Subtract(longEWMA, shortEWMA) // subtract := Subtract(longEWMA, shortEWMA)
func Subtract(a, b Float64Source) *SubtractStream { func Subtract(a, b types.Float64Source) *SubtractStream {
s := &SubtractStream{ s := &SubtractStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
} }
a.OnUpdate(func(v float64) { a.OnUpdate(func(v float64) {
@ -35,13 +36,13 @@ func (s *SubtractStream) calculate() {
return return
} }
if s.a.Length() > s.slice.Length() { if s.a.Length() > s.Slice.Length() {
var numNewElems = s.a.Length() - s.slice.Length() var numNewElems = s.a.Length() - s.Slice.Length()
var tailA = s.a.Tail(numNewElems) var tailA = s.a.Tail(numNewElems)
var tailB = s.b.Tail(numNewElems) var tailB = s.b.Tail(numNewElems)
var tailC = tailA.Sub(tailB) var tailC = tailA.Sub(tailB)
for _, f := range tailC { for _, f := range tailC {
s.slice.Push(f) s.Slice.Push(f)
s.EmitUpdate(f) s.EmitUpdate(f)
} }
} }

View File

@ -1,4 +1,4 @@
package indicator package indicatorv2
import ( import (
"testing" "testing"
@ -21,10 +21,10 @@ func Test_v2_Subtract(t *testing.T) {
stream.EmitKLineClosed(types.KLine{Close: fixedpoint.NewFromFloat(19_000.0 + i)}) stream.EmitKLineClosed(types.KLine{Close: fixedpoint.NewFromFloat(19_000.0 + i)})
} }
t.Logf("fastEMA: %+v", fastEMA.slice) t.Logf("fastEMA: %+v", fastEMA.Slice)
t.Logf("slowEMA: %+v", slowEMA.slice) t.Logf("slowEMA: %+v", slowEMA.Slice)
assert.Equal(t, len(subtract.a), len(subtract.b)) assert.Equal(t, len(subtract.a), len(subtract.b))
assert.Equal(t, len(subtract.a), len(subtract.slice)) assert.Equal(t, len(subtract.a), len(subtract.Slice))
assert.InDelta(t, subtract.slice[0], subtract.a[0]-subtract.b[0], 0.0001) assert.InDelta(t, subtract.Slice[0], subtract.a[0]-subtract.b[0], 0.0001)
} }

View File

@ -1,4 +1,4 @@
package indicator package indicatorv2
import ( import (
"math" "math"
@ -9,7 +9,7 @@ import (
// This TRStream calculates the ATR first // This TRStream calculates the ATR first
type TRStream struct { type TRStream struct {
// embedded struct // embedded struct
*Float64Series *types.Float64Series
// private states // private states
previousClose float64 previousClose float64
@ -17,7 +17,7 @@ type TRStream struct {
func TR2(source KLineSubscription) *TRStream { func TR2(source KLineSubscription) *TRStream {
s := &TRStream{ s := &TRStream{
Float64Series: NewFloat64Series(), Float64Series: types.NewFloat64Series(),
} }
source.AddSubscriber(func(k types.KLine) { source.AddSubscriber(func(k types.KLine) {

View File

@ -1,4 +1,4 @@
package indicator package indicatorv2
import ( import (
"encoding/json" "encoding/json"

View File

@ -1,23 +0,0 @@
package indicator
type CMAStream struct {
*Float64Series
}
func CMA2(source Float64Source) *CMAStream {
s := &CMAStream{
Float64Series: NewFloat64Series(),
}
s.Bind(source, s)
return s
}
func (s *CMAStream) Calculate(x float64) float64 {
l := float64(s.slice.Length())
cma := (s.slice.Last(0)*l + x) / (l + 1.)
return cma
}
func (s *CMAStream) Truncate() {
s.slice.Truncate(MaxNumOfEWMA)
}

View File

@ -1,27 +0,0 @@
package indicator
import (
"github.com/c9s/bbgo/pkg/datatype/floats"
)
type PivotHighStream struct {
*Float64Series
rawValues floats.Slice
window, rightWindow int
}
func PivotHigh2(source Float64Source, window, rightWindow int) *PivotHighStream {
s := &PivotHighStream{
Float64Series: NewFloat64Series(),
window: window,
rightWindow: rightWindow,
}
s.Subscribe(source, func(x float64) {
s.rawValues.Push(x)
if low, ok := calculatePivotHigh(s.rawValues, s.window, s.rightWindow); ok {
s.PushAndEmit(low)
}
})
return s
}

View File

@ -1,27 +0,0 @@
package indicator
import (
"github.com/c9s/bbgo/pkg/datatype/floats"
)
type PivotLowStream struct {
*Float64Series
rawValues floats.Slice
window, rightWindow int
}
func PivotLow2(source Float64Source, window, rightWindow int) *PivotLowStream {
s := &PivotLowStream{
Float64Series: NewFloat64Series(),
window: window,
rightWindow: rightWindow,
}
s.Subscribe(source, func(x float64) {
s.rawValues.Push(x)
if low, ok := calculatePivotLow(s.rawValues, s.window, s.rightWindow); ok {
s.PushAndEmit(low)
}
})
return s
}

View File

@ -58,7 +58,7 @@ func (inc *Volatility) CalculateAndUpdate(allKLines []types.KLine) {
var recentT = allKLines[end-(inc.Window-1) : end+1] var recentT = allKLines[end-(inc.Window-1) : end+1]
volatility, err := calculateVOLATILITY(recentT, inc.Window, KLineClosePriceMapper) volatility, err := calculateVOLATILITY(recentT, inc.Window, types.KLineClosePriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate volatility") log.WithError(err).Error("can not calculate volatility")
return return
@ -86,7 +86,7 @@ func (inc *Volatility) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate) updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
} }
func calculateVOLATILITY(klines []types.KLine, window int, priceF KLineValueMapper) (float64, error) { func calculateVOLATILITY(klines []types.KLine, window int, priceF types.KLineValueMapper) (float64, error) {
length := len(klines) length := len(klines)
if length == 0 || length < window { if length == 0 || length < window {
return 0.0, fmt.Errorf("insufficient elements for calculating VOL with window = %d", window) return 0.0, fmt.Errorf("insufficient elements for calculating VOL with window = %d", window)

View File

@ -71,7 +71,7 @@ func (inc *VWAP) Length() int {
var _ types.SeriesExtend = &VWAP{} var _ types.SeriesExtend = &VWAP{}
func (inc *VWAP) PushK(k types.KLine) { func (inc *VWAP) PushK(k types.KLine) {
inc.Update(KLineTypicalPriceMapper(k), k.Volume.Float64()) inc.Update(types.KLineTypicalPriceMapper(k), k.Volume.Float64())
} }
func (inc *VWAP) CalculateAndUpdate(allKLines []types.KLine) { func (inc *VWAP) CalculateAndUpdate(allKLines []types.KLine) {
@ -99,7 +99,7 @@ func (inc *VWAP) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate) updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
} }
func calculateVWAP(klines []types.KLine, priceF KLineValueMapper, window int) float64 { func calculateVWAP(klines []types.KLine, priceF types.KLineValueMapper, window int) float64 {
vwap := VWAP{IntervalWindow: types.IntervalWindow{Window: window}} vwap := VWAP{IntervalWindow: types.IntervalWindow{Window: window}}
for _, k := range klines { for _, k := range klines {
vwap.Update(priceF(k), k.Volume.Float64()) vwap.Update(priceF(k), k.Volume.Float64())

View File

@ -63,7 +63,7 @@ func Test_calculateVWAP(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
priceF := KLineTypicalPriceMapper priceF := types.KLineTypicalPriceMapper
got := calculateVWAP(tt.kLines, priceF, tt.window) got := calculateVWAP(tt.kLines, priceF, tt.window)
diff := math.Trunc((got-tt.want)*100) / 100 diff := math.Trunc((got-tt.want)*100) / 100
if diff != 0 { if diff != 0 {

View File

@ -4,14 +4,14 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/indicator" "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
type CircuitBreakRiskControl struct { type CircuitBreakRiskControl struct {
// Since price could be fluctuated large, // Since price could be fluctuated large,
// use an EWMA to smooth it in running time // use an EWMA to smooth it in running time
price *indicator.EWMAStream price *indicatorv2.EWMAStream
position *types.Position position *types.Position
profitStats *types.ProfitStats profitStats *types.ProfitStats
lossThreshold fixedpoint.Value lossThreshold fixedpoint.Value
@ -19,7 +19,7 @@ type CircuitBreakRiskControl struct {
func NewCircuitBreakRiskControl( func NewCircuitBreakRiskControl(
position *types.Position, position *types.Position,
price *indicator.EWMAStream, price *indicatorv2.EWMAStream,
lossThreshold fixedpoint.Value, lossThreshold fixedpoint.Value,
profitStats *types.ProfitStats) *CircuitBreakRiskControl { profitStats *types.ProfitStats) *CircuitBreakRiskControl {

View File

@ -6,12 +6,11 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/indicator" indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
func Test_IsHalted(t *testing.T) { func Test_IsHalted(t *testing.T) {
var ( var (
price = 30000.00 price = 30000.00
realizedPnL = fixedpoint.NewFromFloat(-100.0) realizedPnL = fixedpoint.NewFromFloat(-100.0)
@ -19,7 +18,7 @@ func Test_IsHalted(t *testing.T) {
) )
window := types.IntervalWindow{Window: 30, Interval: types.Interval1m} window := types.IntervalWindow{Window: 30, Interval: types.Interval1m}
priceEWMA := indicator.EWMA2(nil, window.Window) priceEWMA := indicatorv2.EWMA2(nil, window.Window)
priceEWMA.PushAndEmit(price) priceEWMA.PushAndEmit(price)
cases := []struct { cases := []struct {

View File

@ -7,6 +7,7 @@ import (
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/indicator" "github.com/c9s/bbgo/pkg/indicator"
indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
@ -28,7 +29,7 @@ type DynamicSpreadSettings struct {
} }
// Initialize dynamic spreads and preload SMAs // Initialize dynamic spreads and preload SMAs
func (ds *DynamicSpreadSettings) Initialize(symbol string, session *bbgo.ExchangeSession, neutralBoll, defaultBoll *indicator.BOLLStream) { func (ds *DynamicSpreadSettings) Initialize(symbol string, session *bbgo.ExchangeSession, neutralBoll, defaultBoll *indicatorv2.BOLLStream) {
switch { switch {
case ds.AmpSpreadSettings != nil: case ds.AmpSpreadSettings != nil:
ds.AmpSpreadSettings.initialize(symbol, session) ds.AmpSpreadSettings.initialize(symbol, session)
@ -164,10 +165,10 @@ type DynamicSpreadBollWidthRatioSettings struct {
// A positive number. The greater factor, the sharper weighting function. Default set to 1.0 . // A positive number. The greater factor, the sharper weighting function. Default set to 1.0 .
Sensitivity float64 `json:"sensitivity"` Sensitivity float64 `json:"sensitivity"`
defaultBoll, neutralBoll *indicator.BOLLStream defaultBoll, neutralBoll *indicatorv2.BOLLStream
} }
func (ds *DynamicSpreadBollWidthRatioSettings) initialize(neutralBoll, defaultBoll *indicator.BOLLStream) { func (ds *DynamicSpreadBollWidthRatioSettings) initialize(neutralBoll, defaultBoll *indicatorv2.BOLLStream) {
ds.neutralBoll = neutralBoll ds.neutralBoll = neutralBoll
ds.defaultBoll = defaultBoll ds.defaultBoll = defaultBoll
if ds.Sensitivity <= 0. { if ds.Sensitivity <= 0. {

View File

@ -6,7 +6,7 @@ import (
"math" "math"
"sync" "sync"
"github.com/c9s/bbgo/pkg/indicator" indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/util" "github.com/c9s/bbgo/pkg/util"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -158,10 +158,10 @@ type Strategy struct {
groupID uint32 groupID uint32
// defaultBoll is the BOLLINGER indicator we used for predicting the price. // defaultBoll is the BOLLINGER indicator we used for predicting the price.
defaultBoll *indicator.BOLLStream defaultBoll *indicatorv2.BOLLStream
// neutralBoll is the neutral price section // neutralBoll is the neutral price section
neutralBoll *indicator.BOLLStream neutralBoll *indicatorv2.BOLLStream
// StrategyController // StrategyController
bbgo.StrategyController bbgo.StrategyController

View File

@ -1,6 +1,8 @@
package bollmaker package bollmaker
import "github.com/c9s/bbgo/pkg/indicator" import (
indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2"
)
type PriceTrend string type PriceTrend string
@ -11,7 +13,7 @@ const (
UnknownTrend PriceTrend = "unknown" UnknownTrend PriceTrend = "unknown"
) )
func detectPriceTrend(inc *indicator.BOLLStream, price float64) PriceTrend { func detectPriceTrend(inc *indicatorv2.BOLLStream, price float64) PriceTrend {
if inBetween(price, inc.DownBand.Last(0), inc.UpBand.Last(0)) { if inBetween(price, inc.DownBand.Last(0), inc.UpBand.Last(0)) {
return NeutralTrend return NeutralTrend
} }

View File

@ -82,13 +82,13 @@ func (inc *PMR) PushK(k types.KLine) {
return return
} }
inc.Update(indicator.KLineClosePriceMapper(k)) inc.Update(types.KLineClosePriceMapper(k))
inc.EndTime = k.EndTime.Time() inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last(0)) inc.EmitUpdate(inc.Last(0))
} }
func CalculateKLinesPMR(allKLines []types.KLine, window int) float64 { func CalculateKLinesPMR(allKLines []types.KLine, window int) float64 {
return pmr(indicator.MapKLinePrice(allKLines, indicator.KLineClosePriceMapper), window) return pmr(types.MapKLinePrice(allKLines, types.KLineClosePriceMapper), window)
} }
func pmr(prices []float64, window int) float64 { func pmr(prices []float64, window int) float64 {

View File

@ -89,13 +89,13 @@ func (inc *PVD) PushK(k types.KLine) {
return return
} }
inc.Update(indicator.KLineClosePriceMapper(k), indicator.KLineVolumeMapper(k)) inc.Update(types.KLineClosePriceMapper(k), types.KLineVolumeMapper(k))
inc.EndTime = k.EndTime.Time() inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last(0)) inc.EmitUpdate(inc.Last(0))
} }
func CalculateKLinesPVD(allKLines []types.KLine, window int) float64 { func CalculateKLinesPVD(allKLines []types.KLine, window int) float64 {
return pvd(indicator.MapKLinePrice(allKLines, indicator.KLineClosePriceMapper), indicator.MapKLinePrice(allKLines, indicator.KLineVolumeMapper), window) return pvd(types.MapKLinePrice(allKLines, types.KLineClosePriceMapper), types.MapKLinePrice(allKLines, types.KLineVolumeMapper), window)
} }
func pvd(prices []float64, volumes []float64, window int) float64 { func pvd(prices []float64, volumes []float64, window int) float64 {

View File

@ -81,7 +81,7 @@ func (inc *RR) PushK(k types.KLine) {
return return
} }
inc.Update(indicator.KLineClosePriceMapper(k)) inc.Update(types.KLineClosePriceMapper(k))
inc.EndTime = k.EndTime.Time() inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last(0)) inc.EmitUpdate(inc.Last(0))
} }

View File

@ -42,7 +42,7 @@ func (inc *A18) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateA18(recentT, indicator.KLineClosePriceMapper) val, err := calculateA18(recentT, types.KLineClosePriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate") log.WithError(err).Error("can not calculate")
return return

View File

@ -42,7 +42,7 @@ func (inc *A2) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateA2(recentT, KLineLowPriceMapper, KLineHighPriceMapper, indicator.KLineClosePriceMapper) val, err := calculateA2(recentT, KLineLowPriceMapper, KLineHighPriceMapper, types.KLineClosePriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate") log.WithError(err).Error("can not calculate")
return return

View File

@ -43,7 +43,7 @@ func (inc *A3) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateA3(recentT, KLineLowPriceMapper, KLineHighPriceMapper, indicator.KLineClosePriceMapper) val, err := calculateA3(recentT, KLineLowPriceMapper, KLineHighPriceMapper, types.KLineClosePriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate pivots") log.WithError(err).Error("can not calculate pivots")
return return

View File

@ -42,7 +42,7 @@ func (inc *A34) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateA34(recentT, indicator.KLineClosePriceMapper) val, err := calculateA34(recentT, types.KLineClosePriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate pivots") log.WithError(err).Error("can not calculate pivots")
return return

View File

@ -46,7 +46,7 @@ func (inc *R) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateR(recentT, indicator.KLineOpenPriceMapper, indicator.KLineClosePriceMapper) val, err := calculateR(recentT, types.KLineOpenPriceMapper, types.KLineClosePriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate pivots") log.WithError(err).Error("can not calculate pivots")
return return

View File

@ -42,7 +42,7 @@ func (inc *S0) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateS0(recentT, indicator.KLineClosePriceMapper) val, err := calculateS0(recentT, types.KLineClosePriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate") log.WithError(err).Error("can not calculate")
return return

View File

@ -40,7 +40,7 @@ func (inc *S1) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
correlation, err := calculateS1(recentT, inc.Window, KLineAmplitudeMapper, indicator.KLineVolumeMapper) correlation, err := calculateS1(recentT, inc.Window, KLineAmplitudeMapper, types.KLineVolumeMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate correlation") log.WithError(err).Error("can not calculate correlation")
return return

View File

@ -40,7 +40,7 @@ func (inc *S2) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
correlation, err := calculateS2(recentT, inc.Window, indicator.KLineOpenPriceMapper, indicator.KLineVolumeMapper) correlation, err := calculateS2(recentT, inc.Window, types.KLineOpenPriceMapper, types.KLineVolumeMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate correlation") log.WithError(err).Error("can not calculate correlation")
return return

View File

@ -42,7 +42,7 @@ func (inc *S3) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateS3(recentT, indicator.KLineClosePriceMapper, indicator.KLineOpenPriceMapper) val, err := calculateS3(recentT, types.KLineClosePriceMapper, types.KLineOpenPriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate") log.WithError(err).Error("can not calculate")
return return

View File

@ -42,7 +42,7 @@ func (inc *S4) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateS4(recentT, indicator.KLineClosePriceMapper) val, err := calculateS4(recentT, types.KLineClosePriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate") log.WithError(err).Error("can not calculate")
return return

View File

@ -42,7 +42,7 @@ func (inc *S5) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateS5(recentT, indicator.KLineVolumeMapper) val, err := calculateS5(recentT, types.KLineVolumeMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate pivots") log.WithError(err).Error("can not calculate pivots")
return return

View File

@ -42,7 +42,7 @@ func (inc *S6) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateS6(recentT, indicator.KLineHighPriceMapper, indicator.KLineLowPriceMapper, indicator.KLineClosePriceMapper, indicator.KLineVolumeMapper) val, err := calculateS6(recentT, types.KLineHighPriceMapper, types.KLineLowPriceMapper, types.KLineClosePriceMapper, types.KLineVolumeMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate") log.WithError(err).Error("can not calculate")
return return

View File

@ -42,7 +42,7 @@ func (inc *S7) CalculateAndUpdate(klines []types.KLine) {
var recentT = klines[end-(inc.Window-1) : end+1] var recentT = klines[end-(inc.Window-1) : end+1]
val, err := calculateS7(recentT, indicator.KLineOpenPriceMapper, indicator.KLineClosePriceMapper) val, err := calculateS7(recentT, types.KLineOpenPriceMapper, types.KLineClosePriceMapper)
if err != nil { if err != nil {
log.WithError(err).Error("can not calculate") log.WithError(err).Error("can not calculate")
return return

View File

@ -72,7 +72,7 @@ func (inc *SHARK) PushK(k types.KLine) {
return return
} }
inc.Update(indicator.KLineHighPriceMapper(k), indicator.KLineLowPriceMapper(k), indicator.KLineClosePriceMapper(k)) inc.Update(types.KLineHighPriceMapper(k), types.KLineLowPriceMapper(k), types.KLineClosePriceMapper(k))
inc.EndTime = k.EndTime.Time() inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last(0)) inc.EmitUpdate(inc.Last(0))
} }

View File

@ -74,7 +74,7 @@ func (inc *NRR) PushK(k types.KLine) {
return return
} }
inc.Update(indicator.KLineOpenPriceMapper(k), indicator.KLineClosePriceMapper(k)) inc.Update(types.KLineOpenPriceMapper(k), types.KLineClosePriceMapper(k))
inc.EndTime = k.EndTime.Time() inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last(0)) inc.EmitUpdate(inc.Last(0))
} }

View File

@ -9,7 +9,7 @@ import (
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/indicator" "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/strategy/common" "github.com/c9s/bbgo/pkg/strategy/common"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
@ -52,10 +52,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
fastRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.FastWindow}) fastRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.FastWindow})
slowRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.SlowWindow}) slowRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.SlowWindow})
rsiCross := indicator.Cross(fastRsi, slowRsi) rsiCross := indicatorv2.Cross(fastRsi, slowRsi)
rsiCross.OnUpdate(func(v float64) { rsiCross.OnUpdate(func(v float64) {
switch indicator.CrossType(v) { switch indicatorv2.CrossType(v) {
case indicator.CrossOver: case indicatorv2.CrossOver:
opts := s.OpenPositionOptions opts := s.OpenPositionOptions
opts.Long = true opts.Long = true
@ -69,7 +69,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
logErr(err, "unable to open position") logErr(err, "unable to open position")
} }
case indicator.CrossUnder: case indicatorv2.CrossUnder:
if err := s.OrderExecutor.ClosePosition(ctx, fixedpoint.One); err != nil { if err := s.OrderExecutor.ClosePosition(ctx, fixedpoint.One); err != nil {
logErr(err, "failed to close position") logErr(err, "failed to close position")
} }

View File

@ -2,24 +2,24 @@ package scmaker
import ( import (
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/indicator" indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
type IntensityStream struct { type IntensityStream struct {
*indicator.Float64Series *types.Float64Series
Buy, Sell *indicator.RMAStream Buy, Sell *indicatorv2.RMAStream
window int window int
} }
func Intensity(source indicator.KLineSubscription, window int) *IntensityStream { func Intensity(source indicatorv2.KLineSubscription, window int) *IntensityStream {
s := &IntensityStream{ s := &IntensityStream{
Float64Series: indicator.NewFloat64Series(), Float64Series: types.NewFloat64Series(),
window: window, window: window,
Buy: indicator.RMA2(indicator.NewFloat64Series(), window, false), Buy: indicatorv2.RMA2(types.NewFloat64Series(), window, false),
Sell: indicator.RMA2(indicator.NewFloat64Series(), window, false), Sell: indicatorv2.RMA2(types.NewFloat64Series(), window, false),
} }
threshold := fixedpoint.NewFromFloat(100.0) threshold := fixedpoint.NewFromFloat(100.0)

View File

@ -10,7 +10,7 @@ import (
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/indicator" . "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/risk/riskcontrol" "github.com/c9s/bbgo/pkg/risk/riskcontrol"
"github.com/c9s/bbgo/pkg/strategy/common" "github.com/c9s/bbgo/pkg/strategy/common"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
@ -73,8 +73,8 @@ type Strategy struct {
liquidityScale bbgo.Scale liquidityScale bbgo.Scale
// indicators // indicators
ewma *indicator.EWMAStream ewma *EWMAStream
boll *indicator.BOLLStream boll *BOLLStream
intensity *IntensityStream intensity *IntensityStream
positionRiskControl *riskcontrol.PositionRiskControl positionRiskControl *riskcontrol.PositionRiskControl
@ -172,7 +172,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
return nil return nil
} }
func (s *Strategy) preloadKLines(inc *indicator.KLineStream, session *bbgo.ExchangeSession, symbol string, interval types.Interval) { func (s *Strategy) preloadKLines(inc *KLineStream, session *bbgo.ExchangeSession, symbol string, interval types.Interval) {
if store, ok := session.MarketDataStore(symbol); ok { if store, ok := session.MarketDataStore(symbol); ok {
if kLinesData, ok := store.KLinesOfInterval(interval); ok { if kLinesData, ok := store.KLinesOfInterval(interval); ok {
for _, k := range *kLinesData { for _, k := range *kLinesData {
@ -183,23 +183,23 @@ func (s *Strategy) preloadKLines(inc *indicator.KLineStream, session *bbgo.Excha
} }
func (s *Strategy) initializeMidPriceEMA(session *bbgo.ExchangeSession) { func (s *Strategy) initializeMidPriceEMA(session *bbgo.ExchangeSession) {
kLines := indicator.KLines(session.MarketDataStream, s.Symbol, s.MidPriceEMA.Interval) kLines := KLines(session.MarketDataStream, s.Symbol, s.MidPriceEMA.Interval)
s.ewma = indicator.EWMA2(indicator.ClosePrices(kLines), s.MidPriceEMA.Window) s.ewma = EWMA2(ClosePrices(kLines), s.MidPriceEMA.Window)
s.preloadKLines(kLines, session, s.Symbol, s.MidPriceEMA.Interval) s.preloadKLines(kLines, session, s.Symbol, s.MidPriceEMA.Interval)
} }
func (s *Strategy) initializeIntensityIndicator(session *bbgo.ExchangeSession) { func (s *Strategy) initializeIntensityIndicator(session *bbgo.ExchangeSession) {
kLines := indicator.KLines(session.MarketDataStream, s.Symbol, s.StrengthInterval) kLines := KLines(session.MarketDataStream, s.Symbol, s.StrengthInterval)
s.intensity = Intensity(kLines, 10) s.intensity = Intensity(kLines, 10)
s.preloadKLines(kLines, session, s.Symbol, s.StrengthInterval) s.preloadKLines(kLines, session, s.Symbol, s.StrengthInterval)
} }
func (s *Strategy) initializePriceRangeBollinger(session *bbgo.ExchangeSession) { func (s *Strategy) initializePriceRangeBollinger(session *bbgo.ExchangeSession) {
kLines := indicator.KLines(session.MarketDataStream, s.Symbol, s.PriceRangeBollinger.Interval) kLines := KLines(session.MarketDataStream, s.Symbol, s.PriceRangeBollinger.Interval)
closePrices := indicator.ClosePrices(kLines) closePrices := ClosePrices(kLines)
s.boll = indicator.BOLL2(closePrices, s.PriceRangeBollinger.Window, s.PriceRangeBollinger.K) s.boll = BOLL(closePrices, s.PriceRangeBollinger.Window, s.PriceRangeBollinger.K)
s.preloadKLines(kLines, session, s.Symbol, s.PriceRangeBollinger.Interval) s.preloadKLines(kLines, session, s.Symbol, s.PriceRangeBollinger.Interval)
} }

View File

@ -1,4 +1,4 @@
package indicator package types
//go:generate callbackgen -type Float64Updater //go:generate callbackgen -type Float64Updater
type Float64Updater struct { type Float64Updater struct {

View File

@ -0,0 +1,15 @@
// Code generated by "callbackgen -type Float64Updater"; DO NOT EDIT.
package types
import ()
func (F *Float64Updater) OnUpdate(cb func(v float64)) {
F.updateCallbacks = append(F.updateCallbacks, cb)
}
func (F *Float64Updater) EmitUpdate(v float64) {
for _, cb := range F.updateCallbacks {
cb(v)
}
}

View File

@ -645,3 +645,41 @@ func KLineWith(symbol string, interval Interval, callback KLineCallback) KLineCa
callback(k) callback(k)
} }
} }
type KLineValueMapper func(k KLine) float64
func KLineOpenPriceMapper(k KLine) float64 {
return k.Open.Float64()
}
func KLineClosePriceMapper(k KLine) float64 {
return k.Close.Float64()
}
func KLineTypicalPriceMapper(k KLine) float64 {
return (k.High.Float64() + k.Low.Float64() + k.Close.Float64()) / 3.
}
func KLinePriceVolumeMapper(k KLine) float64 {
return k.Close.Mul(k.Volume).Float64()
}
func KLineVolumeMapper(k KLine) float64 {
return k.Volume.Float64()
}
func MapKLinePrice(kLines []KLine, f KLineValueMapper) (prices []float64) {
for _, k := range kLines {
prices = append(prices, f(k))
}
return prices
}
func KLineLowPriceMapper(k KLine) float64 {
return k.Low.Float64()
}
func KLineHighPriceMapper(k KLine) float64 {
return k.High.Float64()
}

View File

@ -1,25 +1,24 @@
package indicator package types
import ( import (
"github.com/c9s/bbgo/pkg/datatype/floats" "github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
) )
type Float64Series struct { type Float64Series struct {
types.SeriesBase SeriesBase
Float64Updater Float64Updater
slice floats.Slice Slice floats.Slice
} }
func NewFloat64Series(v ...float64) *Float64Series { func NewFloat64Series(v ...float64) *Float64Series {
s := &Float64Series{} s := &Float64Series{}
s.slice = v s.Slice = v
s.SeriesBase.Series = s.slice s.SeriesBase.Series = s.Slice
return s return s
} }
func (f *Float64Series) Last(i int) float64 { func (f *Float64Series) Last(i int) float64 {
return f.slice.Last(i) return f.Slice.Last(i)
} }
func (f *Float64Series) Index(i int) float64 { func (f *Float64Series) Index(i int) float64 {
@ -27,15 +26,11 @@ func (f *Float64Series) Index(i int) float64 {
} }
func (f *Float64Series) Length() int { func (f *Float64Series) Length() int {
return len(f.slice) return len(f.Slice)
}
func (f *Float64Series) Slice() floats.Slice {
return f.slice
} }
func (f *Float64Series) PushAndEmit(x float64) { func (f *Float64Series) PushAndEmit(x float64) {
f.slice.Push(x) f.Slice.Push(x)
f.EmitUpdate(x) f.EmitUpdate(x)
} }
@ -71,3 +66,22 @@ func (f *Float64Series) Bind(source Float64Source, target Float64Calculator) {
f.Subscribe(source, c) f.Subscribe(source, c)
} }
} }
type Float64Calculator interface {
Calculate(x float64) float64
PushAndEmit(x float64)
}
type Float64Source interface {
Series
OnUpdate(f func(v float64))
}
type Float64Subscription interface {
Series
AddSubscriber(f func(v float64))
}
type Float64Truncator interface {
Truncate()
}