Binance: Update NewOrder API (#507)

Including new fields for request/response and allowing test orders.

Signed-off-by: David Ackroyd <daveo.ackroyd@gmail.com>
This commit is contained in:
David Ackroyd
2020-05-18 15:42:50 +10:00
committed by GitHub
parent 0adf39de35
commit 802d265d56
5 changed files with 117 additions and 20 deletions

View File

@@ -321,14 +321,35 @@ func (b *Binance) GetBestPrice(symbol string) (BestPrice, error) {
// NewOrder sends a new order to Binance
func (b *Binance) NewOrder(o *NewOrderRequest) (NewOrderResponse, error) {
var resp NewOrderResponse
if err := b.newOrder(newOrder, o, &resp); err != nil {
return resp, err
}
path := b.API.Endpoints.URL + newOrder
if resp.Code != 0 {
return resp, errors.New(resp.Msg)
}
return resp, nil
}
// NewOrderTest sends a new test order to Binance
func (b *Binance) NewOrderTest(o *NewOrderRequest) error {
var resp NewOrderResponse
return b.newOrder(newOrderTest, o, &resp)
}
func (b *Binance) newOrder(api string, o *NewOrderRequest, resp *NewOrderResponse) error {
path := b.API.Endpoints.URL + api
params := url.Values{}
params.Set("symbol", o.Symbol)
params.Set("side", o.Side)
params.Set("type", string(o.TradeType))
params.Set("quantity", strconv.FormatFloat(o.Quantity, 'f', -1, 64))
if o.QuoteOrderQty > 0 {
params.Set("quoteOrderQty", strconv.FormatFloat(o.QuoteOrderQty, 'f', -1, 64))
} else {
params.Set("quantity", strconv.FormatFloat(o.Quantity, 'f', -1, 64))
}
if o.TradeType == BinanceRequestParamsOrderLimit {
params.Set("price", strconv.FormatFloat(o.Price, 'f', -1, 64))
}
@@ -352,14 +373,7 @@ func (b *Binance) NewOrder(o *NewOrderRequest) (NewOrderResponse, error) {
params.Set("newOrderRespType", o.NewOrderRespType)
}
if err := b.SendAuthHTTPRequest(http.MethodPost, path, params, limitOrder, &resp); err != nil {
return resp, err
}
if resp.Code != 0 {
return resp, errors.New(resp.Msg)
}
return resp, nil
return b.SendAuthHTTPRequest(http.MethodPost, path, params, limitOrder, resp)
}
// CancelExistingOrder sends a cancel order to Binance

View File

@@ -353,6 +353,47 @@ func TestGetOrderHistory(t *testing.T) {
}
}
func TestNewOrderTest(t *testing.T) {
t.Parallel()
req := &NewOrderRequest{
Symbol: "LTCBTC",
Side: order.Buy.String(),
TradeType: BinanceRequestParamsOrderLimit,
Price: 0.0025,
Quantity: 100000,
TimeInForce: BinanceRequestParamsTimeGTC,
}
err := b.NewOrderTest(req)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("NewOrderTest() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("NewOrderTest() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Mock NewOrderTest() error", err)
}
req = &NewOrderRequest{
Symbol: "LTCBTC",
Side: order.Sell.String(),
TradeType: BinanceRequestParamsOrderMarket,
Price: 0.0045,
QuoteOrderQty: 10,
}
err = b.NewOrderTest(req)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("NewOrderTest() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("NewOrderTest() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Mock NewOrderTest() error", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// -----------------------------------------------------------------------------------------------------------------------------

View File

@@ -281,8 +281,10 @@ type NewOrderRequest struct {
// TimeInForce specifies how long the order remains in effect.
// Examples are (Good Till Cancel (GTC), Immediate or Cancel (IOC) and Fill Or Kill (FOK))
TimeInForce RequestParamsTimeForceType
// Quantity
Quantity float64
// Quantity is the total base qty spent or received in an order.
Quantity float64
// QuoteOrderQty is the total quote qty spent or received in a MARKET order.
QuoteOrderQty float64
Price float64
NewClientOrderID string
StopPrice float64 // Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders.
@@ -301,11 +303,13 @@ type NewOrderResponse struct {
Price float64 `json:"price,string"`
OrigQty float64 `json:"origQty,string"`
ExecutedQty float64 `json:"executedQty,string"`
Status string `json:"status"`
TimeInForce string `json:"timeInForce"`
Type string `json:"type"`
Side string `json:"side"`
Fills []struct {
// The cumulative amount of the quote that has been spent (with a BUY order) or received (with a SELL order).
CumulativeQuoteQty float64 `json:"cummulativeQuoteQty,string"`
Status string `json:"status"`
TimeInForce string `json:"timeInForce"`
Type string `json:"type"`
Side string `json:"side"`
Fills []struct {
Price float64 `json:"price,string"`
Qty float64 `json:"qty,string"`
Commission float64 `json:"commission,string"`

View File

@@ -148,9 +148,6 @@ func HTTPRecord(res *http.Response, service string, respContents []byte) error {
case http.MethodPost:
for i := range mockResponses {
cType, ok := mockResponses[i].Headers[contentType]
if !ok {
return errors.New("cannot find content type within mock responses")
}
jCType := strings.Join(cType, "")
var found bool
@@ -190,7 +187,24 @@ func HTTPRecord(res *http.Response, service string, respContents []byte) error {
mockResponses = append(mockResponses[:i], mockResponses[i+1:]...)
found = true
}
case "":
if !ok {
// Assume query params are used
mockQuery, urlErr := url.ParseQuery(mockResponses[i].QueryString)
if urlErr != nil {
return urlErr
}
if MatchURLVals(mockQuery, res.Request.URL.Query()) {
// if found will delete instance and overwrite with new data
mockResponses = append(mockResponses[:i], mockResponses[i+1:]...)
found = true
}
break
}
fallthrough
default:
return fmt.Errorf("unhandled content type %s", jCType)
}

View File

@@ -49951,6 +49951,30 @@
}
]
},
"/api/v3/order/test": {
"POST": [
{
"data": null,
"queryString": "price=0.0025\u0026quantity=100000\u0026recvWindow=5000\u0026side=BUY\u0026signature=1ddcd1b138325e4b72f045d56e719dc83963648001be999ca23ec88b94b1d900\u0026symbol=LTCBTC\u0026timeInForce=GTC\u0026timestamp=1589766515000\u0026type=LIMIT",
"bodyParams": "",
"headers": {
"X-Mbx-Apikey": [
""
]
}
},
{
"data": null,
"queryString": "quoteOrderQty=10\u0026recvWindow=5000\u0026side=SELL\u0026signature=f7d34dc6a9d6181adc3cd90f269a78d978610818b4d0b454ec95777194212aae\u0026symbol=LTCBTC\u0026timestamp=1589766516000\u0026type=MARKET",
"bodyParams": "",
"headers": {
"X-Mbx-Apikey": [
""
]
}
}
]
},
"/api/v3/ticker/24hr": {
"GET": [
{