mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
xalign: add more complex test case for xalign strategy
This commit is contained in:
parent
3e14c14111
commit
aced149ee8
|
@ -659,6 +659,10 @@ func (session *ExchangeSession) Markets() types.MarketMap {
|
||||||
return session.markets
|
return session.markets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (session *ExchangeSession) SetMarkets(markets types.MarketMap) {
|
||||||
|
session.markets = markets
|
||||||
|
}
|
||||||
|
|
||||||
func (session *ExchangeSession) OrderStore(symbol string) (store *core.OrderStore, ok bool) {
|
func (session *ExchangeSession) OrderStore(symbol string) (store *core.OrderStore, ok bool) {
|
||||||
store, ok = session.orderStores[symbol]
|
store, ok = session.orderStores[symbol]
|
||||||
return store, ok
|
return store, ok
|
||||||
|
|
|
@ -138,15 +138,16 @@ func (s *Strategy) selectSessionForCurrency(
|
||||||
}
|
}
|
||||||
|
|
||||||
// check both fromQuoteCurrency/currency and currency/fromQuoteCurrency
|
// check both fromQuoteCurrency/currency and currency/fromQuoteCurrency
|
||||||
|
reversed := false
|
||||||
baseCurrency := currency
|
baseCurrency := currency
|
||||||
quoteCurrency := fromQuoteCurrency
|
quoteCurrency := fromQuoteCurrency
|
||||||
symbol := currency + fromQuoteCurrency
|
symbol := currency + quoteCurrency
|
||||||
market, ok := session.Market(symbol)
|
market, ok := session.Market(symbol)
|
||||||
if !ok {
|
if !ok {
|
||||||
// for TWD in USDT/TWD market, buy TWD means sell USDT
|
// for TWD in USDT/TWD market, buy TWD means sell USDT
|
||||||
baseCurrency = fromQuoteCurrency
|
baseCurrency = fromQuoteCurrency
|
||||||
quoteCurrency = currency
|
quoteCurrency = currency
|
||||||
symbol = fromQuoteCurrency + currency
|
symbol = baseCurrency + currency
|
||||||
market, ok = session.Market(symbol)
|
market, ok = session.Market(symbol)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
|
@ -154,6 +155,7 @@ func (s *Strategy) selectSessionForCurrency(
|
||||||
|
|
||||||
// reverse side
|
// reverse side
|
||||||
side = side.Reverse()
|
side = side.Reverse()
|
||||||
|
reversed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker, err := session.Exchange.QueryTicker(ctx, symbol)
|
ticker, err := session.Exchange.QueryTicker(ctx, symbol)
|
||||||
|
@ -169,10 +171,17 @@ func (s *Strategy) selectSessionForCurrency(
|
||||||
q := changeQuantity.Abs()
|
q := changeQuantity.Abs()
|
||||||
|
|
||||||
// a fast filtering
|
// a fast filtering
|
||||||
|
if reversed {
|
||||||
|
if q.Compare(market.MinNotional) < 0 {
|
||||||
|
log.Debugf("skip dust notional: %f", q.Float64())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if q.Compare(market.MinQuantity) < 0 {
|
if q.Compare(market.MinQuantity) < 0 {
|
||||||
log.Debugf("skip dust quantity: %f", q.Float64())
|
log.Debugf("skip dust quantity: %f", q.Float64())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Infof("%s changeQuantity: %f ticker: %+v market: %+v", symbol, changeQuantity.Float64(), ticker, market)
|
log.Infof("%s changeQuantity: %f ticker: %+v market: %+v", symbol, changeQuantity.Float64(), ticker, market)
|
||||||
|
|
||||||
|
@ -193,7 +202,13 @@ func (s *Strategy) selectSessionForCurrency(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
requiredQuoteAmount := q.Mul(price)
|
requiredQuoteAmount := fixedpoint.Zero
|
||||||
|
if reversed {
|
||||||
|
requiredQuoteAmount = q
|
||||||
|
} else {
|
||||||
|
requiredQuoteAmount = q.Mul(price)
|
||||||
|
}
|
||||||
|
|
||||||
requiredQuoteAmount = requiredQuoteAmount.Round(market.PricePrecision, fixedpoint.Up)
|
requiredQuoteAmount = requiredQuoteAmount.Round(market.PricePrecision, fixedpoint.Up)
|
||||||
if requiredQuoteAmount.Compare(quoteBalance.Available) > 0 {
|
if requiredQuoteAmount.Compare(quoteBalance.Available) > 0 {
|
||||||
log.Warnf("required quote amount %f > quote balance %v, skip", requiredQuoteAmount.Float64(), quoteBalance)
|
log.Warnf("required quote amount %f > quote balance %v, skip", requiredQuoteAmount.Float64(), quoteBalance)
|
||||||
|
@ -243,6 +258,10 @@ func (s *Strategy) selectSessionForCurrency(
|
||||||
price = ticker.Sell
|
price = ticker.Sell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if reversed {
|
||||||
|
q = q.Div(price)
|
||||||
|
}
|
||||||
|
|
||||||
baseBalance, ok := session.Account.Balance(baseCurrency)
|
baseBalance, ok := session.Account.Balance(baseCurrency)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
|
|
216
pkg/strategy/xalign/strategy_test.go
Normal file
216
pkg/strategy/xalign/strategy_test.go
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
//go:build !dnum
|
||||||
|
|
||||||
|
package xalign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"go.uber.org/mock/gomock"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
. "github.com/c9s/bbgo/pkg/testing/testhelper"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/c9s/bbgo/pkg/types/mocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cat ~/.bbgo/cache/max-markets.json | jq '.[] | select(.symbol == "USDTTWD")'
|
||||||
|
func getTestMarkets() types.MarketMap {
|
||||||
|
return map[string]types.Market{
|
||||||
|
"ETHBTC": {
|
||||||
|
Exchange: types.ExchangeMax,
|
||||||
|
Symbol: "ETHBTC",
|
||||||
|
LocalSymbol: "ETHBTC",
|
||||||
|
PricePrecision: 6,
|
||||||
|
VolumePrecision: 4,
|
||||||
|
BaseCurrency: "ETH",
|
||||||
|
QuoteCurrency: "BTC",
|
||||||
|
MinNotional: Number(0.00030000),
|
||||||
|
MinAmount: Number(0.00030000),
|
||||||
|
MinQuantity: Number(0.00460000),
|
||||||
|
StepSize: Number(0.00010000),
|
||||||
|
TickSize: Number(0.00000100),
|
||||||
|
},
|
||||||
|
"BTCUSDT": {
|
||||||
|
Exchange: types.ExchangeMax,
|
||||||
|
Symbol: "BTCUSDT",
|
||||||
|
LocalSymbol: "BTCUSDT",
|
||||||
|
PricePrecision: 2,
|
||||||
|
VolumePrecision: 6,
|
||||||
|
BaseCurrency: "BTC",
|
||||||
|
QuoteCurrency: "USDT",
|
||||||
|
MinNotional: Number(8.00000000),
|
||||||
|
MinAmount: Number(8.00000000),
|
||||||
|
MinQuantity: Number(0.00030000),
|
||||||
|
StepSize: Number(0.00000100),
|
||||||
|
TickSize: Number(0.01000000),
|
||||||
|
},
|
||||||
|
"BTCTWD": {
|
||||||
|
Exchange: types.ExchangeMax,
|
||||||
|
Symbol: "BTCTWD",
|
||||||
|
LocalSymbol: "BTCTWD",
|
||||||
|
PricePrecision: 1,
|
||||||
|
VolumePrecision: 8,
|
||||||
|
BaseCurrency: "BTC",
|
||||||
|
QuoteCurrency: "TWD",
|
||||||
|
MinNotional: Number(250.00000000),
|
||||||
|
MinAmount: Number(250.00000000),
|
||||||
|
MinQuantity: Number(0.00030000),
|
||||||
|
StepSize: Number(0.00000001),
|
||||||
|
TickSize: Number(0.01000000),
|
||||||
|
},
|
||||||
|
"ETHUSDT": {
|
||||||
|
Exchange: types.ExchangeMax,
|
||||||
|
Symbol: "ETHUSDT",
|
||||||
|
LocalSymbol: "ETHUSDT",
|
||||||
|
PricePrecision: 2,
|
||||||
|
VolumePrecision: 6,
|
||||||
|
BaseCurrency: "ETH",
|
||||||
|
QuoteCurrency: "USDT",
|
||||||
|
MinNotional: Number(8.00000000),
|
||||||
|
MinAmount: Number(8.00000000),
|
||||||
|
MinQuantity: Number(0.00460000),
|
||||||
|
StepSize: Number(0.00001000),
|
||||||
|
TickSize: Number(0.01000000),
|
||||||
|
},
|
||||||
|
"ETHTWD": {
|
||||||
|
Exchange: types.ExchangeMax,
|
||||||
|
Symbol: "ETHTWD",
|
||||||
|
LocalSymbol: "ETHTWD",
|
||||||
|
PricePrecision: 1,
|
||||||
|
VolumePrecision: 6,
|
||||||
|
BaseCurrency: "ETH",
|
||||||
|
QuoteCurrency: "TWD",
|
||||||
|
MinNotional: Number(250.00000000),
|
||||||
|
MinAmount: Number(250.00000000),
|
||||||
|
MinQuantity: Number(0.00460000),
|
||||||
|
StepSize: Number(0.00000100),
|
||||||
|
TickSize: Number(0.10000000),
|
||||||
|
},
|
||||||
|
"USDTTWD": {
|
||||||
|
Exchange: types.ExchangeMax,
|
||||||
|
Symbol: "USDTTWD",
|
||||||
|
LocalSymbol: "USDTTWD",
|
||||||
|
PricePrecision: 3,
|
||||||
|
VolumePrecision: 2,
|
||||||
|
BaseCurrency: "USDT",
|
||||||
|
QuoteCurrency: "TWD",
|
||||||
|
MinNotional: Number(250.00000000),
|
||||||
|
MinAmount: Number(250.00000000),
|
||||||
|
MinQuantity: Number(8.00000000),
|
||||||
|
StepSize: Number(0.01000000),
|
||||||
|
TickSize: Number(0.00100000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrategy(t *testing.T) {
|
||||||
|
mockCtrl := gomock.NewController(t)
|
||||||
|
defer mockCtrl.Finish()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
s := &Strategy{
|
||||||
|
ExpectedBalances: map[string]fixedpoint.Value{
|
||||||
|
"TWD": Number(10_000),
|
||||||
|
},
|
||||||
|
PreferredQuoteCurrencies: &QuoteCurrencyPreference{
|
||||||
|
Buy: []string{"TWD", "USDT"},
|
||||||
|
Sell: []string{"USDT"},
|
||||||
|
},
|
||||||
|
PreferredSessions: []string{"max"},
|
||||||
|
UseTakerOrder: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
testMarkets := getTestMarkets()
|
||||||
|
|
||||||
|
t.Run("buy TWD", func(t *testing.T) {
|
||||||
|
mockEx := mocks.NewMockExchange(mockCtrl)
|
||||||
|
mockEx.EXPECT().QueryTicker(ctx, "USDTTWD").Return(&types.Ticker{
|
||||||
|
Buy: Number(32.0),
|
||||||
|
Sell: Number(33.0),
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
account := types.NewAccount()
|
||||||
|
account.AddBalance("TWD", Number(20_000))
|
||||||
|
account.AddBalance("USDT", Number(80_000))
|
||||||
|
|
||||||
|
session := &bbgo.ExchangeSession{
|
||||||
|
Exchange: mockEx,
|
||||||
|
Account: account,
|
||||||
|
}
|
||||||
|
session.SetMarkets(testMarkets)
|
||||||
|
sessions := map[string]*bbgo.ExchangeSession{}
|
||||||
|
sessions["max"] = session
|
||||||
|
|
||||||
|
_, submitOrder := s.selectSessionForCurrency(ctx, sessions, "TWD", Number(70_000))
|
||||||
|
assert.NotNil(t, submitOrder)
|
||||||
|
assert.Equal(t, types.SideTypeSell, submitOrder.Side)
|
||||||
|
assert.Equal(t, Number(32).String(), submitOrder.Price.String())
|
||||||
|
assert.Equal(t, "2187.5", submitOrder.Quantity.String(), "70_000 / 32 best bid = 2187.5")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("sell TWD", func(t *testing.T) {
|
||||||
|
mockEx := mocks.NewMockExchange(mockCtrl)
|
||||||
|
mockEx.EXPECT().QueryTicker(ctx, "USDTTWD").Return(&types.Ticker{
|
||||||
|
Buy: Number(32.0),
|
||||||
|
Sell: Number(33.0),
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
account := types.NewAccount()
|
||||||
|
account.AddBalance("TWD", Number(20_000))
|
||||||
|
account.AddBalance("USDT", Number(80_000))
|
||||||
|
|
||||||
|
session := &bbgo.ExchangeSession{
|
||||||
|
Exchange: mockEx,
|
||||||
|
Account: account,
|
||||||
|
}
|
||||||
|
session.SetMarkets(testMarkets)
|
||||||
|
sessions := map[string]*bbgo.ExchangeSession{}
|
||||||
|
sessions["max"] = session
|
||||||
|
|
||||||
|
_, submitOrder := s.selectSessionForCurrency(ctx, sessions, "TWD", Number(-10_000))
|
||||||
|
assert.NotNil(t, submitOrder)
|
||||||
|
assert.Equal(t, types.SideTypeBuy, submitOrder.Side)
|
||||||
|
assert.Equal(t, Number(33).String(), submitOrder.Price.String())
|
||||||
|
assert.Equal(t, "303.03", submitOrder.Quantity.String(), "10_000 / 33 best ask = 303.0303030303")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("buy BTC with USDT instead of TWD", func(t *testing.T) {
|
||||||
|
mockEx := mocks.NewMockExchange(mockCtrl)
|
||||||
|
|
||||||
|
mockEx.EXPECT().QueryTicker(ctx, "BTCTWD").Return(&types.Ticker{
|
||||||
|
Sell: Number(36000.0 * 32),
|
||||||
|
Buy: Number(35000.0 * 31),
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
mockEx.EXPECT().QueryTicker(ctx, "BTCUSDT").Return(&types.Ticker{
|
||||||
|
Sell: Number(36000.0),
|
||||||
|
Buy: Number(35000.0),
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
account := types.NewAccount()
|
||||||
|
account.AddBalance("BTC", Number(0.955))
|
||||||
|
account.AddBalance("TWD", Number(60_000))
|
||||||
|
account.AddBalance("USDT", Number(80_000))
|
||||||
|
|
||||||
|
// 36000.0 * 32 * 0.045
|
||||||
|
|
||||||
|
session := &bbgo.ExchangeSession{
|
||||||
|
Exchange: mockEx,
|
||||||
|
Account: account,
|
||||||
|
}
|
||||||
|
|
||||||
|
session.SetMarkets(testMarkets)
|
||||||
|
sessions := map[string]*bbgo.ExchangeSession{}
|
||||||
|
sessions["max"] = session
|
||||||
|
|
||||||
|
_, submitOrder := s.selectSessionForCurrency(ctx, sessions, "BTC", Number(0.045))
|
||||||
|
assert.NotNil(t, submitOrder)
|
||||||
|
assert.Equal(t, types.SideTypeBuy, submitOrder.Side)
|
||||||
|
assert.Equal(t, "36000", submitOrder.Price.String())
|
||||||
|
assert.Equal(t, "0.045", submitOrder.Quantity.String())
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user