diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index 4b36972f..c9b061dd 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -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 diff --git a/exchanges/request/request.go b/exchanges/request/request.go index 5d707f66..da07ca53 100644 --- a/exchanges/request/request.go +++ b/exchanges/request/request.go @@ -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 } }