bbgo_origin/pkg/exchange/ftx/convert.go
2022-03-03 01:47:19 +08:00

250 lines
6.7 KiB
Go

package ftx
import (
"fmt"
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/exchange/ftx/ftxapi"
"github.com/c9s/bbgo/pkg/types"
)
func toGlobalCurrency(original string) string {
return TrimUpperString(original)
}
func toGlobalSymbol(original string) string {
return strings.ReplaceAll(TrimUpperString(original), "/", "")
}
func toLocalSymbol(original string) string {
if symbolMap[original] == "" {
return original
}
return symbolMap[original]
}
func TrimUpperString(original string) string {
return strings.ToUpper(strings.TrimSpace(original))
}
func TrimLowerString(original string) string {
return strings.ToLower(strings.TrimSpace(original))
}
var errUnsupportedOrderStatus = fmt.Errorf("unsupported order status")
func toGlobalOrderNew(r ftxapi.Order) (types.Order, error) {
// In exchange/max/convert.go, it only parses these fields.
timeInForce := types.TimeInForceGTC
if r.Ioc {
timeInForce = types.TimeInForceIOC
}
// order type definition: https://github.com/ftexchange/ftx/blob/master/rest/client.py#L122
orderType := types.OrderType(TrimUpperString(string(r.Type)))
if orderType == types.OrderTypeLimit && r.PostOnly {
orderType = types.OrderTypeLimitMaker
}
o := types.Order{
SubmitOrder: types.SubmitOrder{
ClientOrderID: r.ClientId,
Symbol: toGlobalSymbol(r.Market),
Side: types.SideType(TrimUpperString(string(r.Side))),
Type: orderType,
Quantity: r.Size,
Price: r.Price,
TimeInForce: timeInForce,
},
Exchange: types.ExchangeFTX,
IsWorking: r.Status == ftxapi.OrderStatusOpen || r.Status == ftxapi.OrderStatusNew,
OrderID: uint64(r.Id),
Status: "",
ExecutedQuantity: r.FilledSize,
CreationTime: types.Time(r.CreatedAt),
UpdateTime: types.Time(r.CreatedAt),
}
s, err := toGlobalOrderStatus(r, r.Status)
o.Status = s
return o, err
}
func toGlobalOrderStatus(o ftxapi.Order, s ftxapi.OrderStatus) (types.OrderStatus, error) {
switch s {
case ftxapi.OrderStatusNew:
return types.OrderStatusNew, nil
case ftxapi.OrderStatusOpen:
if !o.FilledSize.IsZero() {
return types.OrderStatusPartiallyFilled, nil
} else {
return types.OrderStatusNew, nil
}
case ftxapi.OrderStatusClosed:
// filled or canceled
if o.FilledSize == o.Size {
return types.OrderStatusFilled, nil
} else {
// can't distinguish it's canceled or rejected from order response, so always set to canceled
return types.OrderStatusCanceled, nil
}
}
return "", fmt.Errorf("unsupported ftx order status %s: %w", s, errUnsupportedOrderStatus)
}
func toGlobalOrder(r order) (types.Order, error) {
// In exchange/max/convert.go, it only parses these fields.
timeInForce := types.TimeInForceGTC
if r.Ioc {
timeInForce = types.TimeInForceIOC
}
// order type definition: https://github.com/ftexchange/ftx/blob/master/rest/client.py#L122
orderType := types.OrderType(TrimUpperString(r.Type))
if orderType == types.OrderTypeLimit && r.PostOnly {
orderType = types.OrderTypeLimitMaker
}
o := types.Order{
SubmitOrder: types.SubmitOrder{
ClientOrderID: r.ClientId,
Symbol: toGlobalSymbol(r.Market),
Side: types.SideType(TrimUpperString(r.Side)),
Type: orderType,
Quantity: r.Size,
Price: r.Price,
TimeInForce: timeInForce,
},
Exchange: types.ExchangeFTX,
IsWorking: r.Status == "open",
OrderID: uint64(r.ID),
Status: "",
ExecutedQuantity: r.FilledSize,
CreationTime: types.Time(r.CreatedAt.Time),
UpdateTime: types.Time(r.CreatedAt.Time),
}
// `new` (accepted but not processed yet), `open`, or `closed` (filled or cancelled)
switch r.Status {
case "new":
o.Status = types.OrderStatusNew
case "open":
if !o.ExecutedQuantity.IsZero() {
o.Status = types.OrderStatusPartiallyFilled
} else {
o.Status = types.OrderStatusNew
}
case "closed":
// filled or canceled
if o.Quantity == o.ExecutedQuantity {
o.Status = types.OrderStatusFilled
} else {
// can't distinguish it's canceled or rejected from order response, so always set to canceled
o.Status = types.OrderStatusCanceled
}
default:
return types.Order{}, fmt.Errorf("unsupported status %s: %w", r.Status, errUnsupportedOrderStatus)
}
return o, nil
}
func toGlobalDeposit(input depositHistory) (types.Deposit, error) {
s, err := toGlobalDepositStatus(input.Status)
if err != nil {
log.WithError(err).Warnf("assign empty string to the deposit status")
}
t := input.Time
if input.ConfirmedTime.Time != (time.Time{}) {
t = input.ConfirmedTime
}
d := types.Deposit{
GID: 0,
Exchange: types.ExchangeFTX,
Time: types.Time(t.Time),
Amount: input.Size,
Asset: toGlobalCurrency(input.Coin),
TransactionID: input.TxID,
Status: s,
Address: input.Address.Address,
AddressTag: input.Address.Tag,
}
return d, nil
}
func toGlobalDepositStatus(input string) (types.DepositStatus, error) {
// The document only list `confirmed` status
switch input {
case "confirmed", "complete":
return types.DepositSuccess, nil
}
return "", fmt.Errorf("unsupported status %s", input)
}
func toGlobalTrade(f ftxapi.Fill) (types.Trade, error) {
return types.Trade{
ID: f.TradeId,
OrderID: f.OrderId,
Exchange: types.ExchangeFTX,
Price: f.Price,
Quantity: f.Size,
QuoteQuantity: f.Price.Mul(f.Size),
Symbol: toGlobalSymbol(f.Market),
Side: types.SideType(strings.ToUpper(string(f.Side))),
IsBuyer: f.Side == ftxapi.SideBuy,
IsMaker: f.Liquidity == ftxapi.LiquidityMaker,
Time: types.Time(f.Time),
Fee: f.Fee,
FeeCurrency: f.FeeCurrency,
IsMargin: false,
IsIsolated: false,
IsFutures: f.Future != "",
}, nil
}
func toGlobalKLine(symbol string, interval types.Interval, h Candle) (types.KLine, error) {
return types.KLine{
Exchange: types.ExchangeFTX,
Symbol: toGlobalSymbol(symbol),
StartTime: types.Time(h.StartTime.Time),
EndTime: types.Time(h.StartTime.Add(interval.Duration())),
Interval: interval,
Open: h.Open,
Close: h.Close,
High: h.High,
Low: h.Low,
Volume: h.Volume,
Closed: true,
}, nil
}
type OrderType string
const (
OrderTypeLimit OrderType = "limit"
OrderTypeMarket OrderType = "market"
)
func toLocalOrderType(orderType types.OrderType) (ftxapi.OrderType, error) {
switch orderType {
case types.OrderTypeLimitMaker:
return ftxapi.OrderTypeLimit, nil
case types.OrderTypeLimit:
return ftxapi.OrderTypeLimit, nil
case types.OrderTypeMarket:
return ftxapi.OrderTypeMarket, nil
}
return "", fmt.Errorf("order type %s not supported", orderType)
}