indicator: add cross stream

This commit is contained in:
c9s 2023-06-04 14:47:29 +08:00
parent 7f3f2c1217
commit ca78a3379a
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
3 changed files with 130 additions and 70 deletions

View File

@ -0,0 +1,71 @@
package indicator
import (
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
)
type Float64Series struct {
types.SeriesBase
Float64Updater
slice floats.Slice
}
func NewFloat64Series(v ...float64) Float64Series {
s := Float64Series{}
s.slice = v
s.SeriesBase.Series = s.slice
return s
}
func (f *Float64Series) Last(i int) float64 {
return f.slice.Last(i)
}
func (f *Float64Series) Index(i int) float64 {
return f.Last(i)
}
func (f *Float64Series) Length() int {
return len(f.slice)
}
func (f *Float64Series) Slice() floats.Slice {
return f.slice
}
func (f *Float64Series) PushAndEmit(x float64) {
f.slice.Push(x)
f.EmitUpdate(x)
}
func (f *Float64Series) Subscribe(source Float64Source, c func(x float64)) {
if sub, ok := source.(Float64Subscription); ok {
sub.AddSubscriber(c)
} else {
source.OnUpdate(c)
}
}
// Bind binds the source event to the target (Float64Calculator)
// A Float64Calculator should be able to calculate the float64 result from a single float64 argument input
func (f *Float64Series) Bind(source Float64Source, target Float64Calculator) {
var c func(x float64)
// optimize the truncation check
trc, canTruncate := target.(Float64Truncator)
if canTruncate {
c = func(x float64) {
y := target.Calculate(x)
target.PushAndEmit(y)
trc.Truncate()
}
} else {
c = func(x float64) {
y := target.Calculate(x)
target.PushAndEmit(y)
}
}
f.Subscribe(source, c)
}

View File

@ -1,76 +1,6 @@
package indicator package indicator
import (
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
)
//go:generate callbackgen -type Float64Updater //go:generate callbackgen -type Float64Updater
type Float64Updater struct { type Float64Updater struct {
updateCallbacks []func(v float64) updateCallbacks []func(v float64)
} }
type Float64Series struct {
types.SeriesBase
Float64Updater
slice floats.Slice
}
func NewFloat64Series(v ...float64) Float64Series {
s := Float64Series{}
s.slice = v
s.SeriesBase.Series = s.slice
return s
}
func (f *Float64Series) Last(i int) float64 {
return f.slice.Last(i)
}
func (f *Float64Series) Index(i int) float64 {
return f.Last(i)
}
func (f *Float64Series) Length() int {
return len(f.slice)
}
func (f *Float64Series) Slice() floats.Slice {
return f.slice
}
func (f *Float64Series) PushAndEmit(x float64) {
f.slice.Push(x)
f.EmitUpdate(x)
}
func (f *Float64Series) Subscribe(source Float64Source, c func(x float64)) {
if sub, ok := source.(Float64Subscription); ok {
sub.AddSubscriber(c)
} else {
source.OnUpdate(c)
}
}
// Bind binds the source event to the target (Float64Calculator)
// A Float64Calculator should be able to calculate the float64 result from a single float64 argument input
func (f *Float64Series) Bind(source Float64Source, target Float64Calculator) {
var c func(x float64)
// optimize the truncation check
trc, canTruncate := target.(Float64Truncator)
if canTruncate {
c = func(x float64) {
y := target.Calculate(x)
target.PushAndEmit(y)
trc.Truncate()
}
} else {
c = func(x float64) {
y := target.Calculate(x)
target.PushAndEmit(y)
}
}
f.Subscribe(source, c)
}

59
pkg/indicator/v2_cross.go Normal file
View File

@ -0,0 +1,59 @@
package indicator
import (
"github.com/c9s/bbgo/pkg/datatype/floats"
)
type CrossType float64
const (
CrossOver CrossType = 1.0
CrossUnder CrossType = -1.0
)
// CrossStream subscribes 2 upstreams, and calculate the cross signal
type CrossStream struct {
Float64Series
a, b floats.Slice
}
// Cross creates the CrossStream object:
//
// cross := Cross(fastEWMA, slowEWMA)
func Cross(a, b Float64Source) *CrossStream {
s := &CrossStream{
Float64Series: NewFloat64Series(),
}
a.OnUpdate(func(v float64) {
s.a.Push(v)
s.calculate()
})
b.OnUpdate(func(v float64) {
s.b.Push(v)
s.calculate()
})
return s
}
func (s *CrossStream) calculate() {
if s.a.Length() != s.b.Length() {
return
}
current := s.a.Last(0) - s.b.Last(0)
previous := s.a.Last(1) - s.b.Last(1)
if previous == 0.0 {
return
}
// cross over or cross under
if current*previous < 0 {
if current > 0 {
s.PushAndEmit(float64(CrossOver))
} else {
s.PushAndEmit(float64(CrossUnder))
}
}
}