Skip to content

Commit

Permalink
gateio: fix spot/futures order issues (thrasher-corp#1524)
Browse files Browse the repository at this point in the history
* gateio: fix spot deployment issue

* fix status bug add test

* to actual return type

* fix linter

* ch type

* glorious: nits

* rm space

* glorious: nits

---------

Co-authored-by: Ryan O'Hara-Reid <[email protected]>
  • Loading branch information
shazbert and Ryan O'Hara-Reid authored Apr 29, 2024
1 parent 44d50a3 commit 694f2f7
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 20 deletions.
6 changes: 1 addition & 5 deletions exchanges/gateio/gateio.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ var (
errTooManyOrderRequest = errors.New("too many order creation request")
errInvalidTimeout = errors.New("invalid timeout, should be in seconds At least 5 seconds, 0 means cancel the countdown")
errNoTickerData = errors.New("no ticker data available")
errOnlyLimitOrderType = errors.New("only order type 'limit' is allowed")
errNilArgument = errors.New("null argument")
errInvalidTimezone = errors.New("invalid timezone")
errMultipleOrders = errors.New("multiple orders passed")
Expand Down Expand Up @@ -632,9 +631,6 @@ func (g *Gateio) PlaceSpotOrder(ctx context.Context, arg *CreateOrderRequestData
if arg.CurrencyPair.IsInvalid() {
return nil, currency.ErrCurrencyPairEmpty
}
if arg.Type != "limit" {
return nil, errOnlyLimitOrderType
}
arg.Side = strings.ToLower(arg.Side)
if arg.Side != "buy" && arg.Side != "sell" {
return nil, errInvalidOrderSide
Expand All @@ -647,7 +643,7 @@ func (g *Gateio) PlaceSpotOrder(ctx context.Context, arg *CreateOrderRequestData
if arg.Amount <= 0 {
return nil, errInvalidAmount
}
if arg.Price <= 0 {
if arg.Price < 0 {
return nil, errInvalidPrice
}
var response *SpotOrder
Expand Down
31 changes: 31 additions & 0 deletions exchanges/gateio/gateio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3550,3 +3550,34 @@ func TestGetTimeInForce(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "fok", ret)
}

func TestProcessFuturesOrdersPushData(t *testing.T) {
t.Parallel()
testCases := []struct {
incoming string
status order.Status
}{
{`{"channel":"futures.orders","event":"update","time":1541505434,"time_ms":1541505434123,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"open","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}`, order.Open},
{`{"channel":"futures.orders","event":"update","time":1541505434,"time_ms":1541505434123,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"filled","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"finished","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}`, order.Filled},
{`{"channel":"futures.orders","event":"update","time":1541505434,"time_ms":1541505434123,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"cancelled","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"finished","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}`, order.Cancelled},
{`{"channel":"futures.orders","event":"update","time":1541505434,"time_ms":1541505434123,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"liquidated","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"finished","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}`, order.Liquidated},
{`{"channel":"futures.orders","event":"update","time":1541505434,"time_ms":1541505434123,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"ioc","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"finished","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}`, order.Cancelled},
{`{"channel":"futures.orders","event":"update","time":1541505434,"time_ms":1541505434123,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"auto_deleveraged","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"finished","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}`, order.AutoDeleverage},
{`{"channel":"futures.orders","event":"update","time":1541505434,"time_ms":1541505434123,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"reduce_only","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"finished","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}`, order.Cancelled},
{`{"channel":"futures.orders","event":"update","time":1541505434,"time_ms":1541505434123,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"position_closed","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"finished","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}`, order.Closed},
{`{"channel":"futures.orders","event":"update","time":1541505434,"time_ms":1541505434123,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"stp","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"finished","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}`, order.STP},
}

for _, tc := range testCases {
tc := tc
t.Run("", func(t *testing.T) {
t.Parallel()
processed, err := g.processFuturesOrdersPushData([]byte(tc.incoming), asset.Futures)
require.NoError(t, err)
require.NotNil(t, processed)
for i := range processed {
assert.Equal(t, tc.status.String(), processed[i].Status.String())
}
})
}
}
8 changes: 5 additions & 3 deletions exchanges/gateio/gateio_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -996,9 +996,6 @@ func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Submi
s.Pair = s.Pair.Upper()
switch s.AssetType {
case asset.Spot, asset.Margin, asset.CrossMargin:
if s.Type != order.Limit {
return nil, errOnlyLimitOrderType
}
switch {
case s.Side.IsLong():
s.Side = order.Buy
Expand All @@ -1007,6 +1004,10 @@ func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Submi
default:
return nil, errInvalidOrderSide
}
timeInForce, err := getTimeInForce(s)
if err != nil {
return nil, err
}
sOrder, err := g.PlaceSpotOrder(ctx, &CreateOrderRequestData{
Side: s.Side.Lower(),
Type: s.Type.Lower(),
Expand All @@ -1015,6 +1016,7 @@ func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Submi
Price: types.Number(s.Price),
CurrencyPair: s.Pair,
Text: s.ClientOrderID,
TimeInForce: timeInForce,
})
if err != nil {
return nil, err
Expand Down
36 changes: 25 additions & 11 deletions exchanges/gateio/gateio_ws_futures.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,13 @@ func (g *Gateio) wsHandleFuturesData(respRaw []byte, assetType asset.Item) error
case futuresCandlesticksChannel:
return g.processFuturesCandlesticks(respRaw, assetType)
case futuresOrdersChannel:
return g.processFuturesOrdersPushData(respRaw, assetType)
var processed []order.Detail
processed, err = g.processFuturesOrdersPushData(respRaw, assetType)
if err != nil {
return err
}
g.Websocket.DataHandler <- processed
return nil
case futuresUserTradesChannel:
return g.procesFuturesUserTrades(respRaw, assetType)
case futuresLiquidatesChannel:
Expand Down Expand Up @@ -642,7 +648,7 @@ func (g *Gateio) processFuturesOrderbookSnapshot(event string, incoming []byte,
return nil
}

func (g *Gateio) processFuturesOrdersPushData(data []byte, assetType asset.Item) error {
func (g *Gateio) processFuturesOrdersPushData(data []byte, assetType asset.Item) ([]order.Detail, error) {
resp := struct {
Time int64 `json:"time"`
Channel string `json:"channel"`
Expand All @@ -651,19 +657,28 @@ func (g *Gateio) processFuturesOrdersPushData(data []byte, assetType asset.Item)
}{}
err := json.Unmarshal(data, &resp)
if err != nil {
return err
return nil, err
}
orderDetails := make([]order.Detail, len(resp.Result))
for x := range resp.Result {
status, err := order.StringToOrderStatus(func() string {
if resp.Result[x].Status == "finished" {
return "cancelled"
var status order.Status
if resp.Result[x].Status == "finished" {
if resp.Result[x].FinishAs == "ioc" || resp.Result[x].FinishAs == "reduce_only" {
status = order.Cancelled
} else {
status, err = order.StringToOrderStatus(resp.Result[x].FinishAs)
}
return resp.Result[x].Status
}())
} else {
status, err = order.StringToOrderStatus(resp.Result[x].Status)
}
if err != nil {
return err
g.Websocket.DataHandler <- order.ClassificationError{
Exchange: g.Name,
OrderID: strconv.FormatInt(resp.Result[x].ID, 10),
Err: err,
}
}

orderDetails[x] = order.Detail{
Amount: resp.Result[x].Size,
Exchange: g.Name,
Expand All @@ -679,8 +694,7 @@ func (g *Gateio) processFuturesOrdersPushData(data []byte, assetType asset.Item)
CloseTime: resp.Result[x].FinishTimeMs.Time(),
}
}
g.Websocket.DataHandler <- orderDetails
return nil
return orderDetails, nil
}

func (g *Gateio) procesFuturesUserTrades(data []byte, assetType asset.Item) error {
Expand Down
1 change: 1 addition & 0 deletions exchanges/order/order_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ const (
Pending
Cancelling
Liquidated
STP
)

// Type enforces a standard for order types across the code base
Expand Down
12 changes: 11 additions & 1 deletion exchanges/order/orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,10 @@ func (s Status) String() string {
return "PENDING"
case Cancelling:
return "CANCELLING"
case Liquidated:
return "LIQUIDATED"
case STP:
return "SELF_TRADE_PREVENTION"
default:
return "UNKNOWN"
}
Expand Down Expand Up @@ -1141,7 +1145,7 @@ func StringToOrderStatus(status string) (Status, error) {
return PartiallyFilledCancelled, nil
case Open.String():
return Open, nil
case Closed.String():
case Closed.String(), "POSITION_CLOSED":
return Closed, nil
case Cancelled.String(), "CANCELED", "ORDER_CANCELLED":
return Cancelled, nil
Expand All @@ -1161,6 +1165,12 @@ func StringToOrderStatus(status string) (Status, error) {
return MarketUnavailable, nil
case Cancelling.String():
return Cancelling, nil
case Liquidated.String():
return Liquidated, nil
case AutoDeleverage.String(), "AUTO_DELEVERAGED":
return AutoDeleverage, nil
case STP.String(), "STP":
return STP, nil
default:
return UnknownStatus, fmt.Errorf("'%s' %w", status, errUnrecognisedOrderStatus)
}
Expand Down

0 comments on commit 694f2f7

Please sign in to comment.