mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 01:01:56 +00:00
Merge pull request #1593 from c9s/c9s/xalign-add-test-cases
FIX: [xalign] add more complex test case for xalign strategy
This commit is contained in:
commit
d58461d1cf
|
@ -659,6 +659,10 @@ func (session *ExchangeSession) Markets() types.MarketMap {
|
|||
return session.markets
|
||||
}
|
||||
|
||||
func (session *ExchangeSession) SetMarkets(markets types.MarketMap) {
|
||||
session.markets = markets
|
||||
}
|
||||
|
||||
func (session *ExchangeSession) OrderStore(symbol string) (store *core.OrderStore, ok bool) {
|
||||
store, ok = session.orderStores[symbol]
|
||||
return store, ok
|
||||
|
|
|
@ -138,15 +138,16 @@ func (s *Strategy) selectSessionForCurrency(
|
|||
}
|
||||
|
||||
// check both fromQuoteCurrency/currency and currency/fromQuoteCurrency
|
||||
reversed := false
|
||||
baseCurrency := currency
|
||||
quoteCurrency := fromQuoteCurrency
|
||||
symbol := currency + fromQuoteCurrency
|
||||
symbol := currency + quoteCurrency
|
||||
market, ok := session.Market(symbol)
|
||||
if !ok {
|
||||
// for TWD in USDT/TWD market, buy TWD means sell USDT
|
||||
baseCurrency = fromQuoteCurrency
|
||||
quoteCurrency = currency
|
||||
symbol = fromQuoteCurrency + currency
|
||||
symbol = baseCurrency + currency
|
||||
market, ok = session.Market(symbol)
|
||||
if !ok {
|
||||
continue
|
||||
|
@ -154,6 +155,7 @@ func (s *Strategy) selectSessionForCurrency(
|
|||
|
||||
// reverse side
|
||||
side = side.Reverse()
|
||||
reversed = true
|
||||
}
|
||||
|
||||
ticker, err := session.Exchange.QueryTicker(ctx, symbol)
|
||||
|
@ -169,10 +171,17 @@ func (s *Strategy) selectSessionForCurrency(
|
|||
q := changeQuantity.Abs()
|
||||
|
||||
// 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 {
|
||||
log.Debugf("skip dust quantity: %f", q.Float64())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("%s changeQuantity: %f ticker: %+v market: %+v", symbol, changeQuantity.Float64(), ticker, market)
|
||||
|
||||
|
@ -193,7 +202,13 @@ func (s *Strategy) selectSessionForCurrency(
|
|||
continue
|
||||
}
|
||||
|
||||
requiredQuoteAmount := q.Mul(price)
|
||||
requiredQuoteAmount := fixedpoint.Zero
|
||||
if reversed {
|
||||
requiredQuoteAmount = q
|
||||
} else {
|
||||
requiredQuoteAmount = q.Mul(price)
|
||||
}
|
||||
|
||||
requiredQuoteAmount = requiredQuoteAmount.Round(market.PricePrecision, fixedpoint.Up)
|
||||
if requiredQuoteAmount.Compare(quoteBalance.Available) > 0 {
|
||||
log.Warnf("required quote amount %f > quote balance %v, skip", requiredQuoteAmount.Float64(), quoteBalance)
|
||||
|
@ -243,6 +258,10 @@ func (s *Strategy) selectSessionForCurrency(
|
|||
price = ticker.Sell
|
||||
}
|
||||
|
||||
if reversed {
|
||||
q = q.Div(price)
|
||||
}
|
||||
|
||||
baseBalance, ok := session.Account.Balance(baseCurrency)
|
||||
if !ok {
|
||||
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