mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 01:01:56 +00:00
all: refactor exit method set and fix dynamic call/merge
Signed-off-by: c9s <yoanlin93@gmail.com>
This commit is contained in:
parent
e2ab363e64
commit
b15e8d0ce4
|
@ -1,11 +1,25 @@
|
|||
package bbgo
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/dynamic"
|
||||
)
|
||||
|
||||
type ExitMethodSet []ExitMethod
|
||||
|
||||
func (s *ExitMethodSet) SetAndSubscribe(session *ExchangeSession, parent interface{}) {
|
||||
for i := range *s {
|
||||
m := (*s)[i]
|
||||
|
||||
// manually inherit configuration from strategy
|
||||
m.Inherit(parent)
|
||||
m.Subscribe(session)
|
||||
}
|
||||
}
|
||||
|
||||
type ExitMethod struct {
|
||||
RoiStopLoss *RoiStopLoss `json:"roiStopLoss"`
|
||||
ProtectiveStopLoss *ProtectiveStopLoss `json:"protectiveStopLoss"`
|
||||
|
@ -14,9 +28,29 @@ type ExitMethod struct {
|
|||
CumulatedVolumeTakeProfit *CumulatedVolumeTakeProfit `json:"cumulatedVolumeTakeProfit"`
|
||||
}
|
||||
|
||||
// Inherit is used for inheriting properties from the given strategy struct
|
||||
// for example, some exit method requires the default interval and symbol name from the strategy param object
|
||||
func (m *ExitMethod) Inherit(parent interface{}) {
|
||||
// we need to pass some information from the strategy configuration to the exit methods, like symbol, interval and window
|
||||
rt := reflect.TypeOf(m).Elem()
|
||||
rv := reflect.ValueOf(m).Elem()
|
||||
for j := 0; j < rv.NumField(); j++ {
|
||||
if !rt.Field(j).IsExported() {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldValue := rv.Field(j)
|
||||
if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
dynamic.MergeStructValues(fieldValue.Interface(), parent)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ExitMethod) Subscribe(session *ExchangeSession) {
|
||||
if err := dynamic.CallStructFieldsMethod(m, "Subscribe", session); err != nil {
|
||||
panic(errors.Wrap(err, "dynamic call failed"))
|
||||
panic(errors.Wrap(err, "dynamic Subscribe call failed"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,12 +8,21 @@ import (
|
|||
)
|
||||
|
||||
type LowerShadowTakeProfit struct {
|
||||
Ratio fixedpoint.Value `json:"ratio"`
|
||||
// inherit from the strategy
|
||||
types.IntervalWindow
|
||||
|
||||
// inherit from the strategy
|
||||
Symbol string `json:"symbol"`
|
||||
|
||||
Ratio fixedpoint.Value `json:"ratio"`
|
||||
session *ExchangeSession
|
||||
orderExecutor *GeneralOrderExecutor
|
||||
}
|
||||
|
||||
func (s *LowerShadowTakeProfit) Subscribe(session *ExchangeSession) {
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval})
|
||||
}
|
||||
|
||||
func (s *LowerShadowTakeProfit) Bind(session *ExchangeSession, orderExecutor *GeneralOrderExecutor) {
|
||||
s.session = session
|
||||
s.orderExecutor = orderExecutor
|
||||
|
|
|
@ -25,16 +25,28 @@ func CallStructFieldsMethod(m interface{}, method string, args ...interface{}) e
|
|||
argValues := ToReflectValues(args...)
|
||||
for i := 0; i < rt.NumField(); i++ {
|
||||
fieldType := rt.Field(i)
|
||||
fieldValue := rv.Field(i)
|
||||
|
||||
// skip non-exported fields
|
||||
if !fieldType.IsExported() {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := fieldType.Type.MethodByName(method); ok {
|
||||
refMethod := rv.Field(i).MethodByName(method)
|
||||
refMethod.Call(argValues)
|
||||
if fieldType.Type.Kind() == reflect.Ptr && fieldValue.IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
methodType, ok := fieldType.Type.MethodByName(method)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(argValues) < methodType.Type.NumIn() {
|
||||
// return fmt.Errorf("method %v require %d args, %d given", methodType, methodType.Type.NumIn(), len(argValues))
|
||||
}
|
||||
|
||||
refMethod := fieldValue.MethodByName(method)
|
||||
refMethod.Call(argValues)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -5,6 +5,10 @@ import "reflect"
|
|||
// MergeStructValues merges the field value from the source struct to the dest struct.
|
||||
// Only fields with the same type and the same name will be updated.
|
||||
func MergeStructValues(dst, src interface{}) {
|
||||
if dst == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rtA := reflect.TypeOf(dst)
|
||||
srcStructType := reflect.TypeOf(src)
|
||||
|
||||
|
@ -14,6 +18,11 @@ func MergeStructValues(dst, src interface{}) {
|
|||
for i := 0; i < rtA.NumField(); i++ {
|
||||
fieldType := rtA.Field(i)
|
||||
fieldName := fieldType.Name
|
||||
|
||||
if !fieldType.IsExported() {
|
||||
continue
|
||||
}
|
||||
|
||||
// if there is a field with the same name
|
||||
if fieldSrcType, ok := srcStructType.FieldByName(fieldName); ok {
|
||||
// ensure that the type is the same
|
||||
|
|
|
@ -36,8 +36,10 @@ func Test_reflectMergeStructFields(t *testing.T) {
|
|||
iw := types.IntervalWindow{Interval: types.Interval1h, Window: 30}
|
||||
a := &struct {
|
||||
types.IntervalWindow
|
||||
Symbol string
|
||||
}{
|
||||
IntervalWindow: iw,
|
||||
Symbol: "BTCUSDT",
|
||||
}
|
||||
b := &struct {
|
||||
Symbol string
|
||||
|
@ -45,6 +47,7 @@ func Test_reflectMergeStructFields(t *testing.T) {
|
|||
}{}
|
||||
MergeStructValues(b, a)
|
||||
assert.Equal(t, iw, b.IntervalWindow)
|
||||
assert.Equal(t, "BTCUSDT", b.Symbol)
|
||||
})
|
||||
|
||||
t.Run("non-zero embedded struct", func(t *testing.T) {
|
||||
|
|
|
@ -15,10 +15,10 @@ func NewTypeValueInterface(typ reflect.Type) interface{} {
|
|||
|
||||
// ToReflectValues convert the go objects into reflect.Value slice
|
||||
func ToReflectValues(args ...interface{}) (values []reflect.Value) {
|
||||
for _, arg := range args {
|
||||
for i := range args {
|
||||
arg := args[i]
|
||||
values = append(values, reflect.ValueOf(arg))
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/dynamic"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
|
@ -87,7 +86,7 @@ type Strategy struct {
|
|||
ResistanceShort *ResistanceShort `json:"resistanceShort"`
|
||||
|
||||
Entry Entry `json:"entry"`
|
||||
ExitMethods []bbgo.ExitMethod `json:"exits"`
|
||||
ExitMethods bbgo.ExitMethodSet `json:"exits"`
|
||||
|
||||
session *bbgo.ExchangeSession
|
||||
orderExecutor *bbgo.GeneralOrderExecutor
|
||||
|
@ -120,13 +119,7 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
|||
session.Subscribe(types.MarketTradeChannel, s.Symbol, types.SubscribeOptions{})
|
||||
}
|
||||
|
||||
for i := range s.ExitMethods {
|
||||
m := s.ExitMethods[i]
|
||||
|
||||
// we need to pass some information from the strategy configuration to the exit methods, like symbol, interval and window
|
||||
dynamic.MergeStructValues(&m, s)
|
||||
m.Subscribe(session)
|
||||
}
|
||||
s.ExitMethods.SetAndSubscribe(session, s)
|
||||
}
|
||||
|
||||
func (s *Strategy) InstanceID() string {
|
||||
|
|
Loading…
Reference in New Issue
Block a user