Skip to content

Commit

Permalink
pkg/exchange: generate place order request by requestgen
Browse files Browse the repository at this point in the history
  • Loading branch information
bailantaotao committed Jan 10, 2024
1 parent 08fa1e9 commit fcd897b
Show file tree
Hide file tree
Showing 6 changed files with 421 additions and 258 deletions.
72 changes: 39 additions & 33 deletions pkg/exchange/okex/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var (
queryTickerLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 10)
queryTickersLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 10)
queryAccountLimiter = rate.NewLimiter(rate.Every(200*time.Millisecond), 5)
placeOrderLimiter = rate.NewLimiter(rate.Every(30*time.Millisecond), 30)
)

const ID = "okex"
Expand Down Expand Up @@ -198,63 +199,68 @@ func (e *Exchange) QueryAccountBalances(ctx context.Context) (types.BalanceMap,
func (e *Exchange) SubmitOrder(ctx context.Context, order types.SubmitOrder) (*types.Order, error) {
orderReq := e.client.NewPlaceOrderRequest()

orderType, err := toLocalOrderType(order.Type)
if err != nil {
return nil, err
}

orderReq.InstrumentID(toLocalSymbol(order.Symbol))
orderReq.Side(toLocalSideType(order.Side))

if order.Market.Symbol != "" {
orderReq.Quantity(order.Market.FormatQuantity(order.Quantity))
} else {
// TODO report error
orderReq.Quantity(order.Quantity.FormatString(8))
}
orderReq.Size(order.Market.FormatQuantity(order.Quantity))

// set price field for limit orders
switch order.Type {
case types.OrderTypeStopLimit, types.OrderTypeLimit:
if order.Market.Symbol != "" {
orderReq.Price(order.Market.FormatPrice(order.Price))
orderReq.Price(order.Market.FormatPrice(order.Price))
case types.OrderTypeMarket:
// Because our order.Quantity unit is base coin, so we indicate the target currency to Base.
if order.Side == types.SideTypeBuy {
orderReq.Size(order.Market.FormatQuantity(order.Quantity))
orderReq.TargetCurrency(okexapi.TargetCurrencyBase)
} else {
// TODO report error
orderReq.Price(order.Price.FormatString(8))
orderReq.Size(order.Market.FormatQuantity(order.Quantity))
orderReq.TargetCurrency(okexapi.TargetCurrencyQuote)
}
}

orderType, err := toLocalOrderType(order.Type)
if err != nil {
return nil, err
}

switch order.TimeInForce {
case "FOK":
case types.TimeInForceFOK:
orderReq.OrderType(okexapi.OrderTypeFOK)
case "IOC":
case types.TimeInForceIOC:
orderReq.OrderType(okexapi.OrderTypeIOC)
default:
orderReq.OrderType(orderType)
}

orderHead, err := orderReq.Do(ctx)
if err := placeOrderLimiter.Wait(ctx); err != nil {
return nil, fmt.Errorf("place order rate limiter wait error: %w", err)
}

_, err = strconv.ParseInt(order.ClientOrderID, 10, 64)
if err != nil {
return nil, err
return nil, fmt.Errorf("client order id should be numberic: %s", order.ClientOrderID)
}
orderReq.ClientOrderID(order.ClientOrderID)

orderID, err := strconv.ParseInt(orderHead.OrderID, 10, 64)
orders, err := orderReq.Do(ctx)
if err != nil {
return nil, err
}

return &types.Order{
SubmitOrder: order,
Exchange: types.ExchangeOKEx,
OrderID: uint64(orderID),
Status: types.OrderStatusNew,
ExecutedQuantity: fixedpoint.Zero,
IsWorking: true,
CreationTime: types.Time(time.Now()),
UpdateTime: types.Time(time.Now()),
IsMargin: false,
IsIsolated: false,
}, nil
if len(orders) != 1 {
return nil, fmt.Errorf("unexpected length of order response: %v", orders)
}

orderRes, err := e.QueryOrder(ctx, types.OrderQuery{
Symbol: order.Symbol,
OrderID: orders[0].OrderID,
ClientOrderID: orders[0].ClientOrderID,
})
if err != nil {
return nil, fmt.Errorf("failed to query order by id: %s, clientOrderId: %s, err: %w", orders[0].OrderID, orders[0].ClientOrderID, err)
}

return orderRes, nil

// TODO: move this to batch place orders interface
/*
Expand Down
14 changes: 10 additions & 4 deletions pkg/exchange/okex/okexapi/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,21 @@ func TestClient_PlaceOrderRequest(t *testing.T) {

order, err := req.
InstrumentID("BTC-USDT").
TradeMode("cash").
Side(SideTypeBuy).
TradeMode(TradeModeCash).
Side(SideTypeSell).
OrderType(OrderTypeLimit).
Price("15000").
Quantity("0.0001").
TargetCurrency(TargetCurrencyBase).
Price("48000").
Size("0.001").
Do(ctx)
assert.NoError(t, err)
assert.NotEmpty(t, order)
t.Logf("place order: %+v", order)

c := client.NewGetOrderDetailsRequest().OrderID(order[0].OrderID).InstrumentID("BTC-USDT")
res, err := c.Do(ctx)
assert.NoError(t, err)
t.Log(res)
}

func TestClient_GetPendingOrderRequest(t *testing.T) {
Expand Down
67 changes: 67 additions & 0 deletions pkg/exchange/okex/okexapi/place_order_request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package okexapi

import "github.com/c9s/requestgen"

type TradeMode string

const (
TradeModeCash TradeMode = "cash"
TradeModeIsolated TradeMode = "isolated"
TradeModeCross TradeMode = "cross"
)

type TargetCurrency string

const (
TargetCurrencyBase TargetCurrency = "base_ccy"
TargetCurrencyQuote TargetCurrency = "quote_ccy"
)

//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data

type OrderResponse struct {
OrderID string `json:"ordId"`
ClientOrderID string `json:"clOrdId"`
Tag string `json:"tag"`
Code string `json:"sCode"`
Message string `json:"sMsg"`
}

//go:generate PostRequest -url "/api/v5/trade/order" -type PlaceOrderRequest -responseDataType []OrderResponse
type PlaceOrderRequest struct {
client requestgen.AuthenticatedAPIClient

instrumentID string `param:"instId"`

// tdMode
// margin mode: "cross", "isolated"
// non-margin mode cash
tradeMode TradeMode `param:"tdMode" validValues:"cross,isolated,cash"`

// A combination of case-sensitive alphanumerics, all numbers, or all letters of up to 32 characters.
clientOrderID *string `param:"clOrdId"`

// A combination of case-sensitive alphanumerics, all numbers, or all letters of up to 8 characters.
tag *string `param:"tag"`

// "buy" or "sell"
side SideType `param:"side" validValues:"buy,sell"`

orderType OrderType `param:"ordType"`

size string `param:"sz"`

// price
price *string `param:"px"`

// Whether the target currency uses the quote or base currency.
// base_ccy: Base currency ,quote_ccy: Quote currency
// Only applicable to SPOT Market Orders
// Default is quote_ccy for buy, base_ccy for sell
targetCurrency *TargetCurrency `param:"tgtCcy" validValues:"quote_ccy,base_ccy"`
}

func (c *RestClient) NewPlaceOrderRequest() *PlaceOrderRequest {
return &PlaceOrderRequest{client: c}
}
151 changes: 0 additions & 151 deletions pkg/exchange/okex/okexapi/place_order_request_accessors.go

This file was deleted.

Loading

0 comments on commit fcd897b

Please sign in to comment.