bbgo_origin/pkg/exchange/max/maxapi/order.go

577 lines
13 KiB
Go

package max
import (
"context"
"net/url"
"strconv"
"time"
"github.com/pkg/errors"
"github.com/c9s/bbgo/pkg/types"
)
var relUrlV2Order *url.URL
var relUrlV2Orders *url.URL
var relUrlV2OrdersClear *url.URL
var relUrlV2OrdersDelete *url.URL
var relUrlV2OrdersMultiOneByOne, relUrlV2OrderDelete *url.URL
func mustParseURL(s string) *url.URL {
u, err := url.Parse(s)
if err != nil {
panic(err)
}
return u
}
func init() {
relUrlV2Order = mustParseURL("v2/order")
relUrlV2OrderDelete = mustParseURL("v2/order/delete")
relUrlV2Orders = mustParseURL("v2/orders")
relUrlV2OrdersClear = mustParseURL("v2/orders/clear")
relUrlV2OrdersDelete = mustParseURL("v2/orders/delete")
relUrlV2OrdersMultiOneByOne = mustParseURL("v2/orders/multi/onebyone")
}
type OrderStateToQuery int
const (
All = iota
Active
Closed
)
type OrderState string
const (
OrderStateDone = OrderState("done")
OrderStateCancel = OrderState("cancel")
OrderStateWait = OrderState("wait")
OrderStateConvert = OrderState("convert")
OrderStateFinalizing = OrderState("finalizing")
OrderStateFailed = OrderState("failed")
)
type OrderType string
// Order types that the API can return.
const (
OrderTypeMarket = OrderType("market")
OrderTypeLimit = OrderType("limit")
OrderTypePostOnly = OrderType("post_only")
OrderTypeStopLimit = OrderType("stop_limit")
OrderTypeStopMarket = OrderType("stop_market")
OrderTypeIOCLimit = OrderType("ioc_limit")
)
type QueryOrderOptions struct {
GroupID int
Offset int
Limit int
Page int
}
// OrderService manages the Order endpoint.
type OrderService struct {
client *RestClient
}
// Order represents one returned order (POST order/GET order/GET orders) on the max platform.
type Order struct {
ID uint64 `json:"id,omitempty"`
Side string `json:"side"`
OrderType OrderType `json:"ord_type"`
Price string `json:"price,omitempty"`
StopPrice string `json:"stop_price,omitempty"`
AveragePrice string `json:"avg_price,omitempty"`
State OrderState `json:"state,omitempty"`
Market string `json:"market,omitempty"`
Volume string `json:"volume"`
RemainingVolume string `json:"remaining_volume,omitempty"`
ExecutedVolume string `json:"executed_volume,omitempty"`
TradesCount int64 `json:"trades_count,omitempty"`
GroupID uint32 `json:"group_id,omitempty"`
ClientOID string `json:"client_oid,omitempty"`
CreatedAt time.Time `json:"-" db:"created_at"`
CreatedAtMs types.MillisecondTimestamp `json:"created_at_in_ms,omitempty"`
InsertedAt time.Time `json:"-" db:"inserted_at"`
}
// Open returns open orders
func (s *OrderService) Closed(market string, options QueryOrderOptions) ([]Order, error) {
payload := map[string]interface{}{
"market": market,
"state": []OrderState{OrderStateDone, OrderStateCancel},
}
if options.GroupID > 0 {
payload["group_id"] = options.GroupID
}
if options.Offset > 0 {
payload["offset"] = options.Offset
}
if options.Limit > 0 {
payload["limit"] = options.Limit
}
if options.Page > 0 {
payload["page"] = options.Page
}
req, err := s.client.newAuthenticatedRequest("GET", "v2/orders", payload, relUrlV2Orders)
if err != nil {
return nil, err
}
response, err := s.client.sendRequest(req)
if err != nil {
return nil, err
}
var orders []Order
if err := response.DecodeJSON(&orders); err != nil {
return nil, err
}
return orders, nil
}
// Open returns open orders
func (s *OrderService) Open(market string, options QueryOrderOptions) ([]Order, error) {
payload := map[string]interface{}{
"market": market,
// "state": []OrderState{OrderStateWait, OrderStateConvert},
"order_by": "desc",
"pagination": false,
}
if options.GroupID > 0 {
payload["group_id"] = options.GroupID
}
req, err := s.client.newAuthenticatedRequest("GET", "v2/orders", payload, relUrlV2Orders)
if err != nil {
return nil, err
}
response, err := s.client.sendRequest(req)
if err != nil {
return nil, err
}
var orders []Order
if err := response.DecodeJSON(&orders); err != nil {
return nil, err
}
return orders, nil
}
// All returns all orders for the authenticated account.
func (s *OrderService) All(market string, limit, page int, states ...OrderState) ([]Order, error) {
payload := map[string]interface{}{
"market": market,
"limit": limit,
"page": page,
"state": states,
"order_by": "desc",
}
req, err := s.client.newAuthenticatedRequest("GET", "v2/orders", payload, relUrlV2Orders)
if err != nil {
return nil, err
}
response, err := s.client.sendRequest(req)
if err != nil {
return nil, err
}
var orders []Order
if err := response.DecodeJSON(&orders); err != nil {
return nil, err
}
return orders, nil
}
// CancelAll active orders for the authenticated account.
func (s *OrderService) CancelAll(side string, market string) error {
payload := map[string]interface{}{}
if side == "buy" || side == "sell" {
payload["side"] = side
}
if market != "all" {
payload["market"] = market
}
req, err := s.client.newAuthenticatedRequest("POST", "v2/orders/clear", payload, relUrlV2OrdersClear)
if err != nil {
return err
}
_, err = s.client.sendRequest(req)
if err != nil {
return err
}
return nil
}
// Options carry the option fields for REST API
type Options map[string]interface{}
// Create a new order.
func (s *OrderService) Create(market string, side string, volume float64, price float64, orderType string, options Options) (*Order, error) {
options["market"] = market
options["volume"] = strconv.FormatFloat(volume, 'f', -1, 64)
options["price"] = strconv.FormatFloat(price, 'f', -1, 64)
options["side"] = side
options["ord_type"] = orderType
response, err := s.client.sendAuthenticatedRequest("POST", "v2/orders", options)
if err != nil {
return nil, err
}
var order = Order{}
if err := response.DecodeJSON(&order); err != nil {
return nil, err
}
return &order, nil
}
// Create multiple order in a single request
func (s *OrderService) CreateMulti(market string, orders []Order) (*MultiOrderResponse, error) {
req := s.NewCreateMultiOrderRequest()
req.Market(market)
req.AddOrders(orders...)
return req.Do(context.Background())
}
// Cancel the order with id `orderID`.
func (s *OrderService) Cancel(orderID uint64, clientOrderID string) error {
req := s.NewOrderCancelRequest()
if orderID > 0 {
req.ID(orderID)
} else if len(clientOrderID) > 0 {
req.ClientOrderID(clientOrderID)
}
return req.Do(context.Background())
}
type OrderCancelAllRequestParams struct {
*PrivateRequestParams
Side string `json:"side,omitempty"`
Market string `json:"market,omitempty"`
GroupID int64 `json:"groupID,omitempty"`
}
type OrderCancelAllRequest struct {
client *RestClient
params OrderCancelAllRequestParams
side *string
market *string
groupID *uint32
}
func (r *OrderCancelAllRequest) Side(side string) *OrderCancelAllRequest {
r.side = &side
return r
}
func (r *OrderCancelAllRequest) Market(market string) *OrderCancelAllRequest {
r.market = &market
return r
}
func (r *OrderCancelAllRequest) GroupID(groupID uint32) *OrderCancelAllRequest {
r.groupID = &groupID
return r
}
func (r *OrderCancelAllRequest) Do(ctx context.Context) (orders []Order, err error) {
var payload = map[string]interface{}{}
if r.side != nil {
payload["side"] = *r.side
}
if r.market != nil {
payload["market"] = *r.market
}
if r.groupID != nil {
payload["groupID"] = *r.groupID
}
req, err := r.client.newAuthenticatedRequest("POST", "v2/orders/clear", payload, nil)
if err != nil {
return
}
response, err := r.client.sendRequest(req)
if err != nil {
return
}
err = response.DecodeJSON(&orders)
return
}
func (s *OrderService) NewOrderCancelAllRequest() *OrderCancelAllRequest {
return &OrderCancelAllRequest{client: s.client}
}
type OrderCancelRequestParams struct {
*PrivateRequestParams
ID uint64 `json:"id,omitempty"`
ClientOrderID string `json:"client_oid,omitempty"`
}
type OrderCancelRequest struct {
client *RestClient
params OrderCancelRequestParams
}
func (r *OrderCancelRequest) ID(id uint64) *OrderCancelRequest {
r.params.ID = id
return r
}
func (r *OrderCancelRequest) ClientOrderID(id string) *OrderCancelRequest {
r.params.ClientOrderID = id
return r
}
func (r *OrderCancelRequest) Do(ctx context.Context) error {
req, err := r.client.newAuthenticatedRequest("POST", "v2/order/delete", &r.params, relUrlV2OrderDelete)
if err != nil {
return err
}
response, err := r.client.sendRequest(req)
if err != nil {
return err
}
var order = Order{}
if err := response.DecodeJSON(&order); err != nil {
return err
}
return err
}
func (s *OrderService) NewOrderCancelRequest() *OrderCancelRequest {
return &OrderCancelRequest{client: s.client}
}
// Status retrieves the given order from the API.
func (s *OrderService) Get(orderID uint64) (*Order, error) {
payload := map[string]interface{}{
"id": orderID,
}
req, err := s.client.newAuthenticatedRequest("GET", "v2/order", payload, relUrlV2Order)
if err != nil {
return &Order{}, err
}
response, err := s.client.sendRequest(req)
if err != nil {
return nil, err
}
var order = Order{}
if err := response.DecodeJSON(&order); err != nil {
return nil, err
}
return &order, nil
}
type MultiOrderRequestParams struct {
*PrivateRequestParams
Market string `json:"market"`
Orders []Order `json:"orders"`
}
type MultiOrderResponse []struct {
Error string `json:"error,omitempty"`
Order Order `json:"order,omitempty"`
}
type CreateMultiOrderRequest struct {
client *RestClient
market *string
groupID *uint32
orders []Order
}
func (r *CreateMultiOrderRequest) GroupID(groupID uint32) *CreateMultiOrderRequest {
r.groupID = &groupID
return r
}
func (r *CreateMultiOrderRequest) Market(market string) *CreateMultiOrderRequest {
r.market = &market
return r
}
func (r *CreateMultiOrderRequest) AddOrders(orders ...Order) *CreateMultiOrderRequest {
r.orders = append(r.orders, orders...)
return r
}
func (r *CreateMultiOrderRequest) Do(ctx context.Context) (multiOrderResponse *MultiOrderResponse, err error) {
var payload = map[string]interface{}{}
if r.market != nil {
payload["market"] = r.market
} else {
return nil, errors.New("parameter market is required")
}
if r.groupID != nil {
payload["group_id"] = r.groupID
}
if len(r.orders) == 0 {
return nil, errors.New("parameter orders can not be empty")
}
// clear group id
for i := range r.orders {
r.orders[i].GroupID = 0
}
payload["orders"] = r.orders
req, err := r.client.newAuthenticatedRequest("POST", "v2/orders/multi/onebyone", payload, relUrlV2OrdersMultiOneByOne)
if err != nil {
return multiOrderResponse, errors.Wrapf(err, "order create error")
}
response, err := r.client.sendRequest(req)
if err != nil {
return multiOrderResponse, err
}
multiOrderResponse = &MultiOrderResponse{}
if errJson := response.DecodeJSON(multiOrderResponse); errJson != nil {
return multiOrderResponse, errJson
}
return multiOrderResponse, err
}
func (s *OrderService) NewCreateMultiOrderRequest() *CreateMultiOrderRequest {
return &CreateMultiOrderRequest{client: s.client}
}
type CreateOrderRequest struct {
client *RestClient
market *string
volume *string
price *string
stopPrice *string
side *string
orderType *string
clientOrderID *string
groupID *string
}
func (r *CreateOrderRequest) Market(market string) *CreateOrderRequest {
r.market = &market
return r
}
func (r *CreateOrderRequest) Volume(volume string) *CreateOrderRequest {
r.volume = &volume
return r
}
func (r *CreateOrderRequest) Price(price string) *CreateOrderRequest {
r.price = &price
return r
}
func (r *CreateOrderRequest) StopPrice(price string) *CreateOrderRequest {
r.stopPrice = &price
return r
}
func (r *CreateOrderRequest) Side(side string) *CreateOrderRequest {
r.side = &side
return r
}
func (r *CreateOrderRequest) OrderType(orderType string) *CreateOrderRequest {
r.orderType = &orderType
return r
}
func (r *CreateOrderRequest) ClientOrderID(clientOrderID string) *CreateOrderRequest {
r.clientOrderID = &clientOrderID
return r
}
func (r *CreateOrderRequest) Do(ctx context.Context) (order *Order, err error) {
var payload = map[string]interface{}{
"market": r.market,
"volume": r.volume,
"side": r.side,
}
if r.price != nil {
payload["price"] = r.price
}
if r.stopPrice != nil {
payload["stop_price"] = r.stopPrice
}
if r.orderType != nil {
payload["ord_type"] = r.orderType
}
if r.clientOrderID != nil {
payload["client_oid"] = r.clientOrderID
}
if r.groupID != nil {
payload["group_id"] = r.groupID
}
req, err := r.client.newAuthenticatedRequest("POST", "v2/orders", payload, relUrlV2Orders)
if err != nil {
return order, errors.Wrapf(err, "order create error")
}
response, err := r.client.sendRequest(req)
if err != nil {
return order, err
}
order = &Order{}
if err := response.DecodeJSON(order); err != nil {
return nil, err
}
return order, err
}
func (s *OrderService) NewCreateOrderRequest() *CreateOrderRequest {
return &CreateOrderRequest{client: s.client}
}