emit klines and setup account balances

This commit is contained in:
c9s 2020-11-07 03:18:05 +08:00
parent 22a214328d
commit b13a2deec5
7 changed files with 84 additions and 36 deletions

View File

@ -58,17 +58,9 @@ backtest:
USDT: 5000.0 USDT: 5000.0
exchangeStrategies: exchangeStrategies:
- on: binance - on: max
buyandhold: buyandhold:
symbol: "BTCUSDT" symbol: "BTCUSDT"
interval: "1m" interval: "1m"
baseQuantity: 0.01 baseQuantity: 0.01
minDropPercentage: -0.02 minDropPercentage: -0.02
- on: max
xpuremaker:
symbol: MAXUSDT
numOrders: 2
side: both
behindVolume: 1000.0
priceTick: 0.01
baseQuantity: 100.0

View File

@ -6,6 +6,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/exchange/binance" "github.com/c9s/bbgo/pkg/exchange/binance"
"github.com/c9s/bbgo/pkg/exchange/max" "github.com/c9s/bbgo/pkg/exchange/max"
"github.com/c9s/bbgo/pkg/service" "github.com/c9s/bbgo/pkg/service"
@ -18,27 +19,55 @@ type Exchange struct {
srv *service.BacktestService srv *service.BacktestService
startTime time.Time startTime time.Time
account *types.Account
config *bbgo.Backtest
closedOrders []types.SubmitOrder closedOrders []types.SubmitOrder
openOrders []types.SubmitOrder openOrders []types.SubmitOrder
stream *Stream
} }
func NewExchange(sourceExchange types.ExchangeName, srv *service.BacktestService, startTime time.Time) *Exchange { func NewExchange(sourceExchange types.ExchangeName, srv *service.BacktestService, config *bbgo.Backtest) *Exchange {
ex, err := newPublicExchange(sourceExchange) ex, err := newPublicExchange(sourceExchange)
if err != nil { if err != nil {
panic(err) panic(err)
} }
if config == nil {
panic(errors.New("backtest config can not be nil"))
}
startTime, err := config.ParseStartTime()
if err != nil {
panic(err)
}
balances := config.Account.Balances.BalanceMap()
account := &types.Account{
MakerCommission: config.Account.MakerCommission,
TakerCommission: config.Account.TakerCommission,
AccountType: "SPOT", // currently not used
}
account.UpdateBalances(balances)
return &Exchange{ return &Exchange{
sourceExchange: sourceExchange, sourceExchange: sourceExchange,
publicExchange: ex, publicExchange: ex,
srv: srv, srv: srv,
config: config,
account: account,
startTime: startTime, startTime: startTime,
} }
} }
func (e *Exchange) NewStream() types.Stream { func (e *Exchange) NewStream() types.Stream {
// TODO: return the stream and feed the data if e.stream != nil {
return &Stream{} panic("backtest stream is already allocated, please check if there are extra NewStream calls")
}
e.stream = &Stream{exchange: e}
return e.stream
} }
func (e Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder) (createdOrders types.OrderSlice, err error) { func (e Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder) (createdOrders types.OrderSlice, err error) {
@ -58,11 +87,11 @@ func (e Exchange) CancelOrders(ctx context.Context, orders ...types.Order) error
} }
func (e Exchange) QueryAccount(ctx context.Context) (*types.Account, error) { func (e Exchange) QueryAccount(ctx context.Context) (*types.Account, error) {
panic("implement me") return e.account, nil
} }
func (e Exchange) QueryAccountBalances(ctx context.Context) (types.BalanceMap, error) { func (e *Exchange) QueryAccountBalances(ctx context.Context) (types.BalanceMap, error) {
panic("implement me") return e.account.Balances(), nil
} }
func (e Exchange) QueryKLines(ctx context.Context, symbol string, interval types.Interval, options types.KLineQueryOptions) ([]types.KLine, error) { func (e Exchange) QueryKLines(ctx context.Context, symbol string, interval types.Interval, options types.KLineQueryOptions) ([]types.KLine, error) {

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
@ -15,6 +16,8 @@ type Stream struct {
} }
func (s *Stream) Connect(ctx context.Context) error { func (s *Stream) Connect(ctx context.Context) error {
log.Infof("collecting backtest configurations...")
loadedSymbols := map[string]struct{}{} loadedSymbols := map[string]struct{}{}
loadedIntervals := map[types.Interval]struct{}{} loadedIntervals := map[types.Interval]struct{}{}
for _, sub := range s.Subscriptions { for _, sub := range s.Subscriptions {
@ -39,6 +42,8 @@ func (s *Stream) Connect(ctx context.Context) error {
intervals = append(intervals, interval) intervals = append(intervals, interval)
} }
log.Infof("used symbols: %v and intervals: %v", symbols, intervals)
// TODO: we can sync before we connect // TODO: we can sync before we connect
/* /*
if err := backtestService.Sync(ctx, exchange, symbol, startTime); err != nil { if err := backtestService.Sync(ctx, exchange, symbol, startTime); err != nil {

View File

@ -4,11 +4,13 @@ import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"reflect" "reflect"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
) )
type PnLReporterConfig struct { type PnLReporterConfig struct {
@ -57,6 +59,14 @@ type Backtest struct {
Account BacktestAccount `json:"account" yaml:"account"` Account BacktestAccount `json:"account" yaml:"account"`
} }
func (t Backtest) ParseStartTime() (time.Time, error) {
if len(t.StartTime) == 0 {
return time.Time{}, errors.New("backtest.startTime must be defined")
}
return time.Parse("2006-01-02", t.StartTime)
}
type BacktestAccount struct { type BacktestAccount struct {
MakerCommission int `json:"makerCommission"` MakerCommission int `json:"makerCommission"`
TakerCommission int `json:"takerCommission"` TakerCommission int `json:"takerCommission"`
@ -67,6 +77,18 @@ type BacktestAccount struct {
type BacktestAccountBalanceMap map[string]fixedpoint.Value type BacktestAccountBalanceMap map[string]fixedpoint.Value
func (m BacktestAccountBalanceMap) BalanceMap() types.BalanceMap {
balances := make(types.BalanceMap)
for currency, value := range m {
balances[currency] = types.Balance{
Currency: currency,
Available: value.Float64(),
Locked: 0.0,
}
}
return balances
}
type Config struct { type Config struct {
Imports []string `json:"imports" yaml:"imports"` Imports []string `json:"imports" yaml:"imports"`

View File

@ -18,8 +18,7 @@ import (
func init() { func init() {
BacktestCmd.Flags().String("exchange", "", "target exchange") BacktestCmd.Flags().String("exchange", "", "target exchange")
BacktestCmd.Flags().String("start", "", "start time") BacktestCmd.Flags().Bool("sync", true, "sync backtest data")
BacktestCmd.Flags().Bool("backtest", true, "sync backtest data")
BacktestCmd.Flags().String("config", "config/bbgo.yaml", "strategy config file") BacktestCmd.Flags().String("config", "config/bbgo.yaml", "strategy config file")
RootCmd.AddCommand(BacktestCmd) RootCmd.AddCommand(BacktestCmd)
} }
@ -61,27 +60,29 @@ var BacktestCmd = &cobra.Command{
return err return err
} }
if userConfig.Backtest == nil {
return errors.New("backtest config is not defined")
}
// set default start time to the past 6 months // set default start time to the past 6 months
startTime := time.Now().AddDate(0, -6, 0) startTime := time.Now().AddDate(0, -6, 0)
if len(userConfig.Backtest.StartTime) == 0 {
startTimeArg, err := cmd.Flags().GetString("start") userConfig.Backtest.StartTime = startTime.Format("2006-01-02")
if err != nil {
return err
}
if len(startTimeArg) > 0 {
startTime, err = time.Parse("2006-01-02", startTimeArg)
if err != nil {
return err
}
} }
backtestService := &service.BacktestService{DB: db} backtestService := &service.BacktestService{DB: db}
exchange := backtest.NewExchange(exchangeName, backtestService, startTime) exchange := backtest.NewExchange(exchangeName, backtestService, userConfig.Backtest)
environ := bbgo.NewEnvironment() environ := bbgo.NewEnvironment()
environ.AddExchange(exchangeName.String(), exchange) environ.AddExchange(exchangeName.String(), exchange)
environ.Notifiability = bbgo.Notifiability{
SymbolChannelRouter: bbgo.NewPatternChannelRouter(nil),
SessionChannelRouter: bbgo.NewPatternChannelRouter(nil),
ObjectChannelRouter: bbgo.NewObjectChannelRouter(),
}
trader := bbgo.NewTrader(environ) trader := bbgo.NewTrader(environ)
if userConfig.RiskControls != nil { if userConfig.RiskControls != nil {
trader.SetRiskControls(userConfig.RiskControls) trader.SetRiskControls(userConfig.RiskControls)
@ -96,7 +97,7 @@ var BacktestCmd = &cobra.Command{
log.Warnf("backtest does not support CrossExchangeStrategy, strategies won't be added.") log.Warnf("backtest does not support CrossExchangeStrategy, strategies won't be added.")
} }
if err := trader.Run(ctx) ; err != nil { if err := trader.Run(ctx); err != nil {
return err return err
} }

View File

@ -262,8 +262,8 @@ func (e *Exchange) QueryAccount(ctx context.Context) (*types.Account, error) {
} }
a := &types.Account{ a := &types.Account{
MakerCommission: account.MakerCommission, MakerCommission: int(account.MakerCommission),
TakerCommission: account.TakerCommission, TakerCommission: int(account.TakerCommission),
} }
a.UpdateBalances(balances) a.UpdateBalances(balances)
return a, nil return a, nil
@ -537,4 +537,3 @@ func (e *Exchange) BatchQueryKLines(ctx context.Context, symbol string, interval
return allKLines, nil return allKLines, nil
} }

View File

@ -19,9 +19,9 @@ type BalanceMap map[string]Balance
type Account struct { type Account struct {
sync.Mutex sync.Mutex
MakerCommission int64 MakerCommission int `json:"makerCommission"`
TakerCommission int64 TakerCommission int `json:"takerCommission"`
AccountType string AccountType string `json:"accountType"`
balances BalanceMap balances BalanceMap
} }