FEATURE: split self trades when use MAX RESTful API to query trades

This commit is contained in:
chiahung 2023-03-08 17:18:18 +08:00
parent f26568e213
commit f9f6346468
7 changed files with 203 additions and 12 deletions

View File

@ -5,7 +5,8 @@ import (
"strings"
"time"
"github.com/c9s/bbgo/pkg/exchange/max/maxapi"
max "github.com/c9s/bbgo/pkg/exchange/max/maxapi"
v3 "github.com/c9s/bbgo/pkg/exchange/max/maxapi/v3"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
@ -193,7 +194,50 @@ func toGlobalOrder(maxOrder max.Order) (*types.Order, error) {
}, nil
}
func toGlobalTrade(t max.Trade) (*types.Trade, error) {
func toGlobalTradeV3(t v3.Trade) ([]types.Trade, error) {
var trades []types.Trade
isMargin := t.WalletType == max.WalletTypeMargin
side := toGlobalSideType(t.Side)
trade := types.Trade{
ID: t.ID,
OrderID: t.OrderID,
Price: t.Price,
Symbol: toGlobalSymbol(t.Market),
Exchange: types.ExchangeMax,
Quantity: t.Volume,
Side: side,
IsBuyer: t.IsBuyer(),
IsMaker: t.IsMaker(),
Fee: t.Fee,
FeeCurrency: toGlobalCurrency(t.FeeCurrency),
QuoteQuantity: t.Funds,
Time: types.Time(t.CreatedAt),
IsMargin: isMargin,
IsIsolated: false,
IsFutures: false,
}
if t.Side == "self-trade" {
trade.Side = types.SideTypeSell
// create trade for bid
bidTrade := trade
bidTrade.Side = types.SideTypeBuy
bidTrade.OrderID = t.SelfTradeBidOrderID
bidTrade.Fee = t.SelfTradeBidFee
bidTrade.FeeCurrency = t.SelfTradeBidFeeCurrency
bidTrade.IsBuyer = !trade.IsBuyer
bidTrade.IsMaker = !trade.IsMaker
trades = append(trades, bidTrade)
}
trades = append(trades, trade)
return trades, nil
}
func toGlobalTradeV2(t max.Trade) (*types.Trade, error) {
isMargin := t.WalletType == max.WalletTypeMargin
side := toGlobalSideType(t.Side)
return &types.Trade{

View File

@ -0,0 +1,116 @@
package max
import (
"encoding/json"
"testing"
v3 "github.com/c9s/bbgo/pkg/exchange/max/maxapi/v3"
"github.com/c9s/bbgo/pkg/types"
"github.com/stretchr/testify/assert"
)
func Test_toGlobalTradeV3(t *testing.T) {
assert := assert.New(t)
t.Run("ask trade", func(t *testing.T) {
str := `
{
"id": 68444,
"order_id": 87,
"wallet_type": "spot",
"price": "21499.0",
"volume": "0.2658",
"funds": "5714.4",
"market": "ethtwd",
"market_name": "ETH/TWD",
"side": "bid",
"fee": "0.00001",
"fee_currency": "usdt",
"self_trade_bid_fee": "0.00001",
"self_trade_bid_fee_currency": "eth",
"self_trade_bid_order_id": 86,
"liquidity": "maker",
"created_at": 1521726960357
}
`
var trade v3.Trade
assert.NoError(json.Unmarshal([]byte(str), &trade))
trades, err := toGlobalTradeV3(trade)
assert.NoError(err)
assert.Len(trades, 1)
assert.Equal(uint64(87), trades[0].OrderID)
assert.Equal(types.SideTypeBuy, trades[0].Side)
})
t.Run("bid trade", func(t *testing.T) {
str := `
{
"id": 68444,
"order_id": 87,
"wallet_type": "spot",
"price": "21499.0",
"volume": "0.2658",
"funds": "5714.4",
"market": "ethtwd",
"market_name": "ETH/TWD",
"side": "ask",
"fee": "0.00001",
"fee_currency": "usdt",
"self_trade_bid_fee": "0.00001",
"self_trade_bid_fee_currency": "eth",
"self_trade_bid_order_id": 86,
"liquidity": "maker",
"created_at": 1521726960357
}
`
var trade v3.Trade
assert.NoError(json.Unmarshal([]byte(str), &trade))
trades, err := toGlobalTradeV3(trade)
assert.NoError(err)
assert.Len(trades, 1)
assert.Equal(uint64(87), trades[0].OrderID)
assert.Equal(types.SideTypeSell, trades[0].Side)
})
t.Run("self trade", func(t *testing.T) {
str := `
{
"id": 68444,
"order_id": 87,
"wallet_type": "spot",
"price": "21499.0",
"volume": "0.2658",
"funds": "5714.4",
"market": "ethtwd",
"market_name": "ETH/TWD",
"side": "self-trade",
"fee": "0.00001",
"fee_currency": "usdt",
"self_trade_bid_fee": "0.00001",
"self_trade_bid_fee_currency": "eth",
"self_trade_bid_order_id": 86,
"liquidity": "maker",
"created_at": 1521726960357
}
`
var trade v3.Trade
assert.NoError(json.Unmarshal([]byte(str), &trade))
trades, err := toGlobalTradeV3(trade)
assert.NoError(err)
assert.Len(trades, 2)
assert.Equal(uint64(86), trades[0].OrderID)
assert.Equal(types.SideTypeBuy, trades[0].Side)
assert.Equal(uint64(87), trades[1].OrderID)
assert.Equal(types.SideTypeSell, trades[1].Side)
})
}

View File

@ -186,13 +186,13 @@ func (e *Exchange) QueryOrderTrades(ctx context.Context, q types.OrderQuery) ([]
var trades []types.Trade
for _, t := range maxTrades {
localTrade, err := toGlobalTrade(t)
localTrades, err := toGlobalTradeV3(t)
if err != nil {
log.WithError(err).Errorf("can not convert trade: %+v", t)
continue
}
trades = append(trades, *localTrade)
trades = append(trades, localTrades...)
}
// ensure everything is sorted ascending
@ -806,13 +806,13 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
}
for _, t := range maxTrades {
localTrade, err := toGlobalTrade(t)
localTrades, err := toGlobalTradeV3(t)
if err != nil {
log.WithError(err).Errorf("can not convert trade: %+v", t)
continue
}
trades = append(trades, *localTrade)
trades = append(trades, localTrades...)
}
// ensure everything is sorted ascending

View File

@ -6,7 +6,6 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/exchange/max/maxapi"
"net/url"
"reflect"
"regexp"
@ -136,7 +135,7 @@ func (g *GetOrderTradesRequest) GetSlugsMap() (map[string]string, error) {
return slugs, nil
}
func (g *GetOrderTradesRequest) Do(ctx context.Context) ([]max.Trade, error) {
func (g *GetOrderTradesRequest) Do(ctx context.Context) ([]Trade, error) {
// empty params for GET operation
var params interface{}
@ -157,7 +156,7 @@ func (g *GetOrderTradesRequest) Do(ctx context.Context) ([]max.Trade, error) {
return nil, err
}
var apiResponse []max.Trade
var apiResponse []Trade
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}

View File

@ -198,7 +198,7 @@ func (g *GetWalletTradesRequest) GetSlugsMap() (map[string]string, error) {
return slugs, nil
}
func (g *GetWalletTradesRequest) Do(ctx context.Context) ([]max.Trade, error) {
func (g *GetWalletTradesRequest) Do(ctx context.Context) ([]Trade, error) {
// empty params for GET operation
var params interface{}
@ -225,7 +225,7 @@ func (g *GetWalletTradesRequest) Do(ctx context.Context) ([]max.Trade, error) {
return nil, err
}
var apiResponse []max.Trade
var apiResponse []Trade
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}

View File

@ -15,7 +15,6 @@ type WalletType = maxapi.WalletType
type OrderType = maxapi.OrderType
type Order = maxapi.Order
type Trade = maxapi.Trade
type Account = maxapi.Account
// OrderService manages the Order endpoint.

View File

@ -0,0 +1,33 @@
package v3
import (
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type Trade struct {
ID uint64 `json:"id" db:"exchange_id"`
WalletType WalletType `json:"wallet_type,omitempty"`
Price fixedpoint.Value `json:"price"`
Volume fixedpoint.Value `json:"volume"`
Funds fixedpoint.Value `json:"funds"`
Market string `json:"market"`
MarketName string `json:"market_name"`
CreatedAt types.MillisecondTimestamp `json:"created_at"`
Side string `json:"side"`
OrderID uint64 `json:"order_id"`
Fee fixedpoint.Value `json:"fee"` // float number as string
FeeCurrency string `json:"fee_currency"`
Liquidity string `json:"liquidity"`
SelfTradeBidFee fixedpoint.Value `json:"self_trade_bid_fee"`
SelfTradeBidFeeCurrency string `json:"self_trade_bid_fee_currency"`
SelfTradeBidOrderID uint64 `json:"self_trade_bid_order_id"`
}
func (t Trade) IsBuyer() bool {
return t.Side == "bid"
}
func (t Trade) IsMaker() bool {
return t.Liquidity == "maker"
}