pkg/exchange: add marketunit for submit order

This commit is contained in:
edwin 2024-10-14 22:05:30 +08:00
parent d48fa7c202
commit 6f7e02daef
4 changed files with 68 additions and 23 deletions

View File

@ -12,7 +12,7 @@ type PlaceOrderResponse struct {
OrderLinkId string `json:"orderLinkId"` OrderLinkId string `json:"orderLinkId"`
} }
//go:generate PostRequest -url "/v5/order/create" -type PlaceOrderRequest -responseDataType .PlaceOrderResponse //go:generate PostRequest -url "/v5/order/create" -type PlaceOrderRequest -responseDataType .PlaceOrderResponse -rateLimiter 5+15/1s
type PlaceOrderRequest struct { type PlaceOrderRequest struct {
client requestgen.AuthenticatedAPIClient client requestgen.AuthenticatedAPIClient
@ -23,6 +23,8 @@ type PlaceOrderRequest struct {
qty string `param:"qty"` qty string `param:"qty"`
orderLinkId string `param:"orderLinkId"` orderLinkId string `param:"orderLinkId"`
timeInForce TimeInForce `param:"timeInForce"` timeInForce TimeInForce `param:"timeInForce"`
// Select the unit for qty when create Spot market orders for UTA account
marketUnit *MarketUnit `param:"marketUnit"`
isLeverage *bool `param:"isLeverage"` isLeverage *bool `param:"isLeverage"`
price *string `param:"price"` price *string `param:"price"`

View File

@ -1,4 +1,4 @@
// Code generated by "requestgen -method POST -responseType .APIResponse -responseDataField Result -url /v5/order/create -type PlaceOrderRequest -responseDataType .PlaceOrderResponse"; DO NOT EDIT. // Code generated by "requestgen -method POST -responseType .APIResponse -responseDataField Result -url /v5/order/create -type PlaceOrderRequest -responseDataType .PlaceOrderResponse -rateLimiter 5+15/1s"; DO NOT EDIT.
package bybitapi package bybitapi
@ -6,11 +6,14 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"golang.org/x/time/rate"
"net/url" "net/url"
"reflect" "reflect"
"regexp" "regexp"
) )
var PlaceOrderRequestLimiter = rate.NewLimiter(15.000000150000002, 5)
func (p *PlaceOrderRequest) Category(category Category) *PlaceOrderRequest { func (p *PlaceOrderRequest) Category(category Category) *PlaceOrderRequest {
p.category = category p.category = category
return p return p
@ -46,6 +49,11 @@ func (p *PlaceOrderRequest) TimeInForce(timeInForce TimeInForce) *PlaceOrderRequ
return p return p
} }
func (p *PlaceOrderRequest) MarketUnit(marketUnit MarketUnit) *PlaceOrderRequest {
p.marketUnit = &marketUnit
return p
}
func (p *PlaceOrderRequest) IsLeverage(isLeverage bool) *PlaceOrderRequest { func (p *PlaceOrderRequest) IsLeverage(isLeverage bool) *PlaceOrderRequest {
p.isLeverage = &isLeverage p.isLeverage = &isLeverage
return p return p
@ -245,6 +253,25 @@ func (p *PlaceOrderRequest) GetParameters() (map[string]interface{}, error) {
// assign parameter of timeInForce // assign parameter of timeInForce
params["timeInForce"] = timeInForce params["timeInForce"] = timeInForce
// check marketUnit field -> json key marketUnit
if p.marketUnit != nil {
marketUnit := *p.marketUnit
// TEMPLATE check-valid-values
switch marketUnit {
case MarketUnitBase, MarketUnitQuote:
params["marketUnit"] = marketUnit
default:
return nil, fmt.Errorf("marketUnit value %v is invalid", marketUnit)
}
// END TEMPLATE check-valid-values
// assign parameter of marketUnit
params["marketUnit"] = marketUnit
} else {
}
// check isLeverage field -> json key isLeverage // check isLeverage field -> json key isLeverage
if p.isLeverage != nil { if p.isLeverage != nil {
isLeverage := *p.isLeverage isLeverage := *p.isLeverage
@ -503,6 +530,9 @@ func (p *PlaceOrderRequest) GetPath() string {
// Do generates the request object and send the request object to the API endpoint // Do generates the request object and send the request object to the API endpoint
func (p *PlaceOrderRequest) Do(ctx context.Context) (*PlaceOrderResponse, error) { func (p *PlaceOrderRequest) Do(ctx context.Context) (*PlaceOrderResponse, error) {
if err := PlaceOrderRequestLimiter.Wait(ctx); err != nil {
return nil, err
}
params, err := p.GetParameters() params, err := p.GetParameters()
if err != nil { if err != nil {
@ -525,15 +555,29 @@ func (p *PlaceOrderRequest) Do(ctx context.Context) (*PlaceOrderResponse, error)
} }
var apiResponse APIResponse var apiResponse APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err type responseUnmarshaler interface {
Unmarshal(data []byte) error
}
if unmarshaler, ok := interface{}(&apiResponse).(responseUnmarshaler); ok {
if err := unmarshaler.Unmarshal(response.Body); err != nil {
return nil, err
}
} else {
// The line below checks the content type, however, some API server might not send the correct content type header,
// Hence, this is commented for backward compatibility
// response.IsJSON()
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
} }
type responseValidator interface { type responseValidator interface {
Validate() error Validate() error
} }
validator, ok := interface{}(apiResponse).(responseValidator)
if ok { if validator, ok := interface{}(&apiResponse).(responseValidator); ok {
if err := validator.Validate(); err != nil { if err := validator.Validate(); err != nil {
return nil, err return nil, err
} }

View File

@ -128,3 +128,10 @@ const (
type AccountType string type AccountType string
const AccountTypeSpot AccountType = "SPOT" const AccountTypeSpot AccountType = "SPOT"
type MarketUnit string
const (
MarketUnitBase MarketUnit = "baseCoin"
MarketUnitQuote MarketUnit = "quoteCoin"
)

View File

@ -272,23 +272,18 @@ func (e *Exchange) SubmitOrder(ctx context.Context, order types.SubmitOrder) (*t
return nil, err return nil, err
} }
req.Side(side) req.Side(side)
req.Qty(order.Market.FormatQuantity(order.Quantity))
// set quantity
orderQty := order.Quantity
// if the order is market buy, the quantity is quote coin, instead of base coin. so we need to convert it.
if order.Type == types.OrderTypeMarket && order.Side == types.SideTypeBuy {
ticker, err := e.QueryTicker(ctx, order.Market.Symbol)
if err != nil {
return nil, err
}
orderQty = order.Quantity.Mul(ticker.Buy)
}
req.Qty(order.Market.FormatQuantity(orderQty))
// set price
switch order.Type { switch order.Type {
case types.OrderTypeLimit: case types.OrderTypeStopLimit, types.OrderTypeLimit, types.OrderTypeLimitMaker:
req.Price(order.Market.FormatPrice(order.Price)) req.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 {
req.MarketUnit(bybitapi.MarketUnitBase)
} else {
req.MarketUnit(bybitapi.MarketUnitQuote)
}
} }
// set timeInForce // set timeInForce
@ -309,9 +304,6 @@ func (e *Exchange) SubmitOrder(ctx context.Context, order types.SubmitOrder) (*t
req.OrderLinkId(order.ClientOrderID) req.OrderLinkId(order.ClientOrderID)
} }
if err := orderRateLimiter.Wait(ctx); err != nil {
return nil, fmt.Errorf("place order rate limiter wait error: %w", err)
}
timeNow := time.Now() timeNow := time.Now()
res, err := req.Do(ctx) res, err := req.Do(ctx)
if err != nil { if err != nil {