qbtrade/pkg/strategy/xalign/strategy_test.go

217 lines
6.2 KiB
Go
Raw Normal View History

2024-06-27 14:42:38 +00:00
//go:build !dnum
package xalign
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"git.qtrade.icu/lychiyu/qbtrade/pkg/fixedpoint"
"git.qtrade.icu/lychiyu/qbtrade/pkg/qbtrade"
. "git.qtrade.icu/lychiyu/qbtrade/pkg/testing/testhelper"
"git.qtrade.icu/lychiyu/qbtrade/pkg/types"
"git.qtrade.icu/lychiyu/qbtrade/pkg/types/mocks"
)
// cat ~/.qbtrade/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 := &qbtrade.ExchangeSession{
Exchange: mockEx,
Account: account,
}
session.SetMarkets(testMarkets)
sessions := map[string]*qbtrade.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 := &qbtrade.ExchangeSession{
Exchange: mockEx,
Account: account,
}
session.SetMarkets(testMarkets)
sessions := map[string]*qbtrade.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 := &qbtrade.ExchangeSession{
Exchange: mockEx,
Account: account,
}
session.SetMarkets(testMarkets)
sessions := map[string]*qbtrade.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())
})
}