optimize single exchange back-test

This commit is contained in:
c9s 2022-05-06 19:16:15 +08:00
parent e527dd8dde
commit 6f16f32e16
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
3 changed files with 52 additions and 25 deletions

View File

@ -0,0 +1,8 @@
package backtest
import "github.com/c9s/bbgo/pkg/types"
type ExchangeDataSource struct {
C chan types.KLine
Exchange *Exchange
}

16
pkg/backtest/report.go Normal file
View File

@ -0,0 +1,16 @@
package backtest
import (
"github.com/c9s/bbgo/pkg/accounting/pnl"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type BackTestReport struct {
Symbol string `json:"symbol,omitempty"`
LastPrice fixedpoint.Value `json:"lastPrice,omitempty"`
StartPrice fixedpoint.Value `json:"startPrice,omitempty"`
PnLReport *pnl.AverageCostPnlReport `json:"pnlReport,omitempty"`
InitialBalances types.BalanceMap `json:"initialBalances,omitempty"`
FinalBalances types.BalanceMap `json:"finalBalances,omitempty"`
}

View File

@ -22,20 +22,10 @@ import (
"github.com/c9s/bbgo/pkg/backtest" "github.com/c9s/bbgo/pkg/backtest"
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/cmd/cmdutil" "github.com/c9s/bbgo/pkg/cmd/cmdutil"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/service" "github.com/c9s/bbgo/pkg/service"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
type BackTestReport struct {
Symbol string `json:"symbol,omitempty"`
LastPrice fixedpoint.Value `json:"lastPrice,omitempty"`
StartPrice fixedpoint.Value `json:"startPrice,omitempty"`
PnLReport *pnl.AverageCostPnlReport `json:"pnlReport,omitempty"`
InitialBalances types.BalanceMap `json:"initialBalances,omitempty"`
FinalBalances types.BalanceMap `json:"finalBalances,omitempty"`
}
func init() { func init() {
BacktestCmd.Flags().Bool("sync", false, "sync backtest data") BacktestCmd.Flags().Bool("sync", false, "sync backtest data")
BacktestCmd.Flags().Bool("sync-only", false, "sync backtest data only, do not run backtest") BacktestCmd.Flags().Bool("sync-only", false, "sync backtest data only, do not run backtest")
@ -53,7 +43,7 @@ func init() {
var BacktestCmd = &cobra.Command{ var BacktestCmd = &cobra.Command{
Use: "backtest", Use: "backtest",
Short: "backtest your strategies", Short: "run backtest with strategies",
SilenceUsage: true, SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
verboseCnt, err := cmd.Flags().GetCount("verbose") verboseCnt, err := cmd.Flags().GetCount("verbose")
@ -99,6 +89,9 @@ var BacktestCmd = &cobra.Command{
return err return err
} }
kLineDirectory := filepath.Join(outputDirectory, "klines")
_ = kLineDirectory
jsonOutputEnabled := len(outputDirectory) > 0 jsonOutputEnabled := len(outputDirectory) > 0
syncOnly, err := cmd.Flags().GetBool("sync-only") syncOnly, err := cmd.Flags().GetBool("sync-only")
@ -317,37 +310,47 @@ var BacktestCmd = &cobra.Command{
return err return err
} }
type KChanEx struct {
KChan chan types.KLine
Exchange *backtest.Exchange
}
for _, session := range environ.Sessions() { for _, session := range environ.Sessions() {
backtestExchange := session.Exchange.(*backtest.Exchange) backtestExchange := session.Exchange.(*backtest.Exchange)
backtestExchange.InitMarketData() backtestExchange.InitMarketData()
} }
var klineChans []KChanEx var exchangeSources []backtest.ExchangeDataSource
for _, session := range environ.Sessions() { for _, session := range environ.Sessions() {
exchange := session.Exchange.(*backtest.Exchange) exchange := session.Exchange.(*backtest.Exchange)
c, err := exchange.GetMarketData() c, err := exchange.GetMarketData()
if err != nil { if err != nil {
return err return err
} }
klineChans = append(klineChans, KChanEx{KChan: c, Exchange: exchange}) exchangeSources = append(exchangeSources, backtest.ExchangeDataSource{C: c, Exchange: exchange})
} }
runCtx, cancelRun := context.WithCancel(ctx) runCtx, cancelRun := context.WithCancel(ctx)
go func() { go func() {
defer cancelRun() defer cancelRun()
// Optimize back-test speed for single exchange source
var count = len(exchangeSources)
if count == 1 {
exSource := exchangeSources[0]
for k := range exSource.C {
exSource.Exchange.ConsumeKLine(k)
}
if err := exSource.Exchange.CloseMarketData(); err != nil {
log.WithError(err).Errorf("close market data error")
}
return
}
for { for {
count := len(klineChans) for _, exK := range exchangeSources {
for _, kchanex := range klineChans { kLine, more := <-exK.C
kLine, more := <-kchanex.KChan
if more { if more {
kchanex.Exchange.ConsumeKLine(kLine) exK.Exchange.ConsumeKLine(kLine)
} else { } else {
if err := kchanex.Exchange.CloseMarketData(); err != nil { if err := exK.Exchange.CloseMarketData(); err != nil {
log.Errorf("%v", err) log.WithError(err).Errorf("close market data error")
return return
} }
count-- count--
@ -397,7 +400,7 @@ var BacktestCmd = &cobra.Command{
report := calculator.Calculate(symbol, trades.Trades, lastPrice) report := calculator.Calculate(symbol, trades.Trades, lastPrice)
report.Print() report.Print()
accountConfig, ok := userConfig.Backtest.Accounts[exchangeName] accountConfig, ok := userConfig.Backtest.Accounts[exchangeName]
if !ok { if !ok {
accountConfig = userConfig.Backtest.Account[exchangeName] accountConfig = userConfig.Backtest.Account[exchangeName]
@ -413,7 +416,7 @@ var BacktestCmd = &cobra.Command{
finalBalances.Print() finalBalances.Print()
if jsonOutputEnabled { if jsonOutputEnabled {
result := BackTestReport{ result := backtest.BackTestReport{
Symbol: symbol, Symbol: symbol,
LastPrice: lastPrice, LastPrice: lastPrice,
StartPrice: startPrice, StartPrice: startPrice,