mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-23 07:15:15 +00:00
280 lines
8.8 KiB
Go
280 lines
8.8 KiB
Go
package binance
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/adshao/go-binance/v2/futures"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
)
|
|
|
|
func toGlobalFuturesAccountInfo(account *futures.Account) *types.FuturesAccountInfo {
|
|
return &types.FuturesAccountInfo{
|
|
Assets: toGlobalFuturesUserAssets(account.Assets),
|
|
Positions: toGlobalFuturesPositions(account.Positions),
|
|
TotalInitialMargin: fixedpoint.MustNewFromString(account.TotalInitialMargin),
|
|
TotalMaintMargin: fixedpoint.MustNewFromString(account.TotalMaintMargin),
|
|
TotalMarginBalance: fixedpoint.MustNewFromString(account.TotalMarginBalance),
|
|
TotalOpenOrderInitialMargin: fixedpoint.MustNewFromString(account.TotalOpenOrderInitialMargin),
|
|
TotalPositionInitialMargin: fixedpoint.MustNewFromString(account.TotalPositionInitialMargin),
|
|
TotalUnrealizedProfit: fixedpoint.MustNewFromString(account.TotalUnrealizedProfit),
|
|
TotalWalletBalance: fixedpoint.MustNewFromString(account.TotalWalletBalance),
|
|
UpdateTime: account.UpdateTime,
|
|
}
|
|
}
|
|
|
|
func toGlobalFuturesBalance(balances []*futures.Balance) types.BalanceMap {
|
|
retBalances := make(types.BalanceMap)
|
|
for _, balance := range balances {
|
|
retBalances[balance.Asset] = types.Balance{
|
|
Currency: balance.Asset,
|
|
Available: fixedpoint.MustNewFromString(balance.AvailableBalance),
|
|
}
|
|
}
|
|
return retBalances
|
|
}
|
|
|
|
func toGlobalFuturesPositions(futuresPositions []*futures.AccountPosition) types.FuturesPositionMap {
|
|
retFuturesPositions := make(types.FuturesPositionMap)
|
|
for _, futuresPosition := range futuresPositions {
|
|
retFuturesPositions[futuresPosition.Symbol] = types.FuturesPosition{ // TODO: types.FuturesPosition
|
|
Isolated: futuresPosition.Isolated,
|
|
PositionRisk: &types.PositionRisk{
|
|
Leverage: fixedpoint.MustNewFromString(futuresPosition.Leverage),
|
|
},
|
|
Symbol: futuresPosition.Symbol,
|
|
UpdateTime: futuresPosition.UpdateTime,
|
|
}
|
|
}
|
|
|
|
return retFuturesPositions
|
|
}
|
|
|
|
func toGlobalFuturesUserAssets(assets []*futures.AccountAsset) (retAssets types.FuturesAssetMap) {
|
|
retFuturesAssets := make(types.FuturesAssetMap)
|
|
for _, futuresAsset := range assets {
|
|
retFuturesAssets[futuresAsset.Asset] = types.FuturesUserAsset{
|
|
Asset: futuresAsset.Asset,
|
|
InitialMargin: fixedpoint.MustNewFromString(futuresAsset.InitialMargin),
|
|
MaintMargin: fixedpoint.MustNewFromString(futuresAsset.MaintMargin),
|
|
MarginBalance: fixedpoint.MustNewFromString(futuresAsset.MarginBalance),
|
|
MaxWithdrawAmount: fixedpoint.MustNewFromString(futuresAsset.MaxWithdrawAmount),
|
|
OpenOrderInitialMargin: fixedpoint.MustNewFromString(futuresAsset.OpenOrderInitialMargin),
|
|
PositionInitialMargin: fixedpoint.MustNewFromString(futuresAsset.PositionInitialMargin),
|
|
UnrealizedProfit: fixedpoint.MustNewFromString(futuresAsset.UnrealizedProfit),
|
|
WalletBalance: fixedpoint.MustNewFromString(futuresAsset.WalletBalance),
|
|
}
|
|
}
|
|
|
|
return retFuturesAssets
|
|
}
|
|
|
|
func toLocalFuturesOrderType(orderType types.OrderType) (futures.OrderType, error) {
|
|
switch orderType {
|
|
|
|
// case types.OrderTypeLimitMaker:
|
|
// return futures.OrderTypeLimitMaker, nil //TODO
|
|
|
|
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
|
|
return futures.OrderTypeLimit, nil
|
|
|
|
// case types.OrderTypeStopLimit:
|
|
// return futures.OrderTypeStopLossLimit, nil //TODO
|
|
|
|
// case types.OrderTypeStopMarket:
|
|
// return futures.OrderTypeStopLoss, nil //TODO
|
|
|
|
case types.OrderTypeMarket:
|
|
return futures.OrderTypeMarket, nil
|
|
}
|
|
|
|
return "", fmt.Errorf("can not convert to local order, order type %s not supported", orderType)
|
|
}
|
|
|
|
func toGlobalFuturesOrders(futuresOrders []*futures.Order) (orders []types.Order, err error) {
|
|
for _, futuresOrder := range futuresOrders {
|
|
order, err := toGlobalFuturesOrder(futuresOrder, false)
|
|
if err != nil {
|
|
return orders, err
|
|
}
|
|
|
|
orders = append(orders, *order)
|
|
}
|
|
|
|
return orders, err
|
|
}
|
|
|
|
func toGlobalFuturesOrder(futuresOrder *futures.Order, isMargin bool) (*types.Order, error) {
|
|
return &types.Order{
|
|
SubmitOrder: types.SubmitOrder{
|
|
ClientOrderID: futuresOrder.ClientOrderID,
|
|
Symbol: futuresOrder.Symbol,
|
|
Side: toGlobalFuturesSideType(futuresOrder.Side),
|
|
Type: toGlobalFuturesOrderType(futuresOrder.Type),
|
|
ReduceOnly: futuresOrder.ReduceOnly,
|
|
ClosePosition: futuresOrder.ClosePosition,
|
|
Quantity: fixedpoint.MustNewFromString(futuresOrder.OrigQuantity),
|
|
Price: fixedpoint.MustNewFromString(futuresOrder.Price),
|
|
TimeInForce: types.TimeInForce(futuresOrder.TimeInForce),
|
|
},
|
|
Exchange: types.ExchangeBinance,
|
|
OrderID: uint64(futuresOrder.OrderID),
|
|
Status: toGlobalFuturesOrderStatus(futuresOrder.Status),
|
|
ExecutedQuantity: fixedpoint.MustNewFromString(futuresOrder.ExecutedQuantity),
|
|
CreationTime: types.Time(millisecondTime(futuresOrder.Time)),
|
|
UpdateTime: types.Time(millisecondTime(futuresOrder.UpdateTime)),
|
|
IsMargin: isMargin,
|
|
}, nil
|
|
}
|
|
|
|
func toGlobalFuturesTrade(t futures.AccountTrade) (*types.Trade, error) {
|
|
// skip trade ID that is the same. however this should not happen
|
|
var side types.SideType
|
|
if t.Buyer {
|
|
side = types.SideTypeBuy
|
|
} else {
|
|
side = types.SideTypeSell
|
|
}
|
|
|
|
price, err := fixedpoint.NewFromString(t.Price)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "price parse error, price: %+v", t.Price)
|
|
}
|
|
|
|
quantity, err := fixedpoint.NewFromString(t.Quantity)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "quantity parse error, quantity: %+v", t.Quantity)
|
|
}
|
|
|
|
var quoteQuantity fixedpoint.Value
|
|
if len(t.QuoteQuantity) > 0 {
|
|
quoteQuantity, err = fixedpoint.NewFromString(t.QuoteQuantity)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "quote quantity parse error, quoteQuantity: %+v", t.QuoteQuantity)
|
|
}
|
|
} else {
|
|
quoteQuantity = price.Mul(quantity)
|
|
}
|
|
|
|
fee, err := fixedpoint.NewFromString(t.Commission)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "commission parse error, commission: %+v", t.Commission)
|
|
}
|
|
|
|
return &types.Trade{
|
|
ID: uint64(t.ID),
|
|
OrderID: uint64(t.OrderID),
|
|
Price: price,
|
|
Symbol: t.Symbol,
|
|
Exchange: "binance",
|
|
Quantity: quantity,
|
|
QuoteQuantity: quoteQuantity,
|
|
Side: side,
|
|
IsBuyer: t.Buyer,
|
|
IsMaker: t.Maker,
|
|
Fee: fee,
|
|
FeeCurrency: t.CommissionAsset,
|
|
Time: types.Time(millisecondTime(t.Time)),
|
|
IsFutures: true,
|
|
}, nil
|
|
}
|
|
|
|
func toGlobalFuturesSideType(side futures.SideType) types.SideType {
|
|
switch side {
|
|
case futures.SideTypeBuy:
|
|
return types.SideTypeBuy
|
|
|
|
case futures.SideTypeSell:
|
|
return types.SideTypeSell
|
|
|
|
default:
|
|
log.Errorf("can not convert futures side type, unknown side type: %q", side)
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func toGlobalFuturesOrderType(orderType futures.OrderType) types.OrderType {
|
|
switch orderType {
|
|
// TODO
|
|
case futures.OrderTypeLimit: // , futures.OrderTypeLimitMaker, futures.OrderTypeTakeProfitLimit:
|
|
return types.OrderTypeLimit
|
|
|
|
case futures.OrderTypeMarket:
|
|
return types.OrderTypeMarket
|
|
// TODO
|
|
// case futures.OrderTypeStopLossLimit:
|
|
// return types.OrderTypeStopLimit
|
|
// TODO
|
|
// case futures.OrderTypeStopLoss:
|
|
// return types.OrderTypeStopMarket
|
|
|
|
default:
|
|
log.Errorf("unsupported order type: %v", orderType)
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func toGlobalFuturesOrderStatus(orderStatus futures.OrderStatusType) types.OrderStatus {
|
|
switch orderStatus {
|
|
case futures.OrderStatusTypeNew:
|
|
return types.OrderStatusNew
|
|
|
|
case futures.OrderStatusTypeRejected:
|
|
return types.OrderStatusRejected
|
|
|
|
case futures.OrderStatusTypeCanceled:
|
|
return types.OrderStatusCanceled
|
|
|
|
case futures.OrderStatusTypePartiallyFilled:
|
|
return types.OrderStatusPartiallyFilled
|
|
|
|
case futures.OrderStatusTypeFilled:
|
|
return types.OrderStatusFilled
|
|
}
|
|
|
|
return types.OrderStatus(orderStatus)
|
|
}
|
|
|
|
func convertPremiumIndex(index *futures.PremiumIndex) (*types.PremiumIndex, error) {
|
|
markPrice, err := fixedpoint.NewFromString(index.MarkPrice)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lastFundingRate, err := fixedpoint.NewFromString(index.LastFundingRate)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nextFundingTime := time.Unix(0, index.NextFundingTime*int64(time.Millisecond))
|
|
t := time.Unix(0, index.Time*int64(time.Millisecond))
|
|
|
|
return &types.PremiumIndex{
|
|
Symbol: index.Symbol,
|
|
MarkPrice: markPrice,
|
|
NextFundingTime: nextFundingTime,
|
|
LastFundingRate: lastFundingRate,
|
|
Time: t,
|
|
}, nil
|
|
}
|
|
|
|
func convertPositionRisk(risk *futures.PositionRisk) (*types.PositionRisk, error) {
|
|
leverage, err := fixedpoint.NewFromString(risk.Leverage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
liquidationPrice, err := fixedpoint.NewFromString(risk.LiquidationPrice)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &types.PositionRisk{
|
|
Leverage: leverage,
|
|
LiquidationPrice: liquidationPrice,
|
|
}, nil
|
|
}
|