From 2fbf19455e6d84f464584738824eb47536182b21 Mon Sep 17 00:00:00 2001 From: c9s Date: Tue, 20 Oct 2020 13:52:25 +0800 Subject: [PATCH] implement strategy yaml loader --- go.mod | 2 +- pkg/bbgo/environment.go | 7 ++++ pkg/bbgo/loader.go | 2 + pkg/bbgo/loader_test.go | 2 + pkg/loader/loader.go | 65 +++++++++++++++++++++++++++++ pkg/loader/loader_test.go | 48 +++++++++++++++++++++ pkg/loader/testdata/strategy.yaml | 6 +++ pkg/strategy/buyandhold/strategy.go | 4 ++ 8 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 pkg/bbgo/loader.go create mode 100644 pkg/bbgo/loader_test.go create mode 100644 pkg/loader/loader.go create mode 100644 pkg/loader/loader_test.go create mode 100644 pkg/loader/testdata/strategy.yaml diff --git a/go.mod b/go.mod index 8c141bacb..35bb2b9cb 100644 --- a/go.mod +++ b/go.mod @@ -39,5 +39,5 @@ require ( golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 gopkg.in/yaml.v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c // indirect + gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c ) diff --git a/pkg/bbgo/environment.go b/pkg/bbgo/environment.go index ab9858b97..8c2c33536 100644 --- a/pkg/bbgo/environment.go +++ b/pkg/bbgo/environment.go @@ -17,6 +17,13 @@ import ( "github.com/c9s/bbgo/pkg/types" ) +var LoadedStrategies = make(map[string]interface{}) + +func RegisterStrategy(key string, configmap interface{}) { + LoadedStrategies[key] = configmap +} + + // Environment presents the real exchange data layer type Environment struct { TradeService *service.TradeService diff --git a/pkg/bbgo/loader.go b/pkg/bbgo/loader.go new file mode 100644 index 000000000..920078f66 --- /dev/null +++ b/pkg/bbgo/loader.go @@ -0,0 +1,2 @@ +package bbgo + diff --git a/pkg/bbgo/loader_test.go b/pkg/bbgo/loader_test.go new file mode 100644 index 000000000..920078f66 --- /dev/null +++ b/pkg/bbgo/loader_test.go @@ -0,0 +1,2 @@ +package bbgo + diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go new file mode 100644 index 000000000..920bdc765 --- /dev/null +++ b/pkg/loader/loader.go @@ -0,0 +1,65 @@ +package loader + +import ( + "encoding/json" + "io/ioutil" + "reflect" + + "github.com/pkg/errors" + "gopkg.in/yaml.v3" + + "github.com/c9s/bbgo/pkg/bbgo" +) + +func LoadExchangeStrategies(configFile string) (strategies []bbgo.SingleExchangeStrategy, err error) { + config, err := ioutil.ReadFile(configFile) + if err != nil { + return nil, err + } + + conf := make(map[string]interface{}) + if err := yaml.Unmarshal(config, conf); err != nil { + return nil, err + } + + exchangeStrategiesConf, ok := conf["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.LoadedStrategies[id]; ok { + // get the type "*Strategy" + rt := reflect.TypeOf(st) + val := reflect.New(rt) + + // now we have &(*Strategy) -> **Strategy + valinf := val.Interface() + + plain, err := json.Marshal(conf) + if err != nil { + return nil, err + } + + if err := json.Unmarshal(plain, valinf); err != nil { + return nil, err + } + + strategies = append(strategies, val.Elem().Interface().(bbgo.SingleExchangeStrategy)) + } + } + } + + return strategies, nil +} diff --git a/pkg/loader/loader_test.go b/pkg/loader/loader_test.go new file mode 100644 index 000000000..99cb2b220 --- /dev/null +++ b/pkg/loader/loader_test.go @@ -0,0 +1,48 @@ +package loader + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + // register the strategies + _ "github.com/c9s/bbgo/pkg/strategy/buyandhold" +) + +func TestLoadStrategies(t *testing.T) { + type args struct { + configFile string + } + + tests := []struct { + name string + args args + wantErr bool + length int + }{ + { + name: "simple", + args: args{ + configFile: "testdata/strategy.yaml", + }, + wantErr: false, + length: 1, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + strategies, err := LoadExchangeStrategies(tt.args.configFile) + if err != nil { + t.Errorf("LoadExchangeStrategies() error = %v", err) + } else { + if tt.wantErr { + t.Errorf("LoadExchangeStrategies() error = %v, wantErr %v", err, tt.wantErr) + } + } + + t.Logf("%+v", strategies[0]) + + assert.Len(t, strategies, tt.length) + }) + } +} diff --git a/pkg/loader/testdata/strategy.yaml b/pkg/loader/testdata/strategy.yaml new file mode 100644 index 000000000..527a5fed3 --- /dev/null +++ b/pkg/loader/testdata/strategy.yaml @@ -0,0 +1,6 @@ +--- +exchangeStrategies: +- buyandhold: + symbol: "BTCUSDT" + interval: "1m" + minDropPercentage: -0.05 diff --git a/pkg/strategy/buyandhold/strategy.go b/pkg/strategy/buyandhold/strategy.go index 6c9f0fa2c..bba3ca96d 100644 --- a/pkg/strategy/buyandhold/strategy.go +++ b/pkg/strategy/buyandhold/strategy.go @@ -13,6 +13,10 @@ import ( "github.com/c9s/bbgo/pkg/util" ) +func init() { + bbgo.RegisterStrategy("buyandhold", &Strategy{}) +} + type Strategy struct { Symbol string `json:"symbol"` Interval string `json:"interval"`