bbgo: implmenet reflectMergeStructFields so that we can merge field values

This commit is contained in:
c9s 2022-06-29 18:39:16 +08:00
parent ab3341d5ae
commit fa917b0b77
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
3 changed files with 105 additions and 18 deletions

View File

@ -3,6 +3,8 @@ package bbgo
import (
"context"
log "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
@ -15,7 +17,10 @@ import (
// > SELECT start_time, `interval`, quote_volume, open, close FROM binance_klines WHERE symbol = 'ETHUSDT' AND `interval` = '5m' ORDER BY quote_volume DESC LIMIT 20;
//
type CumulatedVolumeTakeProfit struct {
Symbol string `json:"symbol"`
types.IntervalWindow
Ratio fixedpoint.Value `json:"ratio"`
MinQuoteVolume fixedpoint.Value `json:"minQuoteVolume"`
@ -32,7 +37,7 @@ func (s *CumulatedVolumeTakeProfit) Bind(session *ExchangeSession, orderExecutor
store, _ := session.MarketDataStore(position.Symbol)
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
if kline.Symbol != position.Symbol || kline.Interval != types.Interval1m {
if kline.Symbol != position.Symbol || kline.Interval != s.Interval {
return
}
@ -46,25 +51,33 @@ func (s *CumulatedVolumeTakeProfit) Bind(session *ExchangeSession, orderExecutor
return
}
if klines, ok := store.KLinesOfInterval(s.Interval); ok {
var cbv = fixedpoint.Zero
var cqv = fixedpoint.Zero
for i := 0; i < s.Window; i++ {
last := (*klines)[len(*klines)-1-i]
cqv = cqv.Add(last.QuoteVolume)
cbv = cbv.Add(last.Volume)
}
klines, ok := store.KLinesOfInterval(s.Interval)
if !ok {
log.Warnf("history kline not found")
return
}
if cqv.Compare(s.MinQuoteVolume) > 0 {
Notify("%s TakeProfit triggered by cumulated volume (window: %d) %f > %f, price = %f",
position.Symbol,
s.Window,
cqv.Float64(),
s.MinQuoteVolume.Float64(), kline.Close.Float64())
if len(*klines) < s.Window {
return
}
_ = orderExecutor.ClosePosition(context.Background(), fixedpoint.One, "cumulatedVolumeTakeProfit")
return
}
var cbv = fixedpoint.Zero
var cqv = fixedpoint.Zero
for i := 0; i < s.Window; i++ {
last := (*klines)[len(*klines)-1-i]
cqv = cqv.Add(last.QuoteVolume)
cbv = cbv.Add(last.Volume)
}
if cqv.Compare(s.MinQuoteVolume) > 0 {
Notify("%s TakeProfit triggered by cumulated volume (window: %d) %f > %f, price = %f",
position.Symbol,
s.Window,
cqv.Float64(),
s.MinQuoteVolume.Float64(), kline.Close.Float64())
_ = orderExecutor.ClosePosition(context.Background(), fixedpoint.One, "cumulatedVolumeTakeProfit")
return
}
})
}

View File

@ -111,6 +111,7 @@ func newTypeValueInterface(typ reflect.Type) interface{} {
return dst.Interface()
}
// toReflectValues convert the go objects into reflect.Value slice
func toReflectValues(args ...interface{}) (values []reflect.Value) {
for _, arg := range args {
values = append(values, reflect.ValueOf(arg))
@ -118,3 +119,25 @@ func toReflectValues(args ...interface{}) (values []reflect.Value) {
return values
}
func reflectMergeStructFields(dst, src interface{}) {
rtA := reflect.TypeOf(dst)
srcStructType := reflect.TypeOf(src)
rtA = rtA.Elem()
srcStructType = srcStructType.Elem()
for i := 0; i < rtA.NumField(); i++ {
fieldType := rtA.Field(i)
fieldName := fieldType.Name
if fieldSrcType, ok := srcStructType.FieldByName(fieldName); ok {
if fieldSrcType.Type == fieldType.Type {
srcValue := reflect.ValueOf(src).Elem().FieldByName(fieldName)
dstValue := reflect.ValueOf(dst).Elem().FieldByName(fieldName)
if (fieldType.Type.Kind() == reflect.Ptr && dstValue.IsNil()) || dstValue.IsZero() {
dstValue.Set(srcValue)
}
}
}
}
}

51
pkg/bbgo/reflect_test.go Normal file
View File

@ -0,0 +1,51 @@
package bbgo
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/c9s/bbgo/pkg/types"
)
func Test_reflectMergeStructFields(t *testing.T) {
t.Run("zero value", func(t *testing.T) {
a := &TestStrategy{Symbol: "BTCUSDT"}
b := &CumulatedVolumeTakeProfit{Symbol: ""}
reflectMergeStructFields(b, a)
assert.Equal(t, "BTCUSDT", b.Symbol)
})
t.Run("non-zero value", func(t *testing.T) {
a := &TestStrategy{Symbol: "BTCUSDT"}
b := &CumulatedVolumeTakeProfit{Symbol: "ETHUSDT"}
reflectMergeStructFields(b, a)
assert.Equal(t, "ETHUSDT", b.Symbol, "should be the original value")
})
t.Run("zero embedded struct", func(t *testing.T) {
iw := types.IntervalWindow{Interval: types.Interval1h, Window: 30}
a := &struct {
types.IntervalWindow
}{
IntervalWindow: iw,
}
b := &CumulatedVolumeTakeProfit{}
reflectMergeStructFields(b, a)
assert.Equal(t, iw, b.IntervalWindow)
})
t.Run("non-zero embedded struct", func(t *testing.T) {
iw := types.IntervalWindow{Interval: types.Interval1h, Window: 30}
a := &struct {
types.IntervalWindow
}{
IntervalWindow: iw,
}
b := &CumulatedVolumeTakeProfit{
IntervalWindow: types.IntervalWindow{Interval: types.Interval5m, Window: 9},
}
reflectMergeStructFields(b, a)
assert.Equal(t, types.IntervalWindow{Interval: types.Interval5m, Window: 9}, b.IntervalWindow)
})
}