fix: move some modify implementation to dynamic

This commit is contained in:
zenix 2022-09-14 12:38:22 +09:00
parent d40b34e4d6
commit aaa657dcc3
6 changed files with 95 additions and 31 deletions

View File

@ -325,8 +325,6 @@ type Config struct {
CrossExchangeStrategies []CrossExchangeStrategy `json:"-" yaml:"-"` CrossExchangeStrategies []CrossExchangeStrategy `json:"-" yaml:"-"`
PnLReporters []PnLReporterConfig `json:"reportPnL,omitempty" yaml:"reportPnL,omitempty"` PnLReporters []PnLReporterConfig `json:"reportPnL,omitempty" yaml:"reportPnL,omitempty"`
ConfigFile string
} }
func (c *Config) Map() (map[string]interface{}, error) { func (c *Config) Map() (map[string]interface{}, error) {
@ -480,10 +478,6 @@ func Load(configFile string, loadStrategies bool) (*Config, error) {
if err := yaml.Unmarshal(content, &config); err != nil { if err := yaml.Unmarshal(content, &config); err != nil {
return nil, err return nil, err
} }
config.ConfigFile = configFile
for _, session := range config.Sessions {
session.ConfigPath = config.ConfigFile
}
// for backward compatible // for backward compatible
if config.Build == nil { if config.Build == nil {

View File

@ -4,8 +4,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"strings"
"github.com/c9s/bbgo/pkg/dynamic"
"github.com/c9s/bbgo/pkg/interact" "github.com/c9s/bbgo/pkg/interact"
"github.com/c9s/bbgo/pkg/util" "github.com/c9s/bbgo/pkg/util"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -23,37 +23,33 @@ func RegisterModifier(s interface{}) {
RegisterCommand("/modify", "Modify config", func(reply interact.Reply) { RegisterCommand("/modify", "Modify config", func(reply interact.Reply) {
reply.Message("Please choose the field name in config to modify:") reply.Message("Please choose the field name in config to modify:")
mapping = make(map[string]string) mapping = make(map[string]string)
for i := 0; i < val.Type().NumField(); i++ { dynamic.GetModifiableFields(val, func(tagName, name string) {
t := val.Type().Field(i) mapping[tagName] = name
if !t.IsExported() { reply.AddButton(tagName, tagName, tagName)
continue })
} }).Next(func(target string, reply interact.Reply) error {
modifiable := t.Tag.Get("modifiable")
if modifiable != "true" {
continue
}
jsonTag := t.Tag.Get("json")
if jsonTag == "" || jsonTag == "-" {
continue
}
name := strings.Split(jsonTag, ",")[0]
mapping[name] = t.Name
reply.AddButton(name, name, name)
}
}).Next(func(target string, reply interact.Reply) {
targetName = mapping[target] targetName = mapping[target]
field := val.FieldByName(targetName) field, ok := dynamic.GetModifiableField(val, targetName)
if !ok {
reply.Message(fmt.Sprintf("target %s is not modifiable", targetName))
return fmt.Errorf("target %s is not modifiable", targetName)
}
currVal = field.Interface() currVal = field.Interface()
if e, err := json.Marshal(currVal); err == nil { if e, err := json.Marshal(currVal); err == nil {
currVal = string(e) currVal = string(e)
} }
reply.Message(fmt.Sprintf("Please enter the new value, current value: %v", currVal)) reply.Message(fmt.Sprintf("Please enter the new value, current value: %v", currVal))
return nil
}).Next(func(value string, reply interact.Reply) { }).Next(func(value string, reply interact.Reply) {
log.Infof("%s", value) log.Infof("try to modify from %s to %s", currVal, value)
if kc, ok := reply.(interact.KeyboardController); ok { if kc, ok := reply.(interact.KeyboardController); ok {
kc.RemoveKeyboard() kc.RemoveKeyboard()
} }
field := val.FieldByName(targetName) field, ok := dynamic.GetModifiableField(val, targetName)
if !ok {
reply.Message(fmt.Sprintf("target %s is not modifiable", targetName))
return
}
x := reflect.New(field.Type()) x := reflect.New(field.Type())
xi := x.Interface() xi := x.Interface()
if err := json.Unmarshal([]byte(value), &xi); err != nil { if err := json.Unmarshal([]byte(value), &xi); err != nil {

View File

@ -40,7 +40,6 @@ type ExchangeSession struct {
Secret string `json:"secret,omitempty" yaml:"secret,omitempty"` Secret string `json:"secret,omitempty" yaml:"secret,omitempty"`
Passphrase string `json:"passphrase,omitempty" yaml:"passphrase,omitempty"` Passphrase string `json:"passphrase,omitempty" yaml:"passphrase,omitempty"`
SubAccount string `json:"subAccount,omitempty" yaml:"subAccount,omitempty"` SubAccount string `json:"subAccount,omitempty" yaml:"subAccount,omitempty"`
ConfigPath string
// Withdrawal is used for enabling withdrawal functions // Withdrawal is used for enabling withdrawal functions
Withdrawal bool `json:"withdrawal,omitempty" yaml:"withdrawal,omitempty"` Withdrawal bool `json:"withdrawal,omitempty" yaml:"withdrawal,omitempty"`

View File

@ -267,7 +267,6 @@ var BacktestCmd = &cobra.Command{
exchangeFromConfig := userConfig.Sessions[name.String()] exchangeFromConfig := userConfig.Sessions[name.String()]
if exchangeFromConfig != nil { if exchangeFromConfig != nil {
session.UseHeikinAshi = exchangeFromConfig.UseHeikinAshi session.UseHeikinAshi = exchangeFromConfig.UseHeikinAshi
session.ConfigPath = exchangeFromConfig.ConfigPath
} }
} }

View File

@ -1,6 +1,9 @@
package dynamic package dynamic
import "reflect" import (
"reflect"
"strings"
)
func HasField(rs reflect.Value, fieldName string) (field reflect.Value, ok bool) { func HasField(rs reflect.Value, fieldName string) (field reflect.Value, ok bool) {
field = rs.FieldByName(fieldName) field = rs.FieldByName(fieldName)
@ -24,3 +27,39 @@ func LookupSymbolField(rs reflect.Value) (string, bool) {
return field.String(), true return field.String(), true
} }
// Used by bbgo/interact_modify.go
func GetModifiableFields(val reflect.Value, callback func(tagName, name string)) {
for i := 0; i < val.Type().NumField(); i++ {
t := val.Type().Field(i)
if !t.IsExported() {
continue
}
modifiable := t.Tag.Get("modifiable")
if modifiable != "true" {
continue
}
jsonTag := t.Tag.Get("json")
if jsonTag == "" || jsonTag == "-" {
continue
}
name := strings.Split(jsonTag, ",")[0]
callback(name, t.Name)
}
}
var ZeroValue reflect.Value = reflect.Zero(reflect.TypeOf(0))
func GetModifiableField(val reflect.Value, name string) (reflect.Value, bool) {
field, ok := val.Type().FieldByName(name)
if !ok {
return ZeroValue, ok
}
if field.Tag.Get("modifiable") != "true" {
return ZeroValue, false
}
jsonTag := field.Tag.Get("json")
if jsonTag == "" || jsonTag == "-" {
return ZeroValue, false
}
return val.FieldByName(name), true
}

37
pkg/dynamic/field_test.go Normal file
View File

@ -0,0 +1,37 @@
package dynamic
import (
"reflect"
"testing"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/stretchr/testify/assert"
)
type Strategy struct {
Field1 fixedpoint.Value `json:"field1" modifiable:"true"`
Field2 float64 `json:"field2"`
field3 float64 `json:"field3" modifiable:"true"`
}
func TestGetModifiableFields(t *testing.T) {
s := Strategy{}
val := reflect.ValueOf(s)
GetModifiableFields(val, func(tagName, name string) {
assert.Equal(t, tagName, "field1")
assert.Equal(t, name, "Field1")
})
}
func TestGetModifiableField(t *testing.T) {
s := Strategy{}
val := reflect.ValueOf(s)
_, ok := GetModifiableField(val, "Field1")
assert.True(t, ok)
_, ok = GetModifiableField(val, "Field2")
assert.False(t, ok)
_, ok = GetModifiableField(val, "Field3")
assert.False(t, ok)
_, ok = GetModifiableField(val, "Random")
assert.False(t, ok)
}