FTX: order cancellation improvement (#727)

* exchanges/request: Requester.doRequest() now always parses returned response body into JSON even in the case of an artificial error after the request itself

* exchanges/ftx: consider order cancellation successful under two new conditions, reported by the exchange: (1) order is already closed or (2) order is already queued for cancellation

* exchanges/ftx: fix a typo in a comment

* exchanges/request: keep the same behavior of doRequest() when there is an unmarshaling error

* exchanges/ftx: FTX.DeleteOrderByClientID now also reports no errors when requesting the cancellation of orders that are already canceled on the exchange

* exchanges/ftx: order deletion methods are now unified

* exchanges/ftx: DeleteOrder* methods now check if the given ID is not empty
This commit is contained in:
Yordan Miladinov
2021-07-31 08:13:52 +03:00
committed by GitHub
parent bf5a24b3d7
commit 3b1fe81d8b
2 changed files with 29 additions and 33 deletions

View File

@@ -129,6 +129,7 @@ const (
)
var (
errInvalidOrderID = errors.New("invalid order ID")
errStartTimeCannotBeAfterEndTime = errors.New("start timestamp cannot be after end timestamp")
errSubaccountNameMustBeSpecified = errors.New("a subaccount name must be specified")
errSubaccountUpdateNameInvalid = errors.New("invalid subaccount old/new name")
@@ -776,51 +777,43 @@ func (f *FTX) GetOrderStatusByClientID(clientOrderID string) (OrderData, error)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOrderStatusByClientID+clientOrderID, nil, &resp)
}
// DeleteOrder deletes an order
func (f *FTX) DeleteOrder(orderID string) (string, error) {
func (f *FTX) deleteOrderByPath(path string) (string, error) {
resp := struct {
Result string `json:"result"`
Success bool `json:"success"`
Error string `json:"error"`
}{}
if err := f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, deleteOrder+orderID, nil, &resp); err != nil {
return "", err
err := f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, path, nil, &resp)
// If there is an error reported, but the resp struct reports one of a very few
// specific error causes, we still consider this a successful cancellation.
if err != nil && !resp.Success && (resp.Error == "Order already closed" || resp.Error == "Order already queued for cancellation") {
return resp.Error, nil
}
if !resp.Success {
return resp.Result, errors.New("delete order request by ID unsuccessful")
return resp.Result, err
}
// DeleteOrder deletes an order
func (f *FTX) DeleteOrder(orderID string) (string, error) {
if orderID == "" {
return "", errInvalidOrderID
}
return resp.Result, nil
return f.deleteOrderByPath(deleteOrder + orderID)
}
// DeleteOrderByClientID deletes an order
func (f *FTX) DeleteOrderByClientID(clientID string) (string, error) {
resp := struct {
Result string `json:"result"`
Success bool `json:"success"`
}{}
if err := f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, deleteOrderByClientID+clientID, nil, &resp); err != nil {
return "", err
if clientID == "" {
return "", errInvalidOrderID
}
if !resp.Success {
return resp.Result, errors.New("delete order request by client ID unsuccessful")
}
return resp.Result, nil
return f.deleteOrderByPath(deleteOrderByClientID + clientID)
}
// DeleteTriggerOrder deletes an order
func (f *FTX) DeleteTriggerOrder(orderID string) (string, error) {
resp := struct {
Result string `json:"result"`
Success bool `json:"success"`
}{}
if err := f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, cancelTriggerOrder+orderID, nil, &resp); err != nil {
return "", err
if orderID == "" {
return "", errInvalidOrderID
}
if !resp.Success {
return resp.Result, errors.New("delete trigger order request unsuccessful")
}
return resp.Result, nil
return f.deleteOrderByPath(cancelTriggerOrder + orderID)
}
// GetFills gets fills' data

View File

@@ -197,6 +197,12 @@ func (r *Requester) doRequest(req *http.Request, p *Item) error {
if err != nil {
return err
}
// Even in the case of an erroneous condition below, yield the parsed
// response to caller.
var unmarshallError error
if p.Result != nil {
unmarshallError = json.Unmarshal(contents, p.Result)
}
if p.HTTPRecording {
// This dumps http responses for future mocking implementations
@@ -242,10 +248,7 @@ func (r *Requester) doRequest(req *http.Request, p *Item) error {
string(contents))
}
}
if p.Result != nil {
return json.Unmarshal(contents, p.Result)
}
return nil
return unmarshallError
}
}