bbgo_origin/pkg/exchange/okex/okexapi/trade.go

521 lines
12 KiB
Go
Raw Normal View History

2021-05-24 16:30:29 +00:00
package okexapi
import (
"context"
"net/url"
2021-05-24 16:50:53 +00:00
"strings"
2021-05-24 16:30:29 +00:00
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/pkg/errors"
)
type TradeService struct {
client *RestClient
}
type OrderResponse struct {
OrderID string `json:"ordId"`
ClientOrderID string `json:"clOrdId"`
Tag string `json:"tag"`
Code string `json:"sCode"`
Message string `json:"sMsg"`
}
func (c *TradeService) NewPlaceOrderRequest() *PlaceOrderRequest {
return &PlaceOrderRequest{
client: c.client,
}
}
func (c *TradeService) NewBatchPlaceOrderRequest() *BatchPlaceOrderRequest {
return &BatchPlaceOrderRequest{
client: c.client,
}
}
func (c *TradeService) NewCancelOrderRequest() *CancelOrderRequest {
return &CancelOrderRequest{
client: c.client,
}
}
func (c *TradeService) NewBatchCancelOrderRequest() *BatchCancelOrderRequest {
return &BatchCancelOrderRequest{
client: c.client,
}
}
func (c *TradeService) NewGetOrderDetailsRequest() *GetOrderDetailsRequest {
return &GetOrderDetailsRequest{
client: c.client,
}
}
func (c *TradeService) NewGetPendingOrderRequest() *GetPendingOrderRequest {
return &GetPendingOrderRequest{
client: c.client,
}
}
func (c *TradeService) NewGetTransactionDetailsRequest() *GetTransactionDetailsRequest {
return &GetTransactionDetailsRequest{
client: c.client,
}
}
//go:generate requestgen -type PlaceOrderRequest
2021-05-24 16:50:53 +00:00
type PlaceOrderRequest struct {
client *RestClient
instrumentID string `param:"instId"`
2021-05-24 16:50:53 +00:00
// tdMode
// margin mode: "cross", "isolated"
// non-margin mode cash
tradeMode string `param:"tdMode" validValues:"cross,isolated,cash"`
2021-05-24 16:50:53 +00:00
// A combination of case-sensitive alphanumerics, all numbers, or all letters of up to 32 characters.
clientOrderID *string `param:"clOrdId"`
2021-05-24 16:50:53 +00:00
// A combination of case-sensitive alphanumerics, all numbers, or all letters of up to 8 characters.
tag *string `param:"tag"`
2021-05-24 16:50:53 +00:00
// "buy" or "sell"
side SideType `param:"side" validValues:"buy,sell"`
2021-05-24 16:50:53 +00:00
orderType OrderType `param:"ordType"`
2021-05-24 16:50:53 +00:00
quantity string `param:"sz"`
2021-05-24 16:50:53 +00:00
// price
price *string `param:"px"`
2021-05-24 16:50:53 +00:00
}
func (r *PlaceOrderRequest) Parameters() map[string]interface{} {
params, _ := r.GetParameters()
return params
2021-05-24 16:50:53 +00:00
}
func (r *PlaceOrderRequest) Do(ctx context.Context) (*OrderResponse, error) {
payload := r.Parameters()
req, err := r.client.newAuthenticatedRequest("POST", "/api/v5/trade/order", nil, payload)
if err != nil {
return nil, err
}
response, err := r.client.sendRequest(req)
if err != nil {
return nil, err
}
var orderResponse struct {
Code string `json:"code"`
Message string `json:"msg"`
Data []OrderResponse `json:"data"`
}
if err := response.DecodeJSON(&orderResponse); err != nil {
return nil, err
}
if len(orderResponse.Data) == 0 {
return nil, errors.New("order create error")
}
return &orderResponse.Data[0], nil
}
//go:generate requestgen -type CancelOrderRequest
2021-05-24 16:30:29 +00:00
type CancelOrderRequest struct {
client *RestClient
instrumentID string `param:"instId"`
orderID *string `param:"ordId"`
clientOrderID *string `param:"clOrdId"`
2021-05-24 16:30:29 +00:00
}
func (r *CancelOrderRequest) Parameters() map[string]interface{} {
payload, _ := r.GetParameters()
2021-05-24 16:30:29 +00:00
return payload
}
func (r *CancelOrderRequest) Do(ctx context.Context) ([]OrderResponse, error) {
payload, err := r.GetParameters()
if err != nil {
return nil, err
}
2021-05-24 16:30:29 +00:00
if r.clientOrderID == nil && r.orderID != nil {
2021-05-24 16:30:29 +00:00
return nil, errors.New("either orderID or clientOrderID is required for canceling order")
}
req, err := r.client.newAuthenticatedRequest("POST", "/api/v5/trade/cancel-order", nil, payload)
if err != nil {
return nil, err
}
response, err := r.client.sendRequest(req)
if err != nil {
return nil, err
}
var orderResponse struct {
Code string `json:"code"`
Message string `json:"msg"`
Data []OrderResponse `json:"data"`
}
if err := response.DecodeJSON(&orderResponse); err != nil {
return nil, err
}
return orderResponse.Data, nil
}
type BatchCancelOrderRequest struct {
client *RestClient
reqs []*CancelOrderRequest
}
func (r *BatchCancelOrderRequest) Add(reqs ...*CancelOrderRequest) *BatchCancelOrderRequest {
r.reqs = append(r.reqs, reqs...)
return r
}
2021-05-27 19:05:59 +00:00
func (r *BatchCancelOrderRequest) Do(ctx context.Context) ([]OrderResponse, error) {
2021-05-24 16:30:29 +00:00
var parameterList []map[string]interface{}
for _, req := range r.reqs {
params := req.Parameters()
parameterList = append(parameterList, params)
}
req, err := r.client.newAuthenticatedRequest("POST", "/api/v5/trade/cancel-batch-orders", nil, parameterList)
if err != nil {
return nil, err
}
response, err := r.client.sendRequest(req)
if err != nil {
return nil, err
}
var orderResponse struct {
Code string `json:"code"`
Message string `json:"msg"`
Data []OrderResponse `json:"data"`
}
if err := response.DecodeJSON(&orderResponse); err != nil {
return nil, err
}
2021-05-27 19:05:59 +00:00
return orderResponse.Data, nil
2021-05-24 16:30:29 +00:00
}
type BatchPlaceOrderRequest struct {
client *RestClient
reqs []*PlaceOrderRequest
}
func (r *BatchPlaceOrderRequest) Add(reqs ...*PlaceOrderRequest) *BatchPlaceOrderRequest {
r.reqs = append(r.reqs, reqs...)
return r
}
func (r *BatchPlaceOrderRequest) Do(ctx context.Context) ([]OrderResponse, error) {
var parameterList []map[string]interface{}
for _, req := range r.reqs {
params := req.Parameters()
parameterList = append(parameterList, params)
}
req, err := r.client.newAuthenticatedRequest("POST", "/api/v5/trade/batch-orders", nil, parameterList)
if err != nil {
return nil, err
}
response, err := r.client.sendRequest(req)
if err != nil {
return nil, err
}
var orderResponse struct {
Code string `json:"code"`
Message string `json:"msg"`
Data []OrderResponse `json:"data"`
}
if err := response.DecodeJSON(&orderResponse); err != nil {
return nil, err
}
return orderResponse.Data, nil
}
2021-05-24 16:50:53 +00:00
type OrderDetails struct {
InstrumentType string `json:"instType"`
InstrumentID string `json:"instId"`
Tag string `json:"tag"`
Price fixedpoint.Value `json:"px"`
Quantity fixedpoint.Value `json:"sz"`
2021-05-24 16:30:29 +00:00
OrderID string `json:"ordId"`
ClientOrderID string `json:"clOrdId"`
OrderType OrderType `json:"ordType"`
Side SideType `json:"side"`
2021-05-24 16:50:53 +00:00
// Accumulated fill quantity
FilledQuantity fixedpoint.Value `json:"accFillSz"`
2021-05-24 16:30:29 +00:00
FeeCurrency string `json:"feeCcy"`
Fee fixedpoint.Value `json:"fee"`
// trade related fields
LastTradeID string `json:"tradeId,omitempty"`
LastFilledPrice fixedpoint.Value `json:"fillPx"`
LastFilledQuantity fixedpoint.Value `json:"fillSz"`
LastFilledTime types.MillisecondTimestamp `json:"fillTime"`
LastFilledFee fixedpoint.Value `json:"fillFee"`
LastFilledFeeCurrency string `json:"fillFeeCcy"`
// ExecutionType = liquidity (M = maker or T = taker)
ExecutionType string `json:"execType"`
2021-05-24 16:30:29 +00:00
2021-05-24 16:50:53 +00:00
// Average filled price. If none is filled, it will return 0.
AveragePrice fixedpoint.Value `json:"avgPx"`
2021-05-24 16:30:29 +00:00
// Currency = Margin currency
// Only applicable to cross MARGIN orders in Single-currency margin.
Currency string `json:"ccy"`
2021-05-24 16:30:29 +00:00
// Leverage = from 0.01 to 125.
// Only applicable to MARGIN/FUTURES/SWAP
Leverage fixedpoint.Value `json:"lever"`
2021-05-24 16:30:29 +00:00
2021-05-24 16:50:53 +00:00
RebateCurrency string `json:"rebateCcy"`
Rebate fixedpoint.Value `json:"rebate"`
2021-05-24 16:30:29 +00:00
PnL fixedpoint.Value `json:"pnl"`
2021-05-24 16:30:29 +00:00
2021-05-24 16:50:53 +00:00
UpdateTime types.MillisecondTimestamp `json:"uTime"`
CreationTime types.MillisecondTimestamp `json:"cTime"`
2021-05-24 16:30:29 +00:00
State OrderState `json:"state"`
2021-05-24 16:30:29 +00:00
}
2021-05-24 16:50:53 +00:00
type GetOrderDetailsRequest struct {
client *RestClient
2021-05-24 16:30:29 +00:00
2021-05-24 16:50:53 +00:00
instId string
ordId *string
clOrdId *string
2021-05-24 16:30:29 +00:00
}
2021-05-24 16:50:53 +00:00
func (r *GetOrderDetailsRequest) InstrumentID(instId string) *GetOrderDetailsRequest {
r.instId = instId
2021-05-24 16:30:29 +00:00
return r
}
2021-05-24 16:50:53 +00:00
func (r *GetOrderDetailsRequest) OrderID(orderID string) *GetOrderDetailsRequest {
r.ordId = &orderID
2021-05-24 16:30:29 +00:00
return r
}
2021-05-24 16:50:53 +00:00
func (r *GetOrderDetailsRequest) ClientOrderID(clientOrderID string) *GetOrderDetailsRequest {
r.clOrdId = &clientOrderID
return r
2021-05-24 16:30:29 +00:00
}
func (r *GetOrderDetailsRequest) QueryParameters() url.Values {
var values = url.Values{}
values.Add("instId", r.instId)
if r.ordId != nil {
values.Add("ordId", *r.ordId)
} else if r.clOrdId != nil {
values.Add("clOrdId", *r.clOrdId)
}
return values
}
2021-05-24 16:50:53 +00:00
func (r *GetOrderDetailsRequest) Do(ctx context.Context) (*OrderDetails, error) {
params := r.QueryParameters()
req, err := r.client.newAuthenticatedRequest("GET", "/api/v5/trade/order", params, nil)
2021-05-24 16:30:29 +00:00
if err != nil {
return nil, err
}
response, err := r.client.sendRequest(req)
if err != nil {
return nil, err
}
var orderResponse struct {
2021-05-24 16:50:53 +00:00
Code string `json:"code"`
Message string `json:"msg"`
Data []OrderDetails `json:"data"`
2021-05-24 16:30:29 +00:00
}
if err := response.DecodeJSON(&orderResponse); err != nil {
return nil, err
}
if len(orderResponse.Data) == 0 {
return nil, errors.New("order create error")
}
return &orderResponse.Data[0], nil
}
2021-05-24 16:50:53 +00:00
type GetPendingOrderRequest struct {
2021-05-24 16:30:29 +00:00
client *RestClient
instId *string
instType *InstrumentType
2021-05-24 16:30:29 +00:00
2021-05-24 16:50:53 +00:00
orderTypes []string
2021-05-24 16:30:29 +00:00
2021-05-24 16:50:53 +00:00
state *OrderState
2021-05-24 16:30:29 +00:00
}
func (r *GetPendingOrderRequest) InstrumentID(instId string) *GetPendingOrderRequest {
r.instId = &instId
return r
}
func (r *GetPendingOrderRequest) InstrumentType(instType InstrumentType) *GetPendingOrderRequest {
2021-05-24 16:50:53 +00:00
r.instType = &instType
return r
2021-05-24 16:30:29 +00:00
}
2021-05-24 16:50:53 +00:00
func (r *GetPendingOrderRequest) State(state OrderState) *GetPendingOrderRequest {
r.state = &state
2021-05-24 16:30:29 +00:00
return r
}
2021-05-24 16:50:53 +00:00
func (r *GetPendingOrderRequest) OrderTypes(orderTypes []string) *GetPendingOrderRequest {
r.orderTypes = orderTypes
2021-05-24 16:30:29 +00:00
return r
}
2021-05-24 16:50:53 +00:00
func (r *GetPendingOrderRequest) AddOrderTypes(orderTypes ...string) *GetPendingOrderRequest {
r.orderTypes = append(r.orderTypes, orderTypes...)
2021-05-24 16:30:29 +00:00
return r
}
2021-05-24 16:50:53 +00:00
func (r *GetPendingOrderRequest) Parameters() map[string]interface{} {
var payload = map[string]interface{}{}
if r.instId != nil {
payload["instId"] = r.instId
}
2021-05-24 16:50:53 +00:00
if r.instType != nil {
payload["instType"] = r.instType
}
if r.state != nil {
payload["state"] = r.state
}
if len(r.orderTypes) > 0 {
payload["ordType"] = strings.Join(r.orderTypes, ",")
}
return payload
}
func (r *GetPendingOrderRequest) Do(ctx context.Context) ([]OrderDetails, error) {
2021-05-24 16:30:29 +00:00
payload := r.Parameters()
2021-05-24 16:50:53 +00:00
req, err := r.client.newAuthenticatedRequest("GET", "/api/v5/trade/orders-pending", nil, payload)
2021-05-24 16:30:29 +00:00
if err != nil {
return nil, err
}
response, err := r.client.sendRequest(req)
if err != nil {
return nil, err
}
var orderResponse struct {
Code string `json:"code"`
Message string `json:"msg"`
Data []OrderDetails `json:"data"`
}
if err := response.DecodeJSON(&orderResponse); err != nil {
return nil, err
}
2021-05-24 16:50:53 +00:00
return orderResponse.Data, nil
2021-05-24 16:30:29 +00:00
}
type GetTransactionDetailsRequest struct {
client *RestClient
instType *InstrumentType
instId *string
ordId *string
}
func (r *GetTransactionDetailsRequest) InstrumentType(instType InstrumentType) *GetTransactionDetailsRequest {
r.instType = &instType
return r
}
func (r *GetTransactionDetailsRequest) InstrumentID(instId string) *GetTransactionDetailsRequest {
r.instId = &instId
return r
}
func (r *GetTransactionDetailsRequest) OrderID(orderID string) *GetTransactionDetailsRequest {
r.ordId = &orderID
return r
}
func (r *GetTransactionDetailsRequest) Parameters() map[string]interface{} {
var payload = map[string]interface{}{}
if r.instType != nil {
payload["instType"] = r.instType
}
if r.instId != nil {
payload["instId"] = r.instId
}
if r.ordId != nil {
payload["ordId"] = r.ordId
}
return payload
}
func (r *GetTransactionDetailsRequest) Do(ctx context.Context) ([]OrderDetails, error) {
payload := r.Parameters()
req, err := r.client.newAuthenticatedRequest("GET", "/api/v5/trade/fills", nil, payload)
if err != nil {
return nil, err
}
response, err := r.client.sendRequest(req)
if err != nil {
return nil, err
}
var orderResponse struct {
Code string `json:"code"`
Message string `json:"msg"`
Data []OrderDetails `json:"data"`
}
if err := response.DecodeJSON(&orderResponse); err != nil {
return nil, err
}
return orderResponse.Data, nil
}