diff --git a/config/bbgo.yaml b/config/bbgo.yaml index 49a767bd3..530ca0e48 100644 --- a/config/bbgo.yaml +++ b/config/bbgo.yaml @@ -11,6 +11,15 @@ reportTrades: "bnbusdt": "bbgo-bnbusdt" "sxpusdt": "bbgo-sxpusdt" +reportPnL: + - averageCostBySymbols: + - "BTCUSDT" + - "BNBUSDT" + of: binance + when: + - @daily + - @hourly + sessions: max: exchange: max @@ -22,9 +31,9 @@ sessions: secretVar: BINANCE_API_SECRET exchangeStrategies: -- on: binance - buyandhold: - symbol: "BTCUSDT" - interval: "1m" - baseQuantity: 0.1 - minDropPercentage: -0.05 + - on: binance + buyandhold: + symbol: "BTCUSDT" + interval: "1m" + baseQuantity: 0.1 + minDropPercentage: -0.05 diff --git a/pkg/config/loader.go b/pkg/config/loader.go index f90978743..be110c7e3 100644 --- a/pkg/config/loader.go +++ b/pkg/config/loader.go @@ -16,9 +16,40 @@ type SingleExchangeStrategyConfig struct { Strategy bbgo.SingleExchangeStrategy } +type StringSlice []string + +func (s *StringSlice) UnmarshalJSON(b []byte) (err error) { + var a interface{} + err = json.Unmarshal(b, &a) + if err != nil { + return err + } + + switch d := a.(type) { + case string: + *s = append(*s, d) + + case []string: + *s = append(*s, d...) + + default: + err = errors.New("unexpected type for StringSlice") + } + + return err +} + +type PnLReporter struct { + AverageCostBySymbols StringSlice `json:"averageCostBySymbols"` + Of StringSlice `json:"of" yaml:"of"` + When StringSlice `json:"when" yaml:"when"` +} + type Config struct { ExchangeStrategies []SingleExchangeStrategyConfig CrossExchangeStrategies []bbgo.CrossExchangeStrategy + + PnLReporters []PnLReporter `json:"reportPnL"` } type Stash map[string]interface{} @@ -45,38 +76,51 @@ func Load(configFile string) (*Config, error) { return nil, err } - strategies, err := loadExchangeStrategies(stash) - if err != nil { + if err := loadExchangeStrategies(&config, stash); err != nil { return nil, err } - config.ExchangeStrategies = strategies - - crossExchangeStrategies, err := loadCrossExchangeStrategies(stash) - if err != nil { + if err := loadCrossExchangeStrategies(&config, stash); err != nil { return nil, err } - config.CrossExchangeStrategies = crossExchangeStrategies + if err := loadReportPnL(&config, stash); err != nil { + return nil, err + } return &config, nil } -func loadCrossExchangeStrategies(stash Stash) (strategies []bbgo.CrossExchangeStrategy, err error) { +func loadReportPnL(config *Config, stash Stash) error { + reporterStash, ok := stash["reportPnL"] + if !ok { + return nil + } + + reporters, err := reUnmarshal(reporterStash, &config.PnLReporters) + if err != nil { + return err + } + + config.PnLReporters = *(reporters.(*[]PnLReporter)) + return nil +} + +func loadCrossExchangeStrategies(config *Config, stash Stash) (err error) { exchangeStrategiesConf, ok := stash["crossExchangeStrategies"] if !ok { - return strategies, nil + return nil } configList, ok := exchangeStrategiesConf.([]interface{}) if !ok { - return nil, errors.New("expecting list in crossExchangeStrategies") + return errors.New("expecting list in crossExchangeStrategies") } for _, entry := range configList { configStash, ok := entry.(Stash) if !ok { - return nil, errors.Errorf("strategy config should be a map, given: %T %+v", entry, entry) + return errors.Errorf("strategy config should be a map, given: %T %+v", entry, entry) } for id, conf := range configStash { @@ -84,34 +128,32 @@ func loadCrossExchangeStrategies(stash Stash) (strategies []bbgo.CrossExchangeSt if st, ok := bbgo.LoadedExchangeStrategies[id]; ok { val, err := reUnmarshal(conf, st) if err != nil { - return nil, err + return err } - strategies = append(strategies, val.(bbgo.CrossExchangeStrategy)) + config.CrossExchangeStrategies = append(config.CrossExchangeStrategies, val.(bbgo.CrossExchangeStrategy)) } } - } - return strategies, nil + return nil } -func loadExchangeStrategies(stash Stash) (strategies []SingleExchangeStrategyConfig, err error) { +func loadExchangeStrategies(config *Config, stash Stash) (err error) { exchangeStrategiesConf, ok := stash["exchangeStrategies"] if !ok { - return strategies, nil - // return nil, errors.New("exchangeStrategies is not defined") + return nil } configList, ok := exchangeStrategiesConf.([]interface{}) if !ok { - return nil, errors.New("expecting list in exchangeStrategies") + return errors.New("expecting list in exchangeStrategies") } for _, entry := range configList { configStash, ok := entry.(Stash) if !ok { - return nil, errors.Errorf("strategy config should be a map, given: %T %+v", entry, entry) + return errors.Errorf("strategy config should be a map, given: %T %+v", entry, entry) } var mounts []string @@ -128,10 +170,10 @@ func loadExchangeStrategies(stash Stash) (strategies []SingleExchangeStrategyCon if st, ok := bbgo.LoadedExchangeStrategies[id]; ok { val, err := reUnmarshal(conf, st) if err != nil { - return nil, err + return err } - strategies = append(strategies, SingleExchangeStrategyConfig{ + config.ExchangeStrategies = append(config.ExchangeStrategies, SingleExchangeStrategyConfig{ Mounts: mounts, Strategy: val.(bbgo.SingleExchangeStrategy), }) @@ -139,7 +181,7 @@ func loadExchangeStrategies(stash Stash) (strategies []SingleExchangeStrategyCon } } - return strategies, nil + return nil } func reUnmarshal(conf interface{}, tpe interface{}) (interface{}, error) { diff --git a/pkg/config/testdata/strategy.yaml b/pkg/config/testdata/strategy.yaml index 624b0290e..28a72c4e8 100644 --- a/pkg/config/testdata/strategy.yaml +++ b/pkg/config/testdata/strategy.yaml @@ -15,3 +15,12 @@ exchangeStrategies: interval: "1m" baseQuantity: 0.1 minDropPercentage: -0.05 + +reportPnL: + - averageCostBySymbols: + - "BTCUSDT" + - "BNBUSDT" + of: binance + when: + - "@daily" + - "@hourly"