improve config loader

This commit is contained in:
c9s 2020-10-20 14:32:22 +08:00
parent 4ee10de40f
commit 324a493aad
5 changed files with 130 additions and 89 deletions

105
pkg/config/loader.go Normal file
View File

@ -0,0 +1,105 @@
package config
import (
"encoding/json"
"io/ioutil"
"reflect"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
"github.com/c9s/bbgo/pkg/bbgo"
)
type Config struct {
ExchangeStrategies []bbgo.SingleExchangeStrategy
CrossExchangeStrategies []bbgo.CrossExchangeStrategy
}
type Stash map[string]interface{}
func loadStash(configFile string) (Stash, error) {
config, err := ioutil.ReadFile(configFile)
if err != nil {
return nil, err
}
stash := make(Stash)
if err := yaml.Unmarshal(config, stash); err != nil {
return nil, err
}
return stash, err
}
func Load(configFile string) (*Config, error) {
var config Config
stash, err := loadStash(configFile)
if err != nil {
return nil, err
}
strategies, err := loadExchangeStrategies(stash)
if err != nil {
return nil, err
}
config.ExchangeStrategies = strategies
return &config, nil
}
func loadExchangeStrategies(stash Stash) (strategies []bbgo.SingleExchangeStrategy, err error) {
exchangeStrategiesConf, ok := stash["exchangeStrategies"]
if !ok {
return nil, errors.New("exchangeStrategies is not defined")
}
list, ok := exchangeStrategiesConf.([]interface{})
if !ok {
return nil, errors.New("exchangeStrategies should be a list")
}
for _, entry := range list {
configStash, ok := entry.(Stash)
if !ok {
return nil, errors.Errorf("strategy config should be a map, given: %T %+v", entry, entry)
}
for id, conf := range configStash {
// look up the real struct type
if st, ok := bbgo.LoadedExchangeStrategies[id]; ok {
val, err := reUnmarshal(conf, st)
if err != nil {
return nil, err
}
strategies = append(strategies, val.(bbgo.SingleExchangeStrategy))
}
}
}
return strategies, nil
}
func reUnmarshal(conf interface{}, tpe interface{}) (interface{}, error) {
// get the type "*Strategy"
rt := reflect.TypeOf(tpe)
// allocate new object from the given type
val := reflect.New(rt)
// now we have &(*Strategy) -> **Strategy
valRef := val.Interface()
plain, err := json.Marshal(conf)
if err != nil {
return nil, err
}
if err := json.Unmarshal(plain, valRef); err != nil {
return nil, errors.Wrapf(err, "json parsing error, given payload: %s", plain)
}
return val.Elem().Interface(), nil
}

View File

@ -1,4 +1,4 @@
package loader
package config
import (
"testing"
@ -32,18 +32,21 @@ func TestLoadStrategies(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strategies, err := LoadExchangeStrategies(tt.args.configFile)
config, err := Load(tt.args.configFile)
if err != nil {
t.Errorf("LoadExchangeStrategies() error = %v", err)
t.Errorf("Load() error = %v", err)
return
} else {
if tt.wantErr {
t.Errorf("LoadExchangeStrategies() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr)
return
}
}
t.Logf("%+v", strategies[0])
assert.NotNil(t, config)
assert.Len(t, config.ExchangeStrategies, tt.length)
assert.Len(t, strategies, tt.length)
t.Logf("%+v", config.ExchangeStrategies[0])
})
}
}

16
pkg/config/testdata/strategy.yaml vendored Normal file
View File

@ -0,0 +1,16 @@
---
sessions:
max:
exchange: max
keyVar: MAX_API_KEY
secretVar: MAX_API_SECRET
binance:
exchange: binance
keyVar: BINANCE_API_KEY
secretVar: BINANCE_API_SECRET
exchangeStrategies:
- buyandhold:
symbol: "BTCUSDT"
interval: "1m"
baseQuantity: 0.1
minDropPercentage: -0.05

View File

@ -1,76 +0,0 @@
package loader
import (
"encoding/json"
"io/ioutil"
"reflect"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
"github.com/c9s/bbgo/pkg/bbgo"
)
type Stash map[string]interface{}
func Load(configFile string) (Stash, error) {
config, err := ioutil.ReadFile(configFile)
if err != nil {
return nil, err
}
stash := make(Stash)
if err := yaml.Unmarshal(config, stash); err != nil {
return nil, err
}
return stash, err
}
func LoadExchangeStrategies(configFile string) (strategies []bbgo.SingleExchangeStrategy, err error) {
stash, err := Load(configFile)
if err != nil {
return nil, err
}
exchangeStrategiesConf, ok := stash["exchangeStrategies"]
if !ok {
return nil, errors.New("exchangeStrategies is not defined")
}
strategiesConfList, ok := exchangeStrategiesConf.([]interface{})
if !ok {
return nil, errors.New("exchangeStrategies should be a list")
}
for _, strategiesConf := range strategiesConfList {
sConf, ok := strategiesConf.(map[string]interface{})
if !ok {
return nil, errors.New("strategy config should be a map")
}
for id, conf := range sConf {
if st, ok := bbgo.LoadedExchangeStrategies[id]; ok {
// get the type "*Strategy"
rt := reflect.TypeOf(st)
val := reflect.New(rt)
// now we have &(*Strategy) -> **Strategy
valRef := val.Interface()
plain, err := json.Marshal(conf)
if err != nil {
return nil, err
}
if err := json.Unmarshal(plain, valRef); err != nil {
return nil, errors.Wrapf(err, "json parsing error, given payload: %s", plain)
}
strategies = append(strategies, val.Elem().Interface().(bbgo.SingleExchangeStrategy))
}
}
}
return strategies, nil
}

View File

@ -1,7 +0,0 @@
---
exchangeStrategies:
- buyandhold:
symbol: "BTCUSDT"
interval: "1m"
baseQuantity: 0.1
minDropPercentage: -0.05