mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
exchanges/binance/coinm: fix order submission (#819)
* exchanges/binance: proper arguments order when calling Binance.FuturesNewOrder * exchanges/binance: adapt FuturesOrderPlaceData (+ unmarshaling) to latest coin/delivery futures API * exchanges/binance: introduce futuresNewOrderRequest and use it to pass order parameters for coin margined futures * exchanges/binance: test json unmarshaling of FuturesOrderPlaceData * exchanges/binance: reorder fields as per docs, include missing fields (also in tests) * exchanges/binance/coinm: use constants instead of hard coded strings (also fixes linting) * exchanges/binance/coinm: pass futuresNewOrderRequest by reference * exchanges/binance/coinm: expose FuturesNewOrderRequest * exchanges/binance/coinm: do not explicitly assign default values fields of FuturesNewOrderRequest * exchanges/binance/coinm: document FuturesNewOrderRequest * exchanges/binance/coinm: expose all fields of FuturesNewOrderRequest * exchanges/binance/coinm: expose fields of FuturesNewOrderRequest * exchange/binance/coin: order submission: add support for priceProtect Co-authored-by: Yordan Miladinov <jordanmiladinov@gmail.bg>
This commit is contained in:
@@ -65,6 +65,14 @@ const (
|
||||
cfuturesNotionalBracket = "/dapi/v1/leverageBracket"
|
||||
cfuturesUsersForceOrders = "/dapi/v1/forceOrders"
|
||||
cfuturesADLQuantile = "/dapi/v1/adlQuantile"
|
||||
|
||||
cfuturesLimit = "LIMIT"
|
||||
cfuturesMarket = "MARKET"
|
||||
cfuturesStop = "STOP"
|
||||
cfuturesTakeProfit = "TAKE_PROFIT"
|
||||
cfuturesStopMarket = "STOP_MARKET"
|
||||
cfuturesTakeProfitMarket = "TAKE_PROFIT_MARKET"
|
||||
cfuturesTrailingStopMarket = "TRAILING_STOP_MARKET"
|
||||
)
|
||||
|
||||
// FuturesExchangeInfo stores CoinMarginedFutures, data
|
||||
@@ -973,60 +981,64 @@ func (b *Binance) GetFuturesBasisData(ctx context.Context, pair, contractType, p
|
||||
}
|
||||
|
||||
// FuturesNewOrder sends a new futures order to the exchange
|
||||
func (b *Binance) FuturesNewOrder(ctx context.Context, symbol currency.Pair, side, positionSide, orderType, timeInForce,
|
||||
newClientOrderID, closePosition, workingType, newOrderRespType string,
|
||||
quantity, price, stopPrice, activationPrice, callbackRate float64, reduceOnly bool) (FuturesOrderPlaceData, error) {
|
||||
func (b *Binance) FuturesNewOrder(ctx context.Context, x *FuturesNewOrderRequest) (
|
||||
FuturesOrderPlaceData,
|
||||
error,
|
||||
) {
|
||||
var resp FuturesOrderPlaceData
|
||||
params := url.Values{}
|
||||
symbolValue, err := b.FormatSymbol(symbol, asset.CoinMarginedFutures)
|
||||
symbolValue, err := b.FormatSymbol(x.Symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
params.Set("symbol", symbolValue)
|
||||
params.Set("side", side)
|
||||
if positionSide != "" {
|
||||
if !common.StringDataCompare(validPositionSide, positionSide) {
|
||||
params.Set("side", x.Side)
|
||||
if x.PositionSide != "" {
|
||||
if !common.StringDataCompare(validPositionSide, x.PositionSide) {
|
||||
return resp, errors.New("invalid positionSide")
|
||||
}
|
||||
params.Set("positionSide", positionSide)
|
||||
params.Set("positionSide", x.PositionSide)
|
||||
}
|
||||
params.Set("type", orderType)
|
||||
params.Set("timeInForce", timeInForce)
|
||||
if reduceOnly {
|
||||
params.Set("type", x.OrderType)
|
||||
params.Set("timeInForce", x.TimeInForce)
|
||||
if x.ReduceOnly {
|
||||
params.Set("reduceOnly", "true")
|
||||
}
|
||||
if newClientOrderID != "" {
|
||||
params.Set("newClientOrderID", newClientOrderID)
|
||||
if x.NewClientOrderID != "" {
|
||||
params.Set("newClientOrderID", x.NewClientOrderID)
|
||||
}
|
||||
if closePosition != "" {
|
||||
params.Set("closePosition", closePosition)
|
||||
if x.ClosePosition != "" {
|
||||
params.Set("closePosition", x.ClosePosition)
|
||||
}
|
||||
if workingType != "" {
|
||||
if !common.StringDataCompare(validWorkingType, workingType) {
|
||||
if x.WorkingType != "" {
|
||||
if !common.StringDataCompare(validWorkingType, x.WorkingType) {
|
||||
return resp, errors.New("invalid workingType")
|
||||
}
|
||||
params.Set("workingType", workingType)
|
||||
params.Set("workingType", x.WorkingType)
|
||||
}
|
||||
if newOrderRespType != "" {
|
||||
if !common.StringDataCompare(validNewOrderRespType, newOrderRespType) {
|
||||
if x.NewOrderRespType != "" {
|
||||
if !common.StringDataCompare(validNewOrderRespType, x.NewOrderRespType) {
|
||||
return resp, errors.New("invalid newOrderRespType")
|
||||
}
|
||||
params.Set("newOrderRespType", newOrderRespType)
|
||||
params.Set("newOrderRespType", x.NewOrderRespType)
|
||||
}
|
||||
if quantity != 0 {
|
||||
params.Set("quantity", strconv.FormatFloat(quantity, 'f', -1, 64))
|
||||
if x.Quantity != 0 {
|
||||
params.Set("quantity", strconv.FormatFloat(x.Quantity, 'f', -1, 64))
|
||||
}
|
||||
if price != 0 {
|
||||
params.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
if x.Price != 0 {
|
||||
params.Set("price", strconv.FormatFloat(x.Price, 'f', -1, 64))
|
||||
}
|
||||
if stopPrice != 0 {
|
||||
params.Set("stopPrice", strconv.FormatFloat(stopPrice, 'f', -1, 64))
|
||||
if x.StopPrice != 0 {
|
||||
params.Set("stopPrice", strconv.FormatFloat(x.StopPrice, 'f', -1, 64))
|
||||
}
|
||||
if activationPrice != 0 {
|
||||
params.Set("activationPrice", strconv.FormatFloat(activationPrice, 'f', -1, 64))
|
||||
if x.ActivationPrice != 0 {
|
||||
params.Set("activationPrice", strconv.FormatFloat(x.ActivationPrice, 'f', -1, 64))
|
||||
}
|
||||
if callbackRate != 0 {
|
||||
params.Set("callbackRate", strconv.FormatFloat(callbackRate, 'f', -1, 64))
|
||||
if x.CallbackRate != 0 {
|
||||
params.Set("callbackRate", strconv.FormatFloat(x.CallbackRate, 'f', -1, 64))
|
||||
}
|
||||
if x.PriceProtect {
|
||||
params.Set("priceProtect", "TRUE")
|
||||
}
|
||||
return resp, b.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, cfuturesOrder, params, cFuturesOrdersDefaultRate, &resp)
|
||||
}
|
||||
|
||||
@@ -903,7 +903,17 @@ func TestFuturesNewOrder(t *testing.T) {
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false")
|
||||
}
|
||||
_, err := b.FuturesNewOrder(context.Background(), currency.NewPairWithDelimiter("BTCUSD", "PERP", "_"), "BUY", "", "LIMIT", "GTC", "", "", "", "", 1, 1, 0, 0, 0, false)
|
||||
_, err := b.FuturesNewOrder(
|
||||
context.Background(),
|
||||
&FuturesNewOrderRequest{
|
||||
Symbol: currency.NewPairWithDelimiter("BTCUSD", "PERP", "_"),
|
||||
Side: "BUY",
|
||||
OrderType: "LIMIT",
|
||||
TimeInForce: "GTC",
|
||||
Quantity: 1,
|
||||
Price: 1,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -936,27 +936,35 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi
|
||||
var oType string
|
||||
switch s.Type {
|
||||
case order.Limit:
|
||||
oType = "LIMIT"
|
||||
oType = cfuturesLimit
|
||||
case order.Market:
|
||||
oType = "MARKET"
|
||||
oType = cfuturesMarket
|
||||
case order.Stop:
|
||||
oType = "STOP"
|
||||
oType = cfuturesStop
|
||||
case order.TakeProfit:
|
||||
oType = "TAKE_PROFIT"
|
||||
oType = cfuturesTakeProfit
|
||||
case order.StopMarket:
|
||||
oType = "STOP_MARKET"
|
||||
oType = cfuturesStopMarket
|
||||
case order.TakeProfitMarket:
|
||||
oType = "TAKE_PROFIT_MARKET"
|
||||
oType = cfuturesTakeProfitMarket
|
||||
case order.TrailingStop:
|
||||
oType = "TRAILING_STOP_MARKET"
|
||||
oType = cfuturesTrailingStopMarket
|
||||
default:
|
||||
return submitOrderResponse, errors.New("invalid type, check api docs for updates")
|
||||
}
|
||||
o, err := b.FuturesNewOrder(ctx,
|
||||
s.Pair, reqSide,
|
||||
"", oType, "GTC", "",
|
||||
s.ClientOrderID, "", "",
|
||||
s.Amount, s.Price, 0, 0, 0, s.ReduceOnly)
|
||||
o, err := b.FuturesNewOrder(
|
||||
ctx,
|
||||
&FuturesNewOrderRequest{
|
||||
Symbol: s.Pair,
|
||||
Side: reqSide,
|
||||
OrderType: oType,
|
||||
TimeInForce: "GTC",
|
||||
NewClientOrderID: s.ClientOrderID,
|
||||
Quantity: s.Amount,
|
||||
Price: s.Price,
|
||||
ReduceOnly: s.ReduceOnly,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package binance
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
)
|
||||
|
||||
@@ -204,13 +205,34 @@ type BatchCancelOrderData struct {
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// FuturesNewOrderRequest stores all the data needed to submit a
|
||||
// delivery/coin-margined-futures order.
|
||||
type FuturesNewOrderRequest struct {
|
||||
Symbol currency.Pair
|
||||
Side string
|
||||
PositionSide string
|
||||
OrderType string
|
||||
TimeInForce string
|
||||
NewClientOrderID string
|
||||
ClosePosition string
|
||||
WorkingType string
|
||||
NewOrderRespType string
|
||||
Quantity float64
|
||||
Price float64
|
||||
StopPrice float64
|
||||
ActivationPrice float64
|
||||
CallbackRate float64
|
||||
ReduceOnly bool
|
||||
PriceProtect bool
|
||||
}
|
||||
|
||||
// FuturesOrderPlaceData stores futures order data
|
||||
type FuturesOrderPlaceData struct {
|
||||
ClientOrderID string `json:"clientOrderID"`
|
||||
ClientOrderID string `json:"clientOrderId"`
|
||||
CumQty float64 `json:"cumQty,string"`
|
||||
CumBase float64 `json:"cumBase,string"`
|
||||
ExecuteQty float64 `json:"executeQty,string"`
|
||||
OrderID int64 `json:"orderID,string"`
|
||||
ExecuteQty float64 `json:"executedQty,string"`
|
||||
OrderID int64 `json:"orderId"`
|
||||
AvgPrice float64 `json:"avgPrice,string"`
|
||||
OrigQty float64 `json:"origQty,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
@@ -218,7 +240,7 @@ type FuturesOrderPlaceData struct {
|
||||
Side string `json:"side"`
|
||||
PositionSide string `json:"positionSide"`
|
||||
Status string `json:"status"`
|
||||
StopPrice int64 `json:"stopPrice"`
|
||||
StopPrice float64 `json:"stopPrice,string"`
|
||||
ClosePosition bool `json:"closePosition"`
|
||||
Symbol string `json:"symbol"`
|
||||
Pair string `json:"pair"`
|
||||
|
||||
69
exchanges/binance/cfutures_types_test.go
Normal file
69
exchanges/binance/cfutures_types_test.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package binance
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFuturesNewOrderRequest_Unmarshal(t *testing.T) {
|
||||
const inp = `
|
||||
{
|
||||
"orderId": 18662274680,
|
||||
"symbol": "ETHUSD_PERP",
|
||||
"pair": "ETHUSD",
|
||||
"status": "NEW",
|
||||
"clientOrderId": "customID",
|
||||
"price": "4096",
|
||||
"avgPrice": "2.00",
|
||||
"origQty": "8",
|
||||
"executedQty": "4",
|
||||
"cumQty": "32",
|
||||
"cumBase": "16",
|
||||
"timeInForce": "GTC",
|
||||
"type": "LIMIT",
|
||||
"reduceOnly": true,
|
||||
"closePosition": true,
|
||||
"side": "BUY",
|
||||
"positionSide": "BOTH",
|
||||
"stopPrice": "2048",
|
||||
"workingType": "CONTRACT_PRICE",
|
||||
"priceProtect": true,
|
||||
"origType": "MARKET",
|
||||
"updateTime": 1635931801320,
|
||||
"activatePrice": "64",
|
||||
"priceRate": "32"
|
||||
}
|
||||
`
|
||||
|
||||
var x FuturesOrderPlaceData
|
||||
|
||||
if err := json.Unmarshal([]byte(inp), &x); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if x.OrderID != 18662274680 ||
|
||||
x.Symbol != "ETHUSD_PERP" ||
|
||||
x.Pair != "ETHUSD" ||
|
||||
x.Status != "NEW" ||
|
||||
x.ClientOrderID != "customID" ||
|
||||
x.Price != 4096 ||
|
||||
x.AvgPrice != 2 ||
|
||||
x.OrigQty != 8 ||
|
||||
x.ExecuteQty != 4 ||
|
||||
x.CumQty != 32 ||
|
||||
x.CumBase != 16 ||
|
||||
x.TimeInForce != "GTC" ||
|
||||
x.OrderType != cfuturesLimit ||
|
||||
!x.ReduceOnly ||
|
||||
!x.ClosePosition ||
|
||||
x.StopPrice != 2048 ||
|
||||
x.WorkingType != "CONTRACT_PRICE" ||
|
||||
!x.PriceProtect ||
|
||||
x.OrigType != cfuturesMarket ||
|
||||
x.UpdateTime != 1635931801320 ||
|
||||
x.ActivatePrice != 64 ||
|
||||
x.PriceRate != 32 {
|
||||
// If any of these values isn't set as expected, mark test as failed.
|
||||
t.Errorf("unmarshaling failed: %v", x)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user