grid2: add test case for aggregateOrderBaseFee

This commit is contained in:
c9s 2022-12-06 15:21:22 +08:00
parent 555d2c5046
commit 3d0cfd16b5
3 changed files with 226 additions and 149 deletions

View File

@ -0,0 +1,154 @@
package grid2
import (
"context"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/c9s/bbgo/pkg/backtest"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/exchange"
"github.com/c9s/bbgo/pkg/service"
"github.com/c9s/bbgo/pkg/types"
)
func RunBacktest(t *testing.T, strategy bbgo.SingleExchangeStrategy) {
// TEMPLATE {{{ start backtest
const sqliteDbFile = "../../../data/bbgo_test.sqlite3"
const backtestExchangeName = "binance"
const backtestStartTime = "2022-06-01"
const backtestEndTime = "2022-06-30"
startTime, err := types.ParseLooseFormatTime(backtestStartTime)
assert.NoError(t, err)
endTime, err := types.ParseLooseFormatTime(backtestEndTime)
assert.NoError(t, err)
backtestConfig := &bbgo.Backtest{
StartTime: startTime,
EndTime: &endTime,
RecordTrades: false,
FeeMode: bbgo.BacktestFeeModeToken,
Accounts: map[string]bbgo.BacktestAccount{
backtestExchangeName: {
MakerFeeRate: number(0.075 * 0.01),
TakerFeeRate: number(0.075 * 0.01),
Balances: bbgo.BacktestAccountBalanceMap{
"USDT": number(10_000.0),
"BTC": number(1.0),
},
},
},
Symbols: []string{"BTCUSDT"},
Sessions: []string{backtestExchangeName},
SyncSecKLines: false,
}
t.Logf("backtestConfig: %+v", backtestConfig)
ctx := context.Background()
environ := bbgo.NewEnvironment()
environ.SetStartTime(startTime.Time())
info, err := os.Stat(sqliteDbFile)
assert.NoError(t, err)
t.Logf("sqlite: %+v", info)
err = environ.ConfigureDatabaseDriver(ctx, "sqlite3", sqliteDbFile)
if !assert.NoError(t, err) {
return
}
backtestService := &service.BacktestService{DB: environ.DatabaseService.DB}
defer func() {
err := environ.DatabaseService.DB.Close()
assert.NoError(t, err)
}()
environ.BacktestService = backtestService
bbgo.SetBackTesting(backtestService)
defer bbgo.SetBackTesting(nil)
exName, err := types.ValidExchangeName(backtestExchangeName)
if !assert.NoError(t, err) {
return
}
t.Logf("using exchange source: %s", exName)
publicExchange, err := exchange.NewPublic(exName)
if !assert.NoError(t, err) {
return
}
backtestExchange, err := backtest.NewExchange(exName, publicExchange, backtestService, backtestConfig)
if !assert.NoError(t, err) {
return
}
session := environ.AddExchange(backtestExchangeName, backtestExchange)
assert.NotNil(t, session)
err = environ.Init(ctx)
assert.NoError(t, err)
for _, ses := range environ.Sessions() {
userDataStream := ses.UserDataStream.(types.StandardStreamEmitter)
backtestEx := ses.Exchange.(*backtest.Exchange)
backtestEx.MarketDataStream = ses.MarketDataStream.(types.StandardStreamEmitter)
backtestEx.BindUserData(userDataStream)
}
trader := bbgo.NewTrader(environ)
if assert.NotNil(t, trader) {
trader.DisableLogging()
}
userConfig := &bbgo.Config{
Backtest: backtestConfig,
ExchangeStrategies: []bbgo.ExchangeStrategyMount{
{
Mounts: []string{backtestExchangeName},
Strategy: strategy,
},
},
}
err = trader.Configure(userConfig)
assert.NoError(t, err)
err = trader.Run(ctx)
assert.NoError(t, err)
allKLineIntervals, requiredInterval, backTestIntervals := backtest.CollectSubscriptionIntervals(environ)
t.Logf("requiredInterval: %s backTestIntervals: %v", requiredInterval, backTestIntervals)
_ = allKLineIntervals
exchangeSources, err := backtest.InitializeExchangeSources(environ.Sessions(), startTime.Time(), endTime.Time(), requiredInterval, backTestIntervals...)
if !assert.NoError(t, err) {
return
}
doneC := make(chan struct{})
go func() {
count := 0
exSource := exchangeSources[0]
for k := range exSource.C {
exSource.Exchange.ConsumeKLine(k, requiredInterval)
count++
}
err = exSource.Exchange.CloseMarketData()
assert.NoError(t, err)
assert.Greater(t, count, 0, "kLines count must be greater than 0, please check your backtest date range and symbol settings")
close(doneC)
}()
<-doneC
// }}}
}

View File

@ -4,18 +4,16 @@ package grid2
import (
"context"
"os"
"testing"
"github.com/golang/mock/gomock"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/c9s/bbgo/pkg/backtest"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/exchange"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/service"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/bbgo/pkg/types/mocks"
)
func TestStrategy_checkRequiredInvestmentByQuantity(t *testing.T) {
@ -267,12 +265,13 @@ func newTestStrategy() *Strategy {
}
s := &Strategy{
logger: logrus.NewEntry(logrus.New()),
Market: market,
GridProfitStats: newGridProfitStats(market),
UpperPrice: number(20_000),
LowerPrice: number(10_000),
GridNum: 10,
logger: logrus.NewEntry(logrus.New()),
Market: market,
GridProfitStats: newGridProfitStats(market),
UpperPrice: number(20_000),
LowerPrice: number(10_000),
GridNum: 10,
historicalTrades: bbgo.NewTradeStore(),
// QuoteInvestment: number(9000.0),
}
@ -333,6 +332,68 @@ func TestStrategy_calculateProfit(t *testing.T) {
})
}
func TestStrategy_aggregateOrderBaseFee(t *testing.T) {
s := newTestStrategy()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
mockService := mocks.NewMockExchangeOrderQueryService(mockCtrl)
s.orderQueryService = mockService
ctx := context.Background()
mockService.EXPECT().QueryOrderTrades(ctx, types.OrderQuery{
Symbol: "BTCUSDT",
OrderID: "3",
}).Return([]types.Trade{
{
ID: 1,
OrderID: 3,
Exchange: "binance",
Price: number(20000.0),
Quantity: number(0.2),
Symbol: "BTCUSDT",
Side: types.SideTypeBuy,
IsBuyer: true,
FeeCurrency: "BTC",
Fee: number(0.2 * 0.01),
},
{
ID: 1,
OrderID: 3,
Exchange: "binance",
Price: number(20000.0),
Quantity: number(0.8),
Symbol: "BTCUSDT",
Side: types.SideTypeBuy,
IsBuyer: true,
FeeCurrency: "BTC",
Fee: number(0.8 * 0.01),
},
}, nil)
baseFee := s.aggregateOrderBaseFee(types.Order{
SubmitOrder: types.SubmitOrder{
Symbol: "BTCUSDT",
Side: types.SideTypeBuy,
Type: types.OrderTypeLimit,
Quantity: number(1.0),
Price: number(20000.0),
AveragePrice: number(0),
StopPrice: number(0),
Market: types.Market{},
TimeInForce: types.TimeInForceGTC,
},
Exchange: "binance",
GID: 1,
OrderID: 3,
Status: types.OrderStatusFilled,
ExecutedQuantity: number(1.0),
IsWorking: false,
})
assert.Equal(t, "0.01", baseFee.String())
}
func TestBacktestStrategy(t *testing.T) {
market := types.Market{
BaseCurrency: "BTC",
@ -353,142 +414,3 @@ func TestBacktestStrategy(t *testing.T) {
}
RunBacktest(t, strategy)
}
func RunBacktest(t *testing.T, strategy bbgo.SingleExchangeStrategy) {
// TEMPLATE {{{ start backtest
const sqliteDbFile = "../../../data/bbgo_test.sqlite3"
const backtestExchangeName = "binance"
const backtestStartTime = "2022-06-01"
const backtestEndTime = "2022-06-30"
startTime, err := types.ParseLooseFormatTime(backtestStartTime)
assert.NoError(t, err)
endTime, err := types.ParseLooseFormatTime(backtestEndTime)
assert.NoError(t, err)
backtestConfig := &bbgo.Backtest{
StartTime: startTime,
EndTime: &endTime,
RecordTrades: false,
FeeMode: bbgo.BacktestFeeModeToken,
Accounts: map[string]bbgo.BacktestAccount{
backtestExchangeName: {
MakerFeeRate: number(0.075 * 0.01),
TakerFeeRate: number(0.075 * 0.01),
Balances: bbgo.BacktestAccountBalanceMap{
"USDT": number(10_000.0),
"BTC": number(1.0),
},
},
},
Symbols: []string{"BTCUSDT"},
Sessions: []string{backtestExchangeName},
SyncSecKLines: false,
}
t.Logf("backtestConfig: %+v", backtestConfig)
ctx := context.Background()
environ := bbgo.NewEnvironment()
environ.SetStartTime(startTime.Time())
info, err := os.Stat(sqliteDbFile)
assert.NoError(t, err)
t.Logf("sqlite: %+v", info)
err = environ.ConfigureDatabaseDriver(ctx, "sqlite3", sqliteDbFile)
if !assert.NoError(t, err) {
return
}
backtestService := &service.BacktestService{DB: environ.DatabaseService.DB}
defer func() {
err := environ.DatabaseService.DB.Close()
assert.NoError(t, err)
}()
environ.BacktestService = backtestService
bbgo.SetBackTesting(backtestService)
defer bbgo.SetBackTesting(nil)
exName, err := types.ValidExchangeName(backtestExchangeName)
if !assert.NoError(t, err) {
return
}
t.Logf("using exchange source: %s", exName)
publicExchange, err := exchange.NewPublic(exName)
if !assert.NoError(t, err) {
return
}
backtestExchange, err := backtest.NewExchange(exName, publicExchange, backtestService, backtestConfig)
if !assert.NoError(t, err) {
return
}
session := environ.AddExchange(backtestExchangeName, backtestExchange)
assert.NotNil(t, session)
err = environ.Init(ctx)
assert.NoError(t, err)
for _, ses := range environ.Sessions() {
userDataStream := ses.UserDataStream.(types.StandardStreamEmitter)
backtestEx := ses.Exchange.(*backtest.Exchange)
backtestEx.MarketDataStream = ses.MarketDataStream.(types.StandardStreamEmitter)
backtestEx.BindUserData(userDataStream)
}
trader := bbgo.NewTrader(environ)
if assert.NotNil(t, trader) {
trader.DisableLogging()
}
userConfig := &bbgo.Config{
Backtest: backtestConfig,
ExchangeStrategies: []bbgo.ExchangeStrategyMount{
{
Mounts: []string{backtestExchangeName},
Strategy: strategy,
},
},
}
err = trader.Configure(userConfig)
assert.NoError(t, err)
err = trader.Run(ctx)
assert.NoError(t, err)
allKLineIntervals, requiredInterval, backTestIntervals := backtest.CollectSubscriptionIntervals(environ)
t.Logf("requiredInterval: %s backTestIntervals: %v", requiredInterval, backTestIntervals)
_ = allKLineIntervals
exchangeSources, err := backtest.InitializeExchangeSources(environ.Sessions(), startTime.Time(), endTime.Time(), requiredInterval, backTestIntervals...)
if !assert.NoError(t, err) {
return
}
doneC := make(chan struct{})
go func() {
count := 0
exSource := exchangeSources[0]
for k := range exSource.C {
exSource.Exchange.ConsumeKLine(k, requiredInterval)
count++
}
err = exSource.Exchange.CloseMarketData()
assert.NoError(t, err)
assert.Greater(t, count, 0, "kLines count must be greater than 0, please check your backtest date range and symbol settings")
close(doneC)
}()
<-doneC
// }}}
}

View File

@ -82,6 +82,7 @@ type Exchange interface {
}
// ExchangeOrderQueryService provides an interface for querying the order status via order ID or client order ID
//go:generate mockgen -destination=mocks/mock_exchange_order_query.go -package=mocks . ExchangeOrderQueryService
type ExchangeOrderQueryService interface {
QueryOrder(ctx context.Context, q OrderQuery) (*Order, error)
QueryOrderTrades(ctx context.Context, q OrderQuery) ([]Trade, error)