//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()) }) }