mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
bbgo: implmenet reflectMergeStructFields so that we can merge field values
This commit is contained in:
parent
ab3341d5ae
commit
fa917b0b77
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
51
pkg/bbgo/reflect_test.go
Normal 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)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user