Merge pull request #1021 from c9s/feature/grid2

grid2: add test case for aggregateOrderBaseFee
This commit is contained in:
Yo-An Lin 2022-12-06 16:51:38 +08:00 committed by GitHub
commit 4c9403d919
5 changed files with 439 additions and 175 deletions

View File

@ -0,0 +1,156 @@
//go:build !dnum
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

@ -128,6 +128,10 @@ func (s *Strategy) Validate() error {
return fmt.Errorf("upperPrice (%s) should not be less than or equal to lowerPrice (%s)", s.UpperPrice.String(), s.LowerPrice.String())
}
if s.GridNum == 0 {
return fmt.Errorf("gridNum can not be zero")
}
if s.FeeRate.IsZero() {
s.FeeRate = fixedpoint.NewFromFloat(0.1 * 0.01) // 0.1%, 0.075% with BNB
}
@ -145,10 +149,6 @@ func (s *Strategy) Validate() error {
}
}
if s.GridNum == 0 {
return fmt.Errorf("gridNum can not be zero")
}
if err := s.QuantityOrAmount.Validate(); err != nil {
if s.QuoteInvestment.IsZero() && s.BaseInvestment.IsZero() {
return err
@ -222,16 +222,16 @@ func collectTradeFee(trades []types.Trade) map[string]fixedpoint.Value {
return fees
}
func (s *Strategy) verifyOrderTrades(o types.Order, trades []types.Trade) bool {
func aggregateTradesQuantity(trades []types.Trade) fixedpoint.Value {
tq := fixedpoint.Zero
for _, t := range trades {
if t.Fee.IsZero() && t.FeeCurrency == "" {
s.logger.Warnf("trade fee and feeCurrency is zero: %+v", t)
return false
}
tq = tq.Add(t.Quantity)
}
return tq
}
func (s *Strategy) verifyOrderTrades(o types.Order, trades []types.Trade) bool {
tq := aggregateTradesQuantity(trades)
if tq.Compare(o.Quantity) != 0 {
s.logger.Warnf("order trades missing. expected: %f actual: %f",
@ -363,7 +363,7 @@ func (s *Strategy) handleOrderFilled(o types.Order) {
Tag: "grid",
}
s.logger.Infof("SUBMIT ORDER: %s", orderForm.String())
s.logger.Infof("SUBMIT GRID REVERSE ORDER: %s", orderForm.String())
if createdOrders, err := s.orderExecutor.SubmitOrders(context.Background(), orderForm); err != nil {
s.logger.WithError(err).Errorf("can not submit arbitrage order")
@ -372,13 +372,6 @@ func (s *Strategy) handleOrderFilled(o types.Order) {
}
}
type InvestmentBudget struct {
baseInvestment fixedpoint.Value
quoteInvestment fixedpoint.Value
baseBalance fixedpoint.Value
quoteBalance fixedpoint.Value
}
func (s *Strategy) checkRequiredInvestmentByQuantity(baseBalance, quoteBalance, quantity, lastPrice fixedpoint.Value, pins []Pin) (requiredBase, requiredQuote fixedpoint.Value, err error) {
// check more investment budget details
requiredBase = fixedpoint.Zero
@ -795,10 +788,18 @@ func (s *Strategy) openGrid(ctx context.Context, session *bbgo.ExchangeSession)
return err
}
// debug info
s.debugGridOrders(submitOrders, lastPrice)
for _, order := range createdOrders {
s.logger.Info(order.String())
}
return nil
}
func (s *Strategy) debugGridOrders(submitOrders []types.SubmitOrder, lastPrice fixedpoint.Value) {
s.logger.Infof("GRID ORDERS: [")
for i, order := range submitOrders {
if i > 0 && lastPrice.Compare(order.Price) >= 0 && lastPrice.Compare(submitOrders[i-1].Price) <= 0 {
s.logger.Infof(" - LAST PRICE: %f", lastPrice.Float64())
}
@ -806,12 +807,6 @@ func (s *Strategy) openGrid(ctx context.Context, session *bbgo.ExchangeSession)
s.logger.Info(" - ", order.String())
}
s.logger.Infof("] END OF GRID ORDERS")
for _, order := range createdOrders {
s.logger.Info(order.String())
}
return nil
}
func (s *Strategy) generateGridOrders(totalQuote, totalBase, lastPrice fixedpoint.Value) ([]types.SubmitOrder, error) {
@ -936,6 +931,25 @@ func (s *Strategy) getLastTradePrice(ctx context.Context, session *bbgo.Exchange
return fixedpoint.Zero, fmt.Errorf("%s ticker price not found", s.Symbol)
}
func calculateMinimalQuoteInvestment(market types.Market, lowerPrice, upperPrice fixedpoint.Value, gridNum int64) fixedpoint.Value {
num := fixedpoint.NewFromInt(gridNum)
minimalAmountLowerPrice := fixedpoint.Max(lowerPrice.Mul(market.MinQuantity), market.MinNotional)
minimalAmountUpperPrice := fixedpoint.Max(upperPrice.Mul(market.MinQuantity), market.MinNotional)
return fixedpoint.Max(minimalAmountLowerPrice, minimalAmountUpperPrice).Mul(num)
}
func (s *Strategy) checkMinimalQuoteInvestment() error {
minimalQuoteInvestment := calculateMinimalQuoteInvestment(s.Market, s.LowerPrice, s.UpperPrice, s.GridNum)
if s.QuoteInvestment.Compare(minimalQuoteInvestment) <= 0 {
return fmt.Errorf("need at least %f %s for quote investment, %f %s given",
minimalQuoteInvestment.Float64(),
s.Market.QuoteCurrency,
s.QuoteInvestment.Float64(),
s.Market.QuoteCurrency)
}
return nil
}
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
instanceID := s.InstanceID()
@ -972,6 +986,13 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
s.Position.Reset()
}
// we need to check the minimal quote investment here, because we need the market info
if s.QuoteInvestment.Sign() > 0 {
if err := s.checkMinimalQuoteInvestment(); err != nil {
return err
}
}
s.historicalTrades = bbgo.NewTradeStore()
s.historicalTrades.EnablePrune = true
s.historicalTrades.BindStream(session.UserDataStream)

View File

@ -4,18 +4,17 @@ package grid2
import (
"context"
"os"
"errors"
"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) {
@ -264,15 +263,18 @@ func newTestStrategy() *Strategy {
TickSize: number(0.01),
PricePrecision: 2,
VolumePrecision: 8,
MinNotional: number(10.0),
MinQuantity: number(0.001),
}
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 +335,163 @@ 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 TestStrategy_aggregateOrderBaseFeeRetry(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(nil, errors.New("api error"))
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 TestStrategy_checkMinimalQuoteInvestment(t *testing.T) {
s := newTestStrategy()
t.Run("10 grids", func(t *testing.T) {
// 10_000 * 0.001 = 10USDT
// 20_000 * 0.001 = 20USDT
// hence we should have at least: 20USDT * 10 grids
s.QuoteInvestment = number(10_000)
s.GridNum = 10
minQuoteInvestment := calculateMinimalQuoteInvestment(s.Market, s.LowerPrice, s.UpperPrice, s.GridNum)
assert.Equal(t, "200", minQuoteInvestment.String())
err := s.checkMinimalQuoteInvestment()
assert.NoError(t, err)
})
t.Run("1000 grids", func(t *testing.T) {
s.QuoteInvestment = number(10_000)
s.GridNum = 1000
minQuoteInvestment := calculateMinimalQuoteInvestment(s.Market, s.LowerPrice, s.UpperPrice, s.GridNum)
assert.Equal(t, "20000", minQuoteInvestment.String())
err := s.checkMinimalQuoteInvestment()
assert.Error(t, err)
assert.EqualError(t, err, "need at least 20000.000000 USDT for quote investment, 10000.000000 USDT given")
})
}
func TestBacktestStrategy(t *testing.T) {
market := types.Market{
BaseCurrency: "BTC",
@ -353,142 +512,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)

View File

@ -0,0 +1,66 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/c9s/bbgo/pkg/types (interfaces: ExchangeOrderQueryService)
// Package mocks is a generated GoMock package.
package mocks
import (
context "context"
reflect "reflect"
types "github.com/c9s/bbgo/pkg/types"
gomock "github.com/golang/mock/gomock"
)
// MockExchangeOrderQueryService is a mock of ExchangeOrderQueryService interface.
type MockExchangeOrderQueryService struct {
ctrl *gomock.Controller
recorder *MockExchangeOrderQueryServiceMockRecorder
}
// MockExchangeOrderQueryServiceMockRecorder is the mock recorder for MockExchangeOrderQueryService.
type MockExchangeOrderQueryServiceMockRecorder struct {
mock *MockExchangeOrderQueryService
}
// NewMockExchangeOrderQueryService creates a new mock instance.
func NewMockExchangeOrderQueryService(ctrl *gomock.Controller) *MockExchangeOrderQueryService {
mock := &MockExchangeOrderQueryService{ctrl: ctrl}
mock.recorder = &MockExchangeOrderQueryServiceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockExchangeOrderQueryService) EXPECT() *MockExchangeOrderQueryServiceMockRecorder {
return m.recorder
}
// QueryOrder mocks base method.
func (m *MockExchangeOrderQueryService) QueryOrder(arg0 context.Context, arg1 types.OrderQuery) (*types.Order, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "QueryOrder", arg0, arg1)
ret0, _ := ret[0].(*types.Order)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// QueryOrder indicates an expected call of QueryOrder.
func (mr *MockExchangeOrderQueryServiceMockRecorder) QueryOrder(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryOrder", reflect.TypeOf((*MockExchangeOrderQueryService)(nil).QueryOrder), arg0, arg1)
}
// QueryOrderTrades mocks base method.
func (m *MockExchangeOrderQueryService) QueryOrderTrades(arg0 context.Context, arg1 types.OrderQuery) ([]types.Trade, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "QueryOrderTrades", arg0, arg1)
ret0, _ := ret[0].([]types.Trade)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// QueryOrderTrades indicates an expected call of QueryOrderTrades.
func (mr *MockExchangeOrderQueryServiceMockRecorder) QueryOrderTrades(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryOrderTrades", reflect.TypeOf((*MockExchangeOrderQueryService)(nil).QueryOrderTrades), arg0, arg1)
}