diff --git a/pkg/exchange/binance/convert.go b/pkg/exchange/binance/convert.go index 1805db858..2a885667e 100644 --- a/pkg/exchange/binance/convert.go +++ b/pkg/exchange/binance/convert.go @@ -84,46 +84,6 @@ func toGlobalFuturesMarket(symbol futures.Symbol) types.Market { return market } -func toGlobalIsolatedUserAsset(userAsset binance.IsolatedUserAsset) types.IsolatedUserAsset { - return types.IsolatedUserAsset{ - Asset: userAsset.Asset, - Borrowed: fixedpoint.MustNewFromString(userAsset.Borrowed), - Free: fixedpoint.MustNewFromString(userAsset.Free), - Interest: fixedpoint.MustNewFromString(userAsset.Interest), - Locked: fixedpoint.MustNewFromString(userAsset.Locked), - NetAsset: fixedpoint.MustNewFromString(userAsset.NetAsset), - NetAssetOfBtc: fixedpoint.MustNewFromString(userAsset.NetAssetOfBtc), - BorrowEnabled: userAsset.BorrowEnabled, - RepayEnabled: userAsset.RepayEnabled, - TotalAsset: fixedpoint.MustNewFromString(userAsset.TotalAsset), - } -} - -func toGlobalIsolatedMarginAsset(asset binance.IsolatedMarginAsset) types.IsolatedMarginAsset { - return types.IsolatedMarginAsset{ - Symbol: asset.Symbol, - QuoteAsset: toGlobalIsolatedUserAsset(asset.QuoteAsset), - BaseAsset: toGlobalIsolatedUserAsset(asset.BaseAsset), - IsolatedCreated: asset.IsolatedCreated, - MarginLevel: fixedpoint.MustNewFromString(asset.MarginLevel), - MarginLevelStatus: asset.MarginLevelStatus, - MarginRatio: fixedpoint.MustNewFromString(asset.MarginRatio), - IndexPrice: fixedpoint.MustNewFromString(asset.IndexPrice), - LiquidatePrice: fixedpoint.MustNewFromString(asset.LiquidatePrice), - LiquidateRate: fixedpoint.MustNewFromString(asset.LiquidateRate), - TradeEnabled: false, - } -} - -func toGlobalIsolatedMarginAssets(assets []binance.IsolatedMarginAsset) (retAssets types.IsolatedMarginAssetMap) { - retMarginAssets := make(types.IsolatedMarginAssetMap) - for _, marginAsset := range assets { - retMarginAssets[marginAsset.Symbol] = toGlobalIsolatedMarginAsset(marginAsset) - } - - return retMarginAssets -} - //func toGlobalIsolatedMarginAccount(account *binance.IsolatedMarginAccount) *types.IsolatedMarginAccount { // return &types.IsolatedMarginAccount{ // TotalAssetOfBTC: fixedpoint.MustNewFromString(account.TotalNetAssetOfBTC), @@ -133,105 +93,6 @@ func toGlobalIsolatedMarginAssets(assets []binance.IsolatedMarginAsset) (retAsse // } //} -func toGlobalMarginUserAssets(assets []binance.UserAsset) types.MarginAssetMap { - retMarginAssets := make(types.MarginAssetMap) - for _, marginAsset := range assets { - retMarginAssets[marginAsset.Asset] = types.MarginUserAsset{ - Asset: marginAsset.Asset, - Borrowed: fixedpoint.MustNewFromString(marginAsset.Borrowed), - Free: fixedpoint.MustNewFromString(marginAsset.Free), - Interest: fixedpoint.MustNewFromString(marginAsset.Interest), - Locked: fixedpoint.MustNewFromString(marginAsset.Locked), - NetAsset: fixedpoint.MustNewFromString(marginAsset.NetAsset), - } - } - - return retMarginAssets -} - -func toGlobalMarginAccountInfo(account *binance.MarginAccount) *types.MarginAccountInfo { - return &types.MarginAccountInfo{ - BorrowEnabled: account.BorrowEnabled, - MarginLevel: fixedpoint.MustNewFromString(account.MarginLevel), - TotalAssetOfBTC: fixedpoint.MustNewFromString(account.TotalAssetOfBTC), - TotalLiabilityOfBTC: fixedpoint.MustNewFromString(account.TotalLiabilityOfBTC), - TotalNetAssetOfBTC: fixedpoint.MustNewFromString(account.TotalNetAssetOfBTC), - TradeEnabled: account.TradeEnabled, - TransferEnabled: account.TransferEnabled, - Assets: toGlobalMarginUserAssets(account.UserAssets), - } -} - -func toGlobalIsolatedMarginAccountInfo(account *binance.IsolatedMarginAccount) *types.IsolatedMarginAccountInfo { - return &types.IsolatedMarginAccountInfo{ - TotalAssetOfBTC: fixedpoint.MustNewFromString(account.TotalAssetOfBTC), - TotalLiabilityOfBTC: fixedpoint.MustNewFromString(account.TotalLiabilityOfBTC), - TotalNetAssetOfBTC: fixedpoint.MustNewFromString(account.TotalNetAssetOfBTC), - Assets: toGlobalIsolatedMarginAssets(account.Assets), - } -} - -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 toGlobalTicker(stats *binance.PriceChangeStats) (*types.Ticker, error) { return &types.Ticker{ Volume: fixedpoint.MustNewFromString(stats.Volume), @@ -267,28 +128,6 @@ func toLocalOrderType(orderType types.OrderType) (binance.OrderType, error) { return "", fmt.Errorf("can not convert to local order, order type %s not supported", orderType) } -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 toGlobalOrders(binanceOrders []*binance.Order) (orders []types.Order, err error) { for _, binanceOrder := range binanceOrders { order, err := toGlobalOrder(binanceOrder, false) @@ -302,19 +141,6 @@ func toGlobalOrders(binanceOrders []*binance.Order) (orders []types.Order, err e return orders, err } -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 toGlobalOrder(binanceOrder *binance.Order, isMargin bool) (*types.Order, error) { return &types.Order{ SubmitOrder: types.SubmitOrder{ @@ -338,29 +164,6 @@ func toGlobalOrder(binanceOrder *binance.Order, isMargin bool) (*types.Order, er }, nil } -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 millisecondTime(t int64) time.Time { return time.Unix(0, t*int64(time.Millisecond)) } @@ -418,58 +221,6 @@ func toGlobalTrade(t binance.TradeV3, isMargin bool) (*types.Trade, error) { }, 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 toGlobalSideType(side binance.SideType) types.SideType { switch side { case binance.SideTypeBuy: @@ -484,20 +235,6 @@ func toGlobalSideType(side binance.SideType) types.SideType { } } -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 toGlobalOrderType(orderType binance.OrderType) types.OrderType { switch orderType { @@ -520,27 +257,6 @@ func toGlobalOrderType(orderType binance.OrderType) types.OrderType { } } -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 toGlobalOrderStatus(orderStatus binance.OrderStatusType) types.OrderStatus { switch orderStatus { case binance.OrderStatusTypeNew: @@ -562,27 +278,6 @@ func toGlobalOrderStatus(orderStatus binance.OrderStatusType) types.OrderStatus return types.OrderStatus(orderStatus) } -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 convertSubscription(s types.Subscription) string { // binance uses lower case symbol name, // for kline, it's "@kline_" @@ -623,42 +318,3 @@ func convertSubscription(s types.Subscription) string { return fmt.Sprintf("%s@%s", strings.ToLower(s.Symbol), s.Channel) } -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 -} diff --git a/pkg/exchange/binance/convert_futures.go b/pkg/exchange/binance/convert_futures.go new file mode 100644 index 000000000..4a26f248c --- /dev/null +++ b/pkg/exchange/binance/convert_futures.go @@ -0,0 +1,279 @@ +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 +} diff --git a/pkg/exchange/binance/convert_margin.go b/pkg/exchange/binance/convert_margin.go new file mode 100644 index 000000000..40aa56e4e --- /dev/null +++ b/pkg/exchange/binance/convert_margin.go @@ -0,0 +1,119 @@ +package binance + +import ( + "github.com/adshao/go-binance/v2" + + "github.com/c9s/bbgo/pkg/exchange/binance/binanceapi" + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +func toGlobalLoan(record binanceapi.MarginLoanRecord) types.MarginLoanRecord { + return types.MarginLoanRecord{ + TransactionID: uint64(record.TxId), + Asset: record.Asset, + Principle: record.Principal, + Time: types.Time(record.Timestamp), + IsolatedSymbol: record.IsolatedSymbol, + } +} + +func toGlobalRepay(record binanceapi.MarginRepayRecord) types.MarginRepayRecord { + return types.MarginRepayRecord{ + TransactionID: record.TxId, + Asset: record.Asset, + Principle: record.Principal, + Time: types.Time(record.Timestamp), + IsolatedSymbol: record.IsolatedSymbol, + } +} + +func toGlobalInterest(record binanceapi.MarginInterest) types.MarginInterest { + return types.MarginInterest{ + Asset: record.Asset, + Principle: record.Principal, + Interest: record.Interest, + InterestRate: record.InterestRate, + IsolatedSymbol: record.IsolatedSymbol, + Time: types.Time(record.InterestAccuredTime), + } + +} + +func toGlobalIsolatedUserAsset(userAsset binance.IsolatedUserAsset) types.IsolatedUserAsset { + return types.IsolatedUserAsset{ + Asset: userAsset.Asset, + Borrowed: fixedpoint.MustNewFromString(userAsset.Borrowed), + Free: fixedpoint.MustNewFromString(userAsset.Free), + Interest: fixedpoint.MustNewFromString(userAsset.Interest), + Locked: fixedpoint.MustNewFromString(userAsset.Locked), + NetAsset: fixedpoint.MustNewFromString(userAsset.NetAsset), + NetAssetOfBtc: fixedpoint.MustNewFromString(userAsset.NetAssetOfBtc), + BorrowEnabled: userAsset.BorrowEnabled, + RepayEnabled: userAsset.RepayEnabled, + TotalAsset: fixedpoint.MustNewFromString(userAsset.TotalAsset), + } +} + +func toGlobalIsolatedMarginAsset(asset binance.IsolatedMarginAsset) types.IsolatedMarginAsset { + return types.IsolatedMarginAsset{ + Symbol: asset.Symbol, + QuoteAsset: toGlobalIsolatedUserAsset(asset.QuoteAsset), + BaseAsset: toGlobalIsolatedUserAsset(asset.BaseAsset), + IsolatedCreated: asset.IsolatedCreated, + MarginLevel: fixedpoint.MustNewFromString(asset.MarginLevel), + MarginLevelStatus: asset.MarginLevelStatus, + MarginRatio: fixedpoint.MustNewFromString(asset.MarginRatio), + IndexPrice: fixedpoint.MustNewFromString(asset.IndexPrice), + LiquidatePrice: fixedpoint.MustNewFromString(asset.LiquidatePrice), + LiquidateRate: fixedpoint.MustNewFromString(asset.LiquidateRate), + TradeEnabled: false, + } +} + +func toGlobalIsolatedMarginAssets(assets []binance.IsolatedMarginAsset) (retAssets types.IsolatedMarginAssetMap) { + retMarginAssets := make(types.IsolatedMarginAssetMap) + for _, marginAsset := range assets { + retMarginAssets[marginAsset.Symbol] = toGlobalIsolatedMarginAsset(marginAsset) + } + + return retMarginAssets +} + +func toGlobalMarginUserAssets(assets []binance.UserAsset) types.MarginAssetMap { + retMarginAssets := make(types.MarginAssetMap) + for _, marginAsset := range assets { + retMarginAssets[marginAsset.Asset] = types.MarginUserAsset{ + Asset: marginAsset.Asset, + Borrowed: fixedpoint.MustNewFromString(marginAsset.Borrowed), + Free: fixedpoint.MustNewFromString(marginAsset.Free), + Interest: fixedpoint.MustNewFromString(marginAsset.Interest), + Locked: fixedpoint.MustNewFromString(marginAsset.Locked), + NetAsset: fixedpoint.MustNewFromString(marginAsset.NetAsset), + } + } + + return retMarginAssets +} + +func toGlobalMarginAccountInfo(account *binance.MarginAccount) *types.MarginAccountInfo { + return &types.MarginAccountInfo{ + BorrowEnabled: account.BorrowEnabled, + MarginLevel: fixedpoint.MustNewFromString(account.MarginLevel), + TotalAssetOfBTC: fixedpoint.MustNewFromString(account.TotalAssetOfBTC), + TotalLiabilityOfBTC: fixedpoint.MustNewFromString(account.TotalLiabilityOfBTC), + TotalNetAssetOfBTC: fixedpoint.MustNewFromString(account.TotalNetAssetOfBTC), + TradeEnabled: account.TradeEnabled, + TransferEnabled: account.TransferEnabled, + Assets: toGlobalMarginUserAssets(account.UserAssets), + } +} + +func toGlobalIsolatedMarginAccountInfo(account *binance.IsolatedMarginAccount) *types.IsolatedMarginAccountInfo { + return &types.IsolatedMarginAccountInfo{ + TotalAssetOfBTC: fixedpoint.MustNewFromString(account.TotalAssetOfBTC), + TotalLiabilityOfBTC: fixedpoint.MustNewFromString(account.TotalLiabilityOfBTC), + TotalNetAssetOfBTC: fixedpoint.MustNewFromString(account.TotalNetAssetOfBTC), + Assets: toGlobalIsolatedMarginAssets(account.Assets), + } +} diff --git a/pkg/exchange/binance/margin_history.go b/pkg/exchange/binance/margin_history.go index 5e8a0321f..59361fab2 100644 --- a/pkg/exchange/binance/margin_history.go +++ b/pkg/exchange/binance/margin_history.go @@ -4,7 +4,6 @@ import ( "context" "time" - "github.com/c9s/bbgo/pkg/exchange/binance/binanceapi" "github.com/c9s/bbgo/pkg/types" ) @@ -51,16 +50,6 @@ func (e *Exchange) QueryLoanHistory(ctx context.Context, asset string, startTime return loans, err } -func toGlobalLoan(record binanceapi.MarginLoanRecord) types.MarginLoanRecord { - return types.MarginLoanRecord{ - TransactionID: uint64(record.TxId), - Asset: record.Asset, - Principle: record.Principal, - Time: types.Time(record.Timestamp), - IsolatedSymbol: record.IsolatedSymbol, - } -} - func (e *Exchange) QueryRepayHistory(ctx context.Context, asset string, startTime, endTime *time.Time) ([]types.MarginRepayRecord, error) { req := e.client2.NewGetMarginRepayHistoryRequest() req.Asset(asset) @@ -101,16 +90,6 @@ func (e *Exchange) QueryRepayHistory(ctx context.Context, asset string, startTim return repays, err } -func toGlobalRepay(record binanceapi.MarginRepayRecord) types.MarginRepayRecord { - return types.MarginRepayRecord{ - TransactionID: record.TxId, - Asset: record.Asset, - Principle: record.Principal, - Time: types.Time(record.Timestamp), - IsolatedSymbol: record.IsolatedSymbol, - } -} - func (e *Exchange) QueryLiquidationHistory(ctx context.Context, startTime, endTime *time.Time) ([]types.MarginLiquidationRecord, error) { req := e.client2.NewGetMarginLiquidationHistoryRequest() @@ -172,14 +151,3 @@ func (e *Exchange) QueryInterestHistory(ctx context.Context, asset string, start return interests, err } -func toGlobalInterest(record binanceapi.MarginInterest) types.MarginInterest { - return types.MarginInterest{ - Asset: record.Asset, - Principle: record.Principal, - Interest: record.Interest, - InterestRate: record.InterestRate, - IsolatedSymbol: record.IsolatedSymbol, - Time: types.Time(record.InterestAccuredTime), - } - -}