mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 06:53:52 +00:00
max: fix trades/orders parsing
This commit is contained in:
parent
d792f3b83b
commit
c891cc56e3
|
@ -4,6 +4,28 @@ sessions:
|
|||
exchange: max
|
||||
margin: true
|
||||
|
||||
sync:
|
||||
# userDataStream is used to sync the trading data in real-time
|
||||
# it uses the websocket connection to insert the trades
|
||||
userDataStream:
|
||||
trades: false
|
||||
filledOrders: false
|
||||
|
||||
# since is the start date of your trading data
|
||||
since: 2019-11-01
|
||||
|
||||
# sessions is the list of session names you want to sync
|
||||
# by default, BBGO sync all your available sessions.
|
||||
sessions:
|
||||
- max_margin
|
||||
|
||||
# symbols is the list of symbols you want to sync
|
||||
# by default, BBGO try to guess your symbols by your existing account balances.
|
||||
symbols:
|
||||
- BTCUSDT
|
||||
- ETHUSDT
|
||||
|
||||
|
||||
exchangeStrategies:
|
||||
|
||||
- on: max_margin
|
||||
|
|
|
@ -168,6 +168,7 @@ func toGlobalOrders(maxOrders []max.Order) (orders []types.Order, err error) {
|
|||
func toGlobalOrder(maxOrder max.Order) (*types.Order, error) {
|
||||
executedVolume := maxOrder.ExecutedVolume
|
||||
remainingVolume := maxOrder.RemainingVolume
|
||||
isMargin := maxOrder.WalletType == max.WalletTypeMargin
|
||||
|
||||
return &types.Order{
|
||||
SubmitOrder: types.SubmitOrder{
|
||||
|
@ -185,52 +186,33 @@ func toGlobalOrder(maxOrder max.Order) (*types.Order, error) {
|
|||
OrderID: maxOrder.ID,
|
||||
Status: toGlobalOrderStatus(maxOrder.State, executedVolume, remainingVolume),
|
||||
ExecutedQuantity: executedVolume,
|
||||
CreationTime: types.Time(maxOrder.CreatedAtMs.Time()),
|
||||
UpdateTime: types.Time(maxOrder.CreatedAtMs.Time()),
|
||||
CreationTime: types.Time(maxOrder.CreatedAt.Time()),
|
||||
UpdateTime: types.Time(maxOrder.CreatedAt.Time()),
|
||||
IsMargin: isMargin,
|
||||
IsIsolated: false, // isolated margin is not supported
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toGlobalTrade(t max.Trade) (*types.Trade, error) {
|
||||
// skip trade ID that is the same. however this should not happen
|
||||
var side = toGlobalSideType(t.Side)
|
||||
|
||||
// trade time
|
||||
mts := t.CreatedAtMilliSeconds
|
||||
|
||||
price, err := fixedpoint.NewFromString(t.Price)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
quantity, err := fixedpoint.NewFromString(t.Volume)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
quoteQuantity, err := fixedpoint.NewFromString(t.Funds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fee, err := fixedpoint.NewFromString(t.Fee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isMargin := t.WalletType == max.WalletTypeMargin
|
||||
side := toGlobalSideType(t.Side)
|
||||
return &types.Trade{
|
||||
ID: t.ID,
|
||||
OrderID: t.OrderID,
|
||||
Price: price,
|
||||
Price: t.Price,
|
||||
Symbol: toGlobalSymbol(t.Market),
|
||||
Exchange: "max",
|
||||
Quantity: quantity,
|
||||
Exchange: types.ExchangeMax,
|
||||
Quantity: t.Volume,
|
||||
Side: side,
|
||||
IsBuyer: t.IsBuyer(),
|
||||
IsMaker: t.IsMaker(),
|
||||
Fee: fee,
|
||||
Fee: t.Fee,
|
||||
FeeCurrency: toGlobalCurrency(t.FeeCurrency),
|
||||
QuoteQuantity: quoteQuantity,
|
||||
Time: types.Time(mts),
|
||||
QuoteQuantity: t.Funds,
|
||||
Time: types.Time(t.CreatedAt),
|
||||
IsMargin: isMargin,
|
||||
IsIsolated: false,
|
||||
IsFutures: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -297,16 +279,6 @@ func convertWebSocketTrade(t max.TradeUpdate) (*types.Trade, error) {
|
|||
}
|
||||
|
||||
func convertWebSocketOrderUpdate(u max.OrderUpdate) (*types.Order, error) {
|
||||
executedVolume, err := fixedpoint.NewFromString(u.ExecutedVolume)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
remainingVolume, err := fixedpoint.NewFromString(u.RemainingVolume)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeInForce := types.TimeInForceGTC
|
||||
if u.OrderType == max.OrderTypeIOCLimit {
|
||||
timeInForce = types.TimeInForceIOC
|
||||
|
@ -318,16 +290,16 @@ func convertWebSocketOrderUpdate(u max.OrderUpdate) (*types.Order, error) {
|
|||
Symbol: toGlobalSymbol(u.Market),
|
||||
Side: toGlobalSideType(u.Side),
|
||||
Type: toGlobalOrderType(u.OrderType),
|
||||
Quantity: fixedpoint.MustNewFromString(u.Volume),
|
||||
Price: fixedpoint.MustNewFromString(u.Price),
|
||||
StopPrice: fixedpoint.MustNewFromString(u.StopPrice),
|
||||
Quantity: u.Volume,
|
||||
Price: u.Price,
|
||||
StopPrice: u.StopPrice,
|
||||
TimeInForce: timeInForce, // MAX only supports GTC
|
||||
GroupID: u.GroupID,
|
||||
},
|
||||
Exchange: types.ExchangeMax,
|
||||
OrderID: u.ID,
|
||||
Status: toGlobalOrderStatus(u.State, executedVolume, remainingVolume),
|
||||
ExecutedQuantity: executedVolume,
|
||||
Status: toGlobalOrderStatus(u.State, u.ExecutedVolume, u.RemainingVolume),
|
||||
ExecutedQuantity: u.ExecutedVolume,
|
||||
CreationTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
|
||||
UpdateTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
|
||||
}, nil
|
||||
|
|
|
@ -773,11 +773,17 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
|
|||
return nil, err
|
||||
}
|
||||
|
||||
req := e.client.TradeService.NewGetPrivateTradeRequest()
|
||||
req.Market(toLocalSymbol(symbol))
|
||||
market := toLocalSymbol(symbol)
|
||||
walletType := maxapi.WalletTypeSpot
|
||||
if e.MarginSettings.IsMargin {
|
||||
walletType = maxapi.WalletTypeMargin
|
||||
}
|
||||
|
||||
req := e.v3order.NewWalletGetTradesRequest(walletType)
|
||||
req.Market(market)
|
||||
|
||||
if options.Limit > 0 {
|
||||
req.Limit(options.Limit)
|
||||
req.Limit(uint64(options.Limit))
|
||||
} else {
|
||||
req.Limit(1000)
|
||||
}
|
||||
|
@ -785,12 +791,9 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
|
|||
// MAX uses exclusive last trade ID
|
||||
// the timestamp parameter is used for reverse order, we can't use it.
|
||||
if options.LastTradeID > 0 {
|
||||
req.From(int64(options.LastTradeID))
|
||||
req.From(options.LastTradeID)
|
||||
}
|
||||
|
||||
// make it compatible with binance, we need the last trade id for the next page.
|
||||
req.OrderBy("asc")
|
||||
|
||||
maxTrades, err := req.Do(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
// Code generated by "requestgen -method GET -url v2/orders/history -type GetOrderHistoryRequest -responseType []Order"; DO NOT EDIT.
|
||||
|
||||
package max
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func (g *GetOrderHistoryRequest) Market(market string) *GetOrderHistoryRequest {
|
||||
g.market = market
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GetOrderHistoryRequest) FromID(fromID uint64) *GetOrderHistoryRequest {
|
||||
g.fromID = &fromID
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GetOrderHistoryRequest) Limit(limit uint) *GetOrderHistoryRequest {
|
||||
g.limit = &limit
|
||||
return g
|
||||
}
|
||||
|
||||
// GetQueryParameters builds and checks the query parameters and returns url.Values
|
||||
func (g *GetOrderHistoryRequest) GetQueryParameters() (url.Values, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
query := url.Values{}
|
||||
for _k, _v := range params {
|
||||
query.Add(_k, fmt.Sprintf("%v", _v))
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParameters builds and checks the parameters and return the result in a map object
|
||||
func (g *GetOrderHistoryRequest) GetParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
// check market field -> json key market
|
||||
market := g.market
|
||||
|
||||
// assign parameter of market
|
||||
params["market"] = market
|
||||
// check fromID field -> json key from_id
|
||||
if g.fromID != nil {
|
||||
fromID := *g.fromID
|
||||
|
||||
// assign parameter of fromID
|
||||
params["from_id"] = fromID
|
||||
} else {
|
||||
}
|
||||
// check limit field -> json key limit
|
||||
if g.limit != nil {
|
||||
limit := *g.limit
|
||||
|
||||
// assign parameter of limit
|
||||
params["limit"] = limit
|
||||
} else {
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
|
||||
func (g *GetOrderHistoryRequest) GetParametersQuery() (url.Values, error) {
|
||||
query := url.Values{}
|
||||
|
||||
params, err := g.GetParameters()
|
||||
if err != nil {
|
||||
return query, err
|
||||
}
|
||||
|
||||
for _k, _v := range params {
|
||||
if g.isVarSlice(_v) {
|
||||
g.iterateSlice(_v, func(it interface{}) {
|
||||
query.Add(_k+"[]", fmt.Sprintf("%v", it))
|
||||
})
|
||||
} else {
|
||||
query.Add(_k, fmt.Sprintf("%v", _v))
|
||||
}
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParametersJSON converts the parameters from GetParameters into the JSON format
|
||||
func (g *GetOrderHistoryRequest) GetParametersJSON() ([]byte, error) {
|
||||
params, err := g.GetParameters()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(params)
|
||||
}
|
||||
|
||||
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
|
||||
func (g *GetOrderHistoryRequest) GetSlugParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (g *GetOrderHistoryRequest) applySlugsToUrl(url string, slugs map[string]string) string {
|
||||
for _k, _v := range slugs {
|
||||
needleRE := regexp.MustCompile(":" + _k + "\\b")
|
||||
url = needleRE.ReplaceAllString(url, _v)
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func (g *GetOrderHistoryRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
|
||||
sliceValue := reflect.ValueOf(slice)
|
||||
for _i := 0; _i < sliceValue.Len(); _i++ {
|
||||
it := sliceValue.Index(_i).Interface()
|
||||
_f(it)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GetOrderHistoryRequest) isVarSlice(_v interface{}) bool {
|
||||
rt := reflect.TypeOf(_v)
|
||||
switch rt.Kind() {
|
||||
case reflect.Slice:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *GetOrderHistoryRequest) GetSlugsMap() (map[string]string, error) {
|
||||
slugs := map[string]string{}
|
||||
params, err := g.GetSlugParameters()
|
||||
if err != nil {
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
for _k, _v := range params {
|
||||
slugs[_k] = fmt.Sprintf("%v", _v)
|
||||
}
|
||||
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
func (g *GetOrderHistoryRequest) Do(ctx context.Context) ([]Order, error) {
|
||||
|
||||
// empty params for GET operation
|
||||
var params interface{}
|
||||
query, err := g.GetParametersQuery()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
apiURL := "v2/orders/history"
|
||||
|
||||
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := g.client.SendRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var apiResponse []Order
|
||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apiResponse, nil
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
// Code generated by "requestgen -method GET -url v2/trades/my -type GetPrivateTradesRequest -responseType []Trade"; DO NOT EDIT.
|
||||
|
||||
package max
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (p *GetPrivateTradesRequest) Market(market string) *GetPrivateTradesRequest {
|
||||
p.market = market
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) Timestamp(timestamp time.Time) *GetPrivateTradesRequest {
|
||||
p.timestamp = ×tamp
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) From(from int64) *GetPrivateTradesRequest {
|
||||
p.from = &from
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) To(to int64) *GetPrivateTradesRequest {
|
||||
p.to = &to
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) OrderBy(orderBy string) *GetPrivateTradesRequest {
|
||||
p.orderBy = &orderBy
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) Pagination(pagination bool) *GetPrivateTradesRequest {
|
||||
p.pagination = &pagination
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) Limit(limit int64) *GetPrivateTradesRequest {
|
||||
p.limit = &limit
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) Offset(offset int64) *GetPrivateTradesRequest {
|
||||
p.offset = &offset
|
||||
return p
|
||||
}
|
||||
|
||||
// GetQueryParameters builds and checks the query parameters and returns url.Values
|
||||
func (p *GetPrivateTradesRequest) GetQueryParameters() (url.Values, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
query := url.Values{}
|
||||
for k, v := range params {
|
||||
query.Add(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParameters builds and checks the parameters and return the result in a map object
|
||||
func (p *GetPrivateTradesRequest) GetParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
// check market field -> json key market
|
||||
market := p.market
|
||||
|
||||
// assign parameter of market
|
||||
params["market"] = market
|
||||
// check timestamp field -> json key timestamp
|
||||
if p.timestamp != nil {
|
||||
timestamp := *p.timestamp
|
||||
|
||||
// assign parameter of timestamp
|
||||
// convert time.Time to seconds time stamp
|
||||
params["timestamp"] = strconv.FormatInt(timestamp.Unix(), 10)
|
||||
} else {
|
||||
}
|
||||
// check from field -> json key from
|
||||
if p.from != nil {
|
||||
from := *p.from
|
||||
|
||||
// assign parameter of from
|
||||
params["from"] = from
|
||||
} else {
|
||||
}
|
||||
// check to field -> json key to
|
||||
if p.to != nil {
|
||||
to := *p.to
|
||||
|
||||
// assign parameter of to
|
||||
params["to"] = to
|
||||
} else {
|
||||
}
|
||||
// check orderBy field -> json key order_by
|
||||
if p.orderBy != nil {
|
||||
orderBy := *p.orderBy
|
||||
|
||||
// assign parameter of orderBy
|
||||
params["order_by"] = orderBy
|
||||
} else {
|
||||
}
|
||||
// check pagination field -> json key pagination
|
||||
if p.pagination != nil {
|
||||
pagination := *p.pagination
|
||||
|
||||
// assign parameter of pagination
|
||||
params["pagination"] = pagination
|
||||
} else {
|
||||
}
|
||||
// check limit field -> json key limit
|
||||
if p.limit != nil {
|
||||
limit := *p.limit
|
||||
|
||||
// assign parameter of limit
|
||||
params["limit"] = limit
|
||||
} else {
|
||||
}
|
||||
// check offset field -> json key offset
|
||||
if p.offset != nil {
|
||||
offset := *p.offset
|
||||
|
||||
// assign parameter of offset
|
||||
params["offset"] = offset
|
||||
} else {
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
|
||||
func (p *GetPrivateTradesRequest) GetParametersQuery() (url.Values, error) {
|
||||
query := url.Values{}
|
||||
|
||||
params, err := p.GetParameters()
|
||||
if err != nil {
|
||||
return query, err
|
||||
}
|
||||
|
||||
for k, v := range params {
|
||||
if p.isVarSlice(v) {
|
||||
p.iterateSlice(v, func(it interface{}) {
|
||||
query.Add(k+"[]", fmt.Sprintf("%v", it))
|
||||
})
|
||||
} else {
|
||||
query.Add(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParametersJSON converts the parameters from GetParameters into the JSON format
|
||||
func (p *GetPrivateTradesRequest) GetParametersJSON() ([]byte, error) {
|
||||
params, err := p.GetParameters()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(params)
|
||||
}
|
||||
|
||||
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
|
||||
func (p *GetPrivateTradesRequest) GetSlugParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) applySlugsToUrl(url string, slugs map[string]string) string {
|
||||
for k, v := range slugs {
|
||||
needleRE := regexp.MustCompile(":" + k + "\\b")
|
||||
url = needleRE.ReplaceAllString(url, v)
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) iterateSlice(slice interface{}, f func(it interface{})) {
|
||||
sliceValue := reflect.ValueOf(slice)
|
||||
for i := 0; i < sliceValue.Len(); i++ {
|
||||
it := sliceValue.Index(i).Interface()
|
||||
f(it)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) isVarSlice(v interface{}) bool {
|
||||
rt := reflect.TypeOf(v)
|
||||
switch rt.Kind() {
|
||||
case reflect.Slice:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) GetSlugsMap() (map[string]string, error) {
|
||||
slugs := map[string]string{}
|
||||
params, err := p.GetSlugParameters()
|
||||
if err != nil {
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
for k, v := range params {
|
||||
slugs[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
func (p *GetPrivateTradesRequest) Do(ctx context.Context) ([]Trade, error) {
|
||||
|
||||
// empty params for GET operation
|
||||
var params interface{}
|
||||
query, err := p.GetParametersQuery()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
apiURL := "v2/trades/my"
|
||||
|
||||
req, err := p.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := p.client.SendRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var apiResponse []Trade
|
||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apiResponse, nil
|
||||
}
|
|
@ -6,7 +6,6 @@ package max
|
|||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/c9s/requestgen"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -99,7 +98,7 @@ type SubmitOrder struct {
|
|||
// Order represents one returned order (POST order/GET order/GET orders) on the max platform.
|
||||
type Order struct {
|
||||
ID uint64 `json:"id,omitempty"`
|
||||
WalletType string `json:"wallet_type,omitempty"`
|
||||
WalletType WalletType `json:"wallet_type,omitempty"`
|
||||
Side string `json:"side"`
|
||||
OrderType OrderType `json:"ord_type"`
|
||||
Price fixedpoint.Value `json:"price,omitempty"`
|
||||
|
@ -113,64 +112,7 @@ type Order struct {
|
|||
TradesCount int64 `json:"trades_count,omitempty"`
|
||||
GroupID uint32 `json:"group_id,omitempty"`
|
||||
ClientOID string `json:"client_oid,omitempty"`
|
||||
CreatedAt time.Time `json:"-"`
|
||||
CreatedAtMs types.MillisecondTimestamp `json:"created_at_in_ms,omitempty"`
|
||||
InsertedAt time.Time `json:"-"`
|
||||
}
|
||||
|
||||
// Open returns open orders
|
||||
func (s *OrderService) Closed(market string, options QueryOrderOptions) ([]Order, error) {
|
||||
req := s.NewGetOrdersRequest()
|
||||
req.Market(market)
|
||||
req.State([]OrderState{OrderStateDone, OrderStateCancel})
|
||||
|
||||
if options.GroupID > 0 {
|
||||
req.GroupID(uint32(options.GroupID))
|
||||
}
|
||||
if options.Offset > 0 {
|
||||
req.Offset(options.Offset)
|
||||
}
|
||||
if options.Limit > 0 {
|
||||
req.Limit(options.Limit)
|
||||
}
|
||||
|
||||
if options.Page > 0 {
|
||||
req.Page(options.Page)
|
||||
}
|
||||
|
||||
if len(options.OrderBy) > 0 {
|
||||
req.OrderBy(options.OrderBy)
|
||||
}
|
||||
|
||||
return req.Do(context.Background())
|
||||
}
|
||||
|
||||
// Open returns open orders
|
||||
func (s *OrderService) Open(market string, options QueryOrderOptions) ([]Order, error) {
|
||||
req := s.NewGetOrdersRequest()
|
||||
req.Market(market)
|
||||
// state default ot wait and convert
|
||||
|
||||
if options.GroupID > 0 {
|
||||
req.GroupID(uint32(options.GroupID))
|
||||
}
|
||||
|
||||
return req.Do(context.Background())
|
||||
}
|
||||
|
||||
//go:generate GetRequest -url "v2/orders/history" -type GetOrderHistoryRequest -responseType []Order
|
||||
type GetOrderHistoryRequest struct {
|
||||
client requestgen.AuthenticatedAPIClient
|
||||
|
||||
market string `param:"market"`
|
||||
fromID *uint64 `param:"from_id"`
|
||||
limit *uint `param:"limit"`
|
||||
}
|
||||
|
||||
func (s *OrderService) NewGetOrderHistoryRequest() *GetOrderHistoryRequest {
|
||||
return &GetOrderHistoryRequest{
|
||||
client: s.client,
|
||||
}
|
||||
CreatedAt types.MillisecondTimestamp `json:"created_at"`
|
||||
}
|
||||
|
||||
//go:generate GetRequest -url "v2/orders" -type GetOrdersRequest -responseType []Order
|
||||
|
|
|
@ -73,24 +73,6 @@ func TestOrderService_GetOrdersRequest_SingleState(t *testing.T) {
|
|||
assert.NotNil(t, orders)
|
||||
}
|
||||
|
||||
func TestOrderService_GetOrderHistoryRequest(t *testing.T) {
|
||||
key, secret, ok := integrationTestConfigured(t, "MAX")
|
||||
if !ok {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
client := NewRestClient(ProductionAPIURL)
|
||||
client.Auth(key, secret)
|
||||
|
||||
req := client.OrderService.NewGetOrderHistoryRequest()
|
||||
req.Market("btcusdt")
|
||||
req.FromID(1)
|
||||
orders, err := req.Do(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, orders)
|
||||
}
|
||||
|
||||
func TestOrderService(t *testing.T) {
|
||||
key, secret, ok := integrationTestConfigured(t, "MAX")
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/c9s/requestgen"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -26,20 +27,23 @@ type TradeInfo struct {
|
|||
Ask *MarkerInfo `json:"ask,omitempty"`
|
||||
}
|
||||
|
||||
type Liquidity string
|
||||
|
||||
// Trade represents one returned trade on the max platform.
|
||||
type Trade struct {
|
||||
ID uint64 `json:"id" db:"exchange_id"`
|
||||
Price string `json:"price" db:"price"`
|
||||
Volume string `json:"volume" db:"volume"`
|
||||
Funds string `json:"funds"`
|
||||
Market string `json:"market" db:"market"`
|
||||
WalletType WalletType `json:"wallet_type,omitempty"`
|
||||
Price fixedpoint.Value `json:"price"`
|
||||
Volume fixedpoint.Value `json:"volume"`
|
||||
Funds fixedpoint.Value `json:"funds"`
|
||||
Market string `json:"market"`
|
||||
MarketName string `json:"market_name"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedAtMilliSeconds types.MillisecondTimestamp `json:"created_at_in_ms"`
|
||||
Side string `json:"side" db:"side"`
|
||||
CreatedAt types.MillisecondTimestamp `json:"created_at"`
|
||||
Side string `json:"side"`
|
||||
OrderID uint64 `json:"order_id"`
|
||||
Fee string `json:"fee" db:"fee"` // float number as string
|
||||
FeeCurrency string `json:"fee_currency" db:"fee_currency"`
|
||||
Fee fixedpoint.Value `json:"fee"` // float number as string
|
||||
FeeCurrency string `json:"fee_currency"`
|
||||
Liquidity Liquidity `json:"liquidity"`
|
||||
Info TradeInfo `json:"info,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -148,4 +152,3 @@ type GetPrivateTradesRequest struct {
|
|||
|
||||
offset *int64 `param:"offset"`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
package max
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTradeService(t *testing.T) {
|
||||
key, secret, ok := integrationTestConfigured(t, "MAX")
|
||||
if !ok {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
client := NewRestClient(ProductionAPIURL)
|
||||
client.Auth(key, secret)
|
||||
|
||||
t.Run("default timestamp", func(t *testing.T) {
|
||||
req := client.TradeService.NewGetPrivateTradeRequest()
|
||||
until := time.Now().AddDate(0, -6, 0)
|
||||
|
||||
trades, err := req.Market("btcusdt").
|
||||
Timestamp(until).
|
||||
Do(ctx)
|
||||
if assert.NoError(t, err) {
|
||||
assert.NotEmptyf(t, trades, "got %d trades", len(trades))
|
||||
for _, td := range trades {
|
||||
t.Logf("trade: %+v", td)
|
||||
assert.True(t, td.CreatedAtMilliSeconds.Time().Before(until))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("desc and pagination = false", func(t *testing.T) {
|
||||
req := client.TradeService.NewGetPrivateTradeRequest()
|
||||
trades, err := req.Market("btcusdt").
|
||||
Pagination(false).
|
||||
OrderBy("asc").
|
||||
Do(ctx)
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
assert.NotEmptyf(t, trades, "got %d trades", len(trades))
|
||||
for _, td := range trades {
|
||||
t.Logf("trade: %+v", td)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/valyala/fastjson"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
|
@ -23,16 +24,16 @@ type OrderUpdate struct {
|
|||
Side string `json:"sd"`
|
||||
OrderType OrderType `json:"ot"`
|
||||
|
||||
Price string `json:"p"`
|
||||
StopPrice string `json:"sp"`
|
||||
Price fixedpoint.Value `json:"p"`
|
||||
StopPrice fixedpoint.Value `json:"sp"`
|
||||
|
||||
Volume string `json:"v"`
|
||||
AveragePrice string `json:"ap"`
|
||||
Volume fixedpoint.Value `json:"v"`
|
||||
AveragePrice fixedpoint.Value `json:"ap"`
|
||||
State OrderState `json:"S"`
|
||||
Market string `json:"M"`
|
||||
|
||||
RemainingVolume string `json:"rv"`
|
||||
ExecutedVolume string `json:"ev"`
|
||||
RemainingVolume fixedpoint.Value `json:"rv"`
|
||||
ExecutedVolume fixedpoint.Value `json:"ev"`
|
||||
|
||||
TradesCount int64 `json:"tc"`
|
||||
|
||||
|
@ -48,36 +49,20 @@ type OrderUpdateEvent struct {
|
|||
Orders []OrderUpdate `json:"o"`
|
||||
}
|
||||
|
||||
func parserOrderUpdate(v *fastjson.Value) OrderUpdate {
|
||||
return OrderUpdate{
|
||||
Event: string(v.GetStringBytes("e")),
|
||||
ID: v.GetUint64("i"),
|
||||
Side: string(v.GetStringBytes("sd")),
|
||||
Market: string(v.GetStringBytes("M")),
|
||||
OrderType: OrderType(v.GetStringBytes("ot")),
|
||||
State: OrderState(v.GetStringBytes("S")),
|
||||
Price: string(v.GetStringBytes("p")),
|
||||
StopPrice: string(v.GetStringBytes("sp")),
|
||||
AveragePrice: string(v.GetStringBytes("ap")),
|
||||
Volume: string(v.GetStringBytes("v")),
|
||||
RemainingVolume: string(v.GetStringBytes("rv")),
|
||||
ExecutedVolume: string(v.GetStringBytes("ev")),
|
||||
TradesCount: v.GetInt64("tc"),
|
||||
GroupID: uint32(v.GetInt("gi")),
|
||||
ClientOID: string(v.GetStringBytes("ci")),
|
||||
CreatedAtMs: v.GetInt64("T"),
|
||||
UpdateTime: v.GetInt64("TU"),
|
||||
}
|
||||
}
|
||||
|
||||
func parseOrderUpdateEvent(v *fastjson.Value) *OrderUpdateEvent {
|
||||
var e OrderUpdateEvent
|
||||
e.Event = string(v.GetStringBytes("e"))
|
||||
e.Timestamp = v.GetInt64("T")
|
||||
|
||||
for _, ov := range v.GetArray("o") {
|
||||
o := parserOrderUpdate(ov)
|
||||
e.Orders = append(e.Orders, o)
|
||||
var o = ov.String()
|
||||
var u OrderUpdate
|
||||
if err := json.Unmarshal([]byte(o), &u); err != nil {
|
||||
log.WithError(err).Error("parse error")
|
||||
continue
|
||||
}
|
||||
|
||||
e.Orders = append(e.Orders, u)
|
||||
}
|
||||
|
||||
return &e
|
||||
|
@ -95,8 +80,14 @@ func parserOrderSnapshotEvent(v *fastjson.Value) *OrderSnapshotEvent {
|
|||
e.Timestamp = v.GetInt64("T")
|
||||
|
||||
for _, ov := range v.GetArray("o") {
|
||||
o := parserOrderUpdate(ov)
|
||||
e.Orders = append(e.Orders, o)
|
||||
var o = ov.String()
|
||||
var u OrderUpdate
|
||||
if err := json.Unmarshal([]byte(o), &u); err != nil {
|
||||
log.WithError(err).Error("parse error")
|
||||
continue
|
||||
}
|
||||
|
||||
e.Orders = append(e.Orders, u)
|
||||
}
|
||||
|
||||
return &e
|
||||
|
|
|
@ -5,6 +5,8 @@ package v3
|
|||
//go:generate -command DeleteRequest requestgen -method DELETE
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/c9s/requestgen"
|
||||
|
||||
maxapi "github.com/c9s/bbgo/pkg/exchange/max/maxapi"
|
||||
|
@ -35,6 +37,10 @@ func (s *OrderService) NewWalletOrderCancelAllRequest(walletType WalletType) *Wa
|
|||
return &WalletOrderCancelAllRequest{client: s.Client, walletType: walletType}
|
||||
}
|
||||
|
||||
func (s *OrderService) NewWalletGetTradesRequest(walletType WalletType) *WalletGetTradesRequest {
|
||||
return &WalletGetTradesRequest{client: s.Client, walletType: walletType}
|
||||
}
|
||||
|
||||
func (s *OrderService) NewOrderCancelRequest() *OrderCancelRequest {
|
||||
return &OrderCancelRequest{client: s.Client}
|
||||
}
|
||||
|
@ -88,6 +94,21 @@ type WalletOrderCancelAllRequest struct {
|
|||
groupID *uint32 `param:"groupID"`
|
||||
}
|
||||
|
||||
type Trade = maxapi.Trade
|
||||
|
||||
//go:generate GetRequest -url "/api/v3/wallet/:walletType/trades" -type WalletGetTradesRequest -responseType []Trade
|
||||
type WalletGetTradesRequest struct {
|
||||
client requestgen.AuthenticatedAPIClient
|
||||
|
||||
walletType WalletType `param:"walletType,slug,required"`
|
||||
|
||||
market string `param:"market,required"`
|
||||
from *uint64 `param:"from_id"`
|
||||
startTime *time.Time `param:"start_time,milliseconds"`
|
||||
endTime *time.Time `param:"end_time,milliseconds"`
|
||||
limit *uint64 `param:"limit"`
|
||||
}
|
||||
|
||||
//go:generate PostRequest -url "/api/v3/order" -type OrderCancelRequest -responseType .Order
|
||||
type OrderCancelRequest struct {
|
||||
client requestgen.AuthenticatedAPIClient
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
// Code generated by "requestgen -method GET -url /api/v3/wallet/:walletType/trades -type WalletGetTradesRequest -responseType []Trade"; DO NOT EDIT.
|
||||
|
||||
package v3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/exchange/max/maxapi"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (w *WalletGetTradesRequest) Market(market string) *WalletGetTradesRequest {
|
||||
w.market = market
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) From(from uint64) *WalletGetTradesRequest {
|
||||
w.from = &from
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) StartTime(startTime time.Time) *WalletGetTradesRequest {
|
||||
w.startTime = &startTime
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) EndTime(endTime time.Time) *WalletGetTradesRequest {
|
||||
w.endTime = &endTime
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) Limit(limit uint64) *WalletGetTradesRequest {
|
||||
w.limit = &limit
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) WalletType(walletType max.WalletType) *WalletGetTradesRequest {
|
||||
w.walletType = walletType
|
||||
return w
|
||||
}
|
||||
|
||||
// GetQueryParameters builds and checks the query parameters and returns url.Values
|
||||
func (w *WalletGetTradesRequest) GetQueryParameters() (url.Values, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
query := url.Values{}
|
||||
for _k, _v := range params {
|
||||
query.Add(_k, fmt.Sprintf("%v", _v))
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParameters builds and checks the parameters and return the result in a map object
|
||||
func (w *WalletGetTradesRequest) GetParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
// check market field -> json key market
|
||||
market := w.market
|
||||
|
||||
// TEMPLATE check-required
|
||||
if len(market) == 0 {
|
||||
return nil, fmt.Errorf("market is required, empty string given")
|
||||
}
|
||||
// END TEMPLATE check-required
|
||||
|
||||
// assign parameter of market
|
||||
params["market"] = market
|
||||
// check from field -> json key from_id
|
||||
if w.from != nil {
|
||||
from := *w.from
|
||||
|
||||
// assign parameter of from
|
||||
params["from_id"] = from
|
||||
} else {
|
||||
}
|
||||
// check startTime field -> json key start_time
|
||||
if w.startTime != nil {
|
||||
startTime := *w.startTime
|
||||
|
||||
// assign parameter of startTime
|
||||
// convert time.Time to milliseconds time stamp
|
||||
params["start_time"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
|
||||
} else {
|
||||
}
|
||||
// check endTime field -> json key end_time
|
||||
if w.endTime != nil {
|
||||
endTime := *w.endTime
|
||||
|
||||
// assign parameter of endTime
|
||||
// convert time.Time to milliseconds time stamp
|
||||
params["end_time"] = strconv.FormatInt(endTime.UnixNano()/int64(time.Millisecond), 10)
|
||||
} else {
|
||||
}
|
||||
// check limit field -> json key limit
|
||||
if w.limit != nil {
|
||||
limit := *w.limit
|
||||
|
||||
// assign parameter of limit
|
||||
params["limit"] = limit
|
||||
} else {
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
|
||||
func (w *WalletGetTradesRequest) GetParametersQuery() (url.Values, error) {
|
||||
query := url.Values{}
|
||||
|
||||
params, err := w.GetParameters()
|
||||
if err != nil {
|
||||
return query, err
|
||||
}
|
||||
|
||||
for _k, _v := range params {
|
||||
if w.isVarSlice(_v) {
|
||||
w.iterateSlice(_v, func(it interface{}) {
|
||||
query.Add(_k+"[]", fmt.Sprintf("%v", it))
|
||||
})
|
||||
} else {
|
||||
query.Add(_k, fmt.Sprintf("%v", _v))
|
||||
}
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParametersJSON converts the parameters from GetParameters into the JSON format
|
||||
func (w *WalletGetTradesRequest) GetParametersJSON() ([]byte, error) {
|
||||
params, err := w.GetParameters()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(params)
|
||||
}
|
||||
|
||||
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
|
||||
func (w *WalletGetTradesRequest) GetSlugParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
// check walletType field -> json key walletType
|
||||
walletType := w.walletType
|
||||
|
||||
// TEMPLATE check-required
|
||||
if len(walletType) == 0 {
|
||||
return nil, fmt.Errorf("walletType is required, empty string given")
|
||||
}
|
||||
// END TEMPLATE check-required
|
||||
|
||||
// assign parameter of walletType
|
||||
params["walletType"] = walletType
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) applySlugsToUrl(url string, slugs map[string]string) string {
|
||||
for _k, _v := range slugs {
|
||||
needleRE := regexp.MustCompile(":" + _k + "\\b")
|
||||
url = needleRE.ReplaceAllString(url, _v)
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
|
||||
sliceValue := reflect.ValueOf(slice)
|
||||
for _i := 0; _i < sliceValue.Len(); _i++ {
|
||||
it := sliceValue.Index(_i).Interface()
|
||||
_f(it)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) isVarSlice(_v interface{}) bool {
|
||||
rt := reflect.TypeOf(_v)
|
||||
switch rt.Kind() {
|
||||
case reflect.Slice:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) GetSlugsMap() (map[string]string, error) {
|
||||
slugs := map[string]string{}
|
||||
params, err := w.GetSlugParameters()
|
||||
if err != nil {
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
for _k, _v := range params {
|
||||
slugs[_k] = fmt.Sprintf("%v", _v)
|
||||
}
|
||||
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
func (w *WalletGetTradesRequest) Do(ctx context.Context) ([]max.Trade, error) {
|
||||
|
||||
// empty params for GET operation
|
||||
var params interface{}
|
||||
query, err := w.GetParametersQuery()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
apiURL := "/api/v3/wallet/:walletType/trades"
|
||||
slugs, err := w.GetSlugsMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
apiURL = w.applySlugsToUrl(apiURL, slugs)
|
||||
|
||||
req, err := w.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := w.client.SendRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var apiResponse []max.Trade
|
||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apiResponse, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user