bbgo_origin/pkg/exchange/max/convert.go

334 lines
8.6 KiB
Go
Raw Normal View History

package max
import (
"fmt"
"strings"
"time"
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"
)
func toGlobalCurrency(currency string) string {
return strings.ToUpper(currency)
}
func toLocalCurrency(currency string) string {
return strings.ToLower(currency)
}
func toLocalSymbol(symbol string) string {
return strings.ToLower(symbol)
}
func toGlobalSymbol(symbol string) string {
return strings.ToUpper(symbol)
}
func toLocalSideType(side types.SideType) string {
return strings.ToLower(string(side))
}
func toGlobalSideType(v string) types.SideType {
switch strings.ToLower(v) {
case "bid", "buy":
return types.SideTypeBuy
case "ask", "sell":
return types.SideTypeSell
case "self-trade":
return types.SideTypeSelf
}
return types.SideType(v)
}
func toGlobalRewards(maxRewards []max.Reward) ([]types.Reward, error) {
// convert to global reward
var rewards []types.Reward
for _, r := range maxRewards {
// ignore "accepted"
if r.State != "done" {
continue
}
reward, err := r.Reward()
if err != nil {
return nil, err
}
rewards = append(rewards, *reward)
}
return rewards, nil
}
func toGlobalOrderStatus(orderState max.OrderState, executedVolume, remainingVolume fixedpoint.Value) types.OrderStatus {
switch orderState {
2021-12-26 16:21:52 +00:00
case max.OrderStateCancel:
return types.OrderStatusCanceled
case max.OrderStateFinalizing, max.OrderStateDone:
if executedVolume.IsZero() {
2021-12-26 16:21:52 +00:00
return types.OrderStatusCanceled
} else if remainingVolume.IsZero() {
return types.OrderStatusFilled
}
return types.OrderStatusFilled
case max.OrderStateWait:
if executedVolume.Sign() > 0 && remainingVolume.Sign() > 0 {
return types.OrderStatusPartiallyFilled
}
return types.OrderStatusNew
case max.OrderStateConvert:
if executedVolume.Sign() > 0 && remainingVolume.Sign() > 0 {
return types.OrderStatusPartiallyFilled
}
return types.OrderStatusNew
case max.OrderStateFailed:
return types.OrderStatusRejected
}
log.Errorf("can not convert MAX exchange order status, unknown order state: %q", orderState)
return types.OrderStatus(orderState)
}
func toGlobalOrderType(orderType max.OrderType) types.OrderType {
switch orderType {
case max.OrderTypeLimit:
return types.OrderTypeLimit
case max.OrderTypeMarket:
return types.OrderTypeMarket
case max.OrderTypeStopLimit:
return types.OrderTypeStopLimit
case max.OrderTypeStopMarket:
return types.OrderTypeStopMarket
2021-04-11 12:07:05 +00:00
case max.OrderTypeIOCLimit:
2022-02-18 05:57:47 +00:00
return types.OrderTypeLimit
2021-04-11 12:07:05 +00:00
2021-05-14 03:53:07 +00:00
case max.OrderTypePostOnly:
return types.OrderTypeLimitMaker
}
2022-01-02 04:00:06 +00:00
log.Errorf("order convert error, unknown order type: %v", orderType)
return types.OrderType(orderType)
}
func toLocalOrderType(orderType types.OrderType) (max.OrderType, error) {
switch orderType {
case types.OrderTypeStopLimit:
return max.OrderTypeStopLimit, nil
case types.OrderTypeStopMarket:
return max.OrderTypeStopMarket, nil
2021-03-22 09:25:25 +00:00
case types.OrderTypeLimitMaker:
return max.OrderTypePostOnly, nil
case types.OrderTypeLimit:
return max.OrderTypeLimit, nil
case types.OrderTypeMarket:
return max.OrderTypeMarket, nil
}
return "", fmt.Errorf("order type %s not supported", orderType)
}
2020-12-29 08:00:03 +00:00
func toGlobalOrders(maxOrders []max.Order) (orders []types.Order, err error) {
for _, localOrder := range maxOrders {
o, err := toGlobalOrder(localOrder)
if err != nil {
log.WithError(err).Error("order convert error")
} else {
orders = append(orders, *o)
2020-12-29 08:00:03 +00:00
}
}
return orders, err
}
func toGlobalOrder(maxOrder max.Order) (*types.Order, error) {
2022-05-26 11:52:38 +00:00
executedVolume := maxOrder.ExecutedVolume
remainingVolume := maxOrder.RemainingVolume
2022-05-27 11:20:45 +00:00
isMargin := maxOrder.WalletType == max.WalletTypeMargin
return &types.Order{
SubmitOrder: types.SubmitOrder{
ClientOrderID: maxOrder.ClientOID,
2020-11-03 09:55:20 +00:00
Symbol: toGlobalSymbol(maxOrder.Market),
Side: toGlobalSideType(maxOrder.Side),
Type: toGlobalOrderType(maxOrder.OrderType),
2022-05-26 11:52:38 +00:00
Quantity: maxOrder.Volume,
Price: maxOrder.Price,
TimeInForce: types.TimeInForceGTC, // MAX only supports GTC
2021-01-23 09:17:46 +00:00
GroupID: maxOrder.GroupID,
},
Exchange: types.ExchangeMax,
2022-05-26 11:52:38 +00:00
IsWorking: maxOrder.State == max.OrderStateWait,
OrderID: maxOrder.ID,
Status: toGlobalOrderStatus(maxOrder.State, executedVolume, remainingVolume),
ExecutedQuantity: executedVolume,
2022-05-27 11:20:45 +00:00
CreationTime: types.Time(maxOrder.CreatedAt.Time()),
UpdateTime: types.Time(maxOrder.UpdatedAt.Time()),
2022-05-27 11:20:45 +00:00
IsMargin: isMargin,
IsIsolated: false, // isolated margin is not supported
}, nil
}
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),
2023-07-27 08:28:54 +00:00
FeeDiscounted: t.FeeDiscounted,
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
2023-03-15 10:09:46 +00:00
bidTrade.FeeCurrency = toGlobalCurrency(t.SelfTradeBidFeeCurrency)
2023-07-27 08:28:54 +00:00
bidTrade.FeeDiscounted = t.SelfTradeBidFeeDiscounted
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) {
2022-05-27 11:20:45 +00:00
isMargin := t.WalletType == max.WalletTypeMargin
side := toGlobalSideType(t.Side)
return &types.Trade{
2021-12-23 05:15:27 +00:00
ID: t.ID,
2020-11-03 09:55:20 +00:00
OrderID: t.OrderID,
2022-05-27 11:20:45 +00:00
Price: t.Price,
Symbol: toGlobalSymbol(t.Market),
2022-05-27 11:20:45 +00:00
Exchange: types.ExchangeMax,
Quantity: t.Volume,
Side: side,
IsBuyer: t.IsBuyer(),
IsMaker: t.IsMaker(),
2022-05-27 11:20:45 +00:00
Fee: t.Fee,
FeeCurrency: toGlobalCurrency(t.FeeCurrency),
2022-05-27 11:20:45 +00:00
QuoteQuantity: t.Funds,
Time: types.Time(t.CreatedAt),
IsMargin: isMargin,
IsIsolated: false,
IsFutures: false,
}, nil
}
func toGlobalDepositStatus(a max.DepositState) types.DepositStatus {
switch a {
case max.DepositStateSubmitting, max.DepositStateSubmitted, max.DepositStatePending, max.DepositStateChecking:
return types.DepositPending
case max.DepositStateRejected:
return types.DepositRejected
case max.DepositStateCancelled:
return types.DepositCancelled
case max.DepositStateAccepted:
return types.DepositSuccess
}
// other states goes to this
// max.DepositStateSuspect, max.DepositStateSuspended
return types.DepositStatus(a)
}
func convertWebSocketTrade(t max.TradeUpdate) (*types.Trade, error) {
// skip trade ID that is the same. however this should not happen
var side = toGlobalSideType(t.Side)
return &types.Trade{
ID: t.ID,
OrderID: t.OrderID,
Symbol: toGlobalSymbol(t.Market),
Exchange: types.ExchangeMax,
Price: t.Price,
Quantity: t.Volume,
Side: side,
IsBuyer: side == types.SideTypeBuy,
IsMaker: t.Maker,
Fee: t.Fee,
FeeCurrency: toGlobalCurrency(t.FeeCurrency),
FeeDiscounted: t.FeeDiscounted,
QuoteQuantity: t.Price.Mul(t.Volume),
Time: types.Time(t.Timestamp.Time()),
}, nil
}
func convertWebSocketOrderUpdate(u max.OrderUpdate) (*types.Order, error) {
2022-03-06 10:37:34 +00:00
timeInForce := types.TimeInForceGTC
if u.OrderType == max.OrderTypeIOCLimit {
timeInForce = types.TimeInForceIOC
}
return &types.Order{
SubmitOrder: types.SubmitOrder{
ClientOrderID: u.ClientOID,
Symbol: toGlobalSymbol(u.Market),
Side: toGlobalSideType(u.Side),
Type: toGlobalOrderType(u.OrderType),
2022-05-27 11:20:45 +00:00
Quantity: u.Volume,
Price: u.Price,
StopPrice: u.StopPrice,
2022-03-06 10:37:34 +00:00
TimeInForce: timeInForce, // MAX only supports GTC
GroupID: u.GroupID,
},
Exchange: types.ExchangeMax,
OrderID: u.ID,
2022-05-27 11:20:45 +00:00
Status: toGlobalOrderStatus(u.State, u.ExecutedVolume, u.RemainingVolume),
ExecutedQuantity: u.ExecutedVolume,
CreationTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
2022-03-06 10:33:21 +00:00
UpdateTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
}, nil
}