mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 14:55:16 +00:00
Merge pull request #636 from c9s/feature/max-margin-wallet
fix: max: fix trades/orders parsing
This commit is contained in:
commit
5c5a88fe0e
|
@ -4,6 +4,28 @@ sessions:
|
||||||
exchange: max
|
exchange: max
|
||||||
margin: true
|
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:
|
exchangeStrategies:
|
||||||
|
|
||||||
- on: max_margin
|
- 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) {
|
func toGlobalOrder(maxOrder max.Order) (*types.Order, error) {
|
||||||
executedVolume := maxOrder.ExecutedVolume
|
executedVolume := maxOrder.ExecutedVolume
|
||||||
remainingVolume := maxOrder.RemainingVolume
|
remainingVolume := maxOrder.RemainingVolume
|
||||||
|
isMargin := maxOrder.WalletType == max.WalletTypeMargin
|
||||||
|
|
||||||
return &types.Order{
|
return &types.Order{
|
||||||
SubmitOrder: types.SubmitOrder{
|
SubmitOrder: types.SubmitOrder{
|
||||||
|
@ -185,52 +186,33 @@ func toGlobalOrder(maxOrder max.Order) (*types.Order, error) {
|
||||||
OrderID: maxOrder.ID,
|
OrderID: maxOrder.ID,
|
||||||
Status: toGlobalOrderStatus(maxOrder.State, executedVolume, remainingVolume),
|
Status: toGlobalOrderStatus(maxOrder.State, executedVolume, remainingVolume),
|
||||||
ExecutedQuantity: executedVolume,
|
ExecutedQuantity: executedVolume,
|
||||||
CreationTime: types.Time(maxOrder.CreatedAtMs.Time()),
|
CreationTime: types.Time(maxOrder.CreatedAt.Time()),
|
||||||
UpdateTime: types.Time(maxOrder.CreatedAtMs.Time()),
|
UpdateTime: types.Time(maxOrder.CreatedAt.Time()),
|
||||||
|
IsMargin: isMargin,
|
||||||
|
IsIsolated: false, // isolated margin is not supported
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toGlobalTrade(t max.Trade) (*types.Trade, error) {
|
func toGlobalTrade(t max.Trade) (*types.Trade, error) {
|
||||||
// skip trade ID that is the same. however this should not happen
|
isMargin := t.WalletType == max.WalletTypeMargin
|
||||||
var side = toGlobalSideType(t.Side)
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.Trade{
|
return &types.Trade{
|
||||||
ID: t.ID,
|
ID: t.ID,
|
||||||
OrderID: t.OrderID,
|
OrderID: t.OrderID,
|
||||||
Price: price,
|
Price: t.Price,
|
||||||
Symbol: toGlobalSymbol(t.Market),
|
Symbol: toGlobalSymbol(t.Market),
|
||||||
Exchange: "max",
|
Exchange: types.ExchangeMax,
|
||||||
Quantity: quantity,
|
Quantity: t.Volume,
|
||||||
Side: side,
|
Side: side,
|
||||||
IsBuyer: t.IsBuyer(),
|
IsBuyer: t.IsBuyer(),
|
||||||
IsMaker: t.IsMaker(),
|
IsMaker: t.IsMaker(),
|
||||||
Fee: fee,
|
Fee: t.Fee,
|
||||||
FeeCurrency: toGlobalCurrency(t.FeeCurrency),
|
FeeCurrency: toGlobalCurrency(t.FeeCurrency),
|
||||||
QuoteQuantity: quoteQuantity,
|
QuoteQuantity: t.Funds,
|
||||||
Time: types.Time(mts),
|
Time: types.Time(t.CreatedAt),
|
||||||
|
IsMargin: isMargin,
|
||||||
|
IsIsolated: false,
|
||||||
|
IsFutures: false,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,16 +279,6 @@ func convertWebSocketTrade(t max.TradeUpdate) (*types.Trade, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertWebSocketOrderUpdate(u max.OrderUpdate) (*types.Order, 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
|
timeInForce := types.TimeInForceGTC
|
||||||
if u.OrderType == max.OrderTypeIOCLimit {
|
if u.OrderType == max.OrderTypeIOCLimit {
|
||||||
timeInForce = types.TimeInForceIOC
|
timeInForce = types.TimeInForceIOC
|
||||||
|
@ -318,16 +290,16 @@ func convertWebSocketOrderUpdate(u max.OrderUpdate) (*types.Order, error) {
|
||||||
Symbol: toGlobalSymbol(u.Market),
|
Symbol: toGlobalSymbol(u.Market),
|
||||||
Side: toGlobalSideType(u.Side),
|
Side: toGlobalSideType(u.Side),
|
||||||
Type: toGlobalOrderType(u.OrderType),
|
Type: toGlobalOrderType(u.OrderType),
|
||||||
Quantity: fixedpoint.MustNewFromString(u.Volume),
|
Quantity: u.Volume,
|
||||||
Price: fixedpoint.MustNewFromString(u.Price),
|
Price: u.Price,
|
||||||
StopPrice: fixedpoint.MustNewFromString(u.StopPrice),
|
StopPrice: u.StopPrice,
|
||||||
TimeInForce: timeInForce, // MAX only supports GTC
|
TimeInForce: timeInForce, // MAX only supports GTC
|
||||||
GroupID: u.GroupID,
|
GroupID: u.GroupID,
|
||||||
},
|
},
|
||||||
Exchange: types.ExchangeMax,
|
Exchange: types.ExchangeMax,
|
||||||
OrderID: u.ID,
|
OrderID: u.ID,
|
||||||
Status: toGlobalOrderStatus(u.State, executedVolume, remainingVolume),
|
Status: toGlobalOrderStatus(u.State, u.ExecutedVolume, u.RemainingVolume),
|
||||||
ExecutedQuantity: executedVolume,
|
ExecutedQuantity: u.ExecutedVolume,
|
||||||
CreationTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
|
CreationTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
|
||||||
UpdateTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
|
UpdateTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -34,7 +34,8 @@ type Exchange struct {
|
||||||
key, secret string
|
key, secret string
|
||||||
client *maxapi.RestClient
|
client *maxapi.RestClient
|
||||||
|
|
||||||
v3order *v3.OrderService
|
v3order *v3.OrderService
|
||||||
|
v3margin *v3.MarginService
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(key, secret string) *Exchange {
|
func New(key, secret string) *Exchange {
|
||||||
|
@ -46,10 +47,11 @@ func New(key, secret string) *Exchange {
|
||||||
client := maxapi.NewRestClient(baseURL)
|
client := maxapi.NewRestClient(baseURL)
|
||||||
client.Auth(key, secret)
|
client.Auth(key, secret)
|
||||||
return &Exchange{
|
return &Exchange{
|
||||||
client: client,
|
client: client,
|
||||||
key: key,
|
key: key,
|
||||||
secret: secret,
|
secret: secret,
|
||||||
v3order: &v3.OrderService{Client: client},
|
v3order: &v3.OrderService{Client: client},
|
||||||
|
v3margin: &v3.MarginService{Client: client},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,11 +575,27 @@ func (e *Exchange) QueryAccount(ctx context.Context) (*types.Account, error) {
|
||||||
// MAX returns the fee rate in the following format:
|
// MAX returns the fee rate in the following format:
|
||||||
// "maker_fee": 0.0005 -> 0.05%
|
// "maker_fee": 0.0005 -> 0.05%
|
||||||
// "taker_fee": 0.0015 -> 0.15%
|
// "taker_fee": 0.0015 -> 0.15%
|
||||||
|
|
||||||
a := &types.Account{
|
a := &types.Account{
|
||||||
|
AccountType: types.AccountTypeSpot,
|
||||||
|
MarginLevel: fixedpoint.Zero,
|
||||||
MakerFeeRate: fixedpoint.NewFromFloat(vipLevel.Current.MakerFee), // 0.15% = 0.0015
|
MakerFeeRate: fixedpoint.NewFromFloat(vipLevel.Current.MakerFee), // 0.15% = 0.0015
|
||||||
TakerFeeRate: fixedpoint.NewFromFloat(vipLevel.Current.TakerFee), // 0.15% = 0.0015
|
TakerFeeRate: fixedpoint.NewFromFloat(vipLevel.Current.TakerFee), // 0.15% = 0.0015
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.MarginSettings.IsMargin {
|
||||||
|
a.AccountType = types.AccountTypeMargin
|
||||||
|
|
||||||
|
req := e.v3margin.NewGetMarginADRatioRequest()
|
||||||
|
adRatio, err := req.Do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.MarginLevel = adRatio.AdRatio
|
||||||
|
a.TotalAccountValue = adRatio.AssetInUsdt
|
||||||
|
}
|
||||||
|
|
||||||
a.UpdateBalances(balances)
|
a.UpdateBalances(balances)
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -773,11 +791,17 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req := e.client.TradeService.NewGetPrivateTradeRequest()
|
market := toLocalSymbol(symbol)
|
||||||
req.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 {
|
if options.Limit > 0 {
|
||||||
req.Limit(options.Limit)
|
req.Limit(uint64(options.Limit))
|
||||||
} else {
|
} else {
|
||||||
req.Limit(1000)
|
req.Limit(1000)
|
||||||
}
|
}
|
||||||
|
@ -785,12 +809,9 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
|
||||||
// MAX uses exclusive last trade ID
|
// MAX uses exclusive last trade ID
|
||||||
// the timestamp parameter is used for reverse order, we can't use it.
|
// the timestamp parameter is used for reverse order, we can't use it.
|
||||||
if options.LastTradeID > 0 {
|
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)
|
maxTrades, err := req.Do(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/c9s/requestgen"
|
"github.com/c9s/requestgen"
|
||||||
"github.com/pkg/errors"
|
"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.
|
// Order represents one returned order (POST order/GET order/GET orders) on the max platform.
|
||||||
type Order struct {
|
type Order struct {
|
||||||
ID uint64 `json:"id,omitempty"`
|
ID uint64 `json:"id,omitempty"`
|
||||||
WalletType string `json:"wallet_type,omitempty"`
|
WalletType WalletType `json:"wallet_type,omitempty"`
|
||||||
Side string `json:"side"`
|
Side string `json:"side"`
|
||||||
OrderType OrderType `json:"ord_type"`
|
OrderType OrderType `json:"ord_type"`
|
||||||
Price fixedpoint.Value `json:"price,omitempty"`
|
Price fixedpoint.Value `json:"price,omitempty"`
|
||||||
|
@ -113,64 +112,7 @@ type Order struct {
|
||||||
TradesCount int64 `json:"trades_count,omitempty"`
|
TradesCount int64 `json:"trades_count,omitempty"`
|
||||||
GroupID uint32 `json:"group_id,omitempty"`
|
GroupID uint32 `json:"group_id,omitempty"`
|
||||||
ClientOID string `json:"client_oid,omitempty"`
|
ClientOID string `json:"client_oid,omitempty"`
|
||||||
CreatedAt time.Time `json:"-"`
|
CreatedAt types.MillisecondTimestamp `json:"created_at"`
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate GetRequest -url "v2/orders" -type GetOrdersRequest -responseType []Order
|
//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)
|
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) {
|
func TestOrderService(t *testing.T) {
|
||||||
key, secret, ok := integrationTestConfigured(t, "MAX")
|
key, secret, ok := integrationTestConfigured(t, "MAX")
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/c9s/requestgen"
|
"github.com/c9s/requestgen"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,21 +27,24 @@ type TradeInfo struct {
|
||||||
Ask *MarkerInfo `json:"ask,omitempty"`
|
Ask *MarkerInfo `json:"ask,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Liquidity string
|
||||||
|
|
||||||
// Trade represents one returned trade on the max platform.
|
// Trade represents one returned trade on the max platform.
|
||||||
type Trade struct {
|
type Trade struct {
|
||||||
ID uint64 `json:"id" db:"exchange_id"`
|
ID uint64 `json:"id" db:"exchange_id"`
|
||||||
Price string `json:"price" db:"price"`
|
WalletType WalletType `json:"wallet_type,omitempty"`
|
||||||
Volume string `json:"volume" db:"volume"`
|
Price fixedpoint.Value `json:"price"`
|
||||||
Funds string `json:"funds"`
|
Volume fixedpoint.Value `json:"volume"`
|
||||||
Market string `json:"market" db:"market"`
|
Funds fixedpoint.Value `json:"funds"`
|
||||||
MarketName string `json:"market_name"`
|
Market string `json:"market"`
|
||||||
CreatedAt int64 `json:"created_at"`
|
MarketName string `json:"market_name"`
|
||||||
CreatedAtMilliSeconds types.MillisecondTimestamp `json:"created_at_in_ms"`
|
CreatedAt types.MillisecondTimestamp `json:"created_at"`
|
||||||
Side string `json:"side" db:"side"`
|
Side string `json:"side"`
|
||||||
OrderID uint64 `json:"order_id"`
|
OrderID uint64 `json:"order_id"`
|
||||||
Fee string `json:"fee" db:"fee"` // float number as string
|
Fee fixedpoint.Value `json:"fee"` // float number as string
|
||||||
FeeCurrency string `json:"fee_currency" db:"fee_currency"`
|
FeeCurrency string `json:"fee_currency"`
|
||||||
Info TradeInfo `json:"info,omitempty"`
|
Liquidity Liquidity `json:"liquidity"`
|
||||||
|
Info TradeInfo `json:"info,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Trade) IsBuyer() bool {
|
func (t Trade) IsBuyer() bool {
|
||||||
|
@ -148,4 +152,3 @@ type GetPrivateTradesRequest struct {
|
||||||
|
|
||||||
offset *int64 `param:"offset"`
|
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"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/valyala/fastjson"
|
"github.com/valyala/fastjson"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
@ -23,16 +24,16 @@ type OrderUpdate struct {
|
||||||
Side string `json:"sd"`
|
Side string `json:"sd"`
|
||||||
OrderType OrderType `json:"ot"`
|
OrderType OrderType `json:"ot"`
|
||||||
|
|
||||||
Price string `json:"p"`
|
Price fixedpoint.Value `json:"p"`
|
||||||
StopPrice string `json:"sp"`
|
StopPrice fixedpoint.Value `json:"sp"`
|
||||||
|
|
||||||
Volume string `json:"v"`
|
Volume fixedpoint.Value `json:"v"`
|
||||||
AveragePrice string `json:"ap"`
|
AveragePrice fixedpoint.Value `json:"ap"`
|
||||||
State OrderState `json:"S"`
|
State OrderState `json:"S"`
|
||||||
Market string `json:"M"`
|
Market string `json:"M"`
|
||||||
|
|
||||||
RemainingVolume string `json:"rv"`
|
RemainingVolume fixedpoint.Value `json:"rv"`
|
||||||
ExecutedVolume string `json:"ev"`
|
ExecutedVolume fixedpoint.Value `json:"ev"`
|
||||||
|
|
||||||
TradesCount int64 `json:"tc"`
|
TradesCount int64 `json:"tc"`
|
||||||
|
|
||||||
|
@ -48,36 +49,20 @@ type OrderUpdateEvent struct {
|
||||||
Orders []OrderUpdate `json:"o"`
|
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 {
|
func parseOrderUpdateEvent(v *fastjson.Value) *OrderUpdateEvent {
|
||||||
var e OrderUpdateEvent
|
var e OrderUpdateEvent
|
||||||
e.Event = string(v.GetStringBytes("e"))
|
e.Event = string(v.GetStringBytes("e"))
|
||||||
e.Timestamp = v.GetInt64("T")
|
e.Timestamp = v.GetInt64("T")
|
||||||
|
|
||||||
for _, ov := range v.GetArray("o") {
|
for _, ov := range v.GetArray("o") {
|
||||||
o := parserOrderUpdate(ov)
|
var o = ov.String()
|
||||||
e.Orders = append(e.Orders, o)
|
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
|
return &e
|
||||||
|
@ -95,8 +80,14 @@ func parserOrderSnapshotEvent(v *fastjson.Value) *OrderSnapshotEvent {
|
||||||
e.Timestamp = v.GetInt64("T")
|
e.Timestamp = v.GetInt64("T")
|
||||||
|
|
||||||
for _, ov := range v.GetArray("o") {
|
for _, ov := range v.GetArray("o") {
|
||||||
o := parserOrderUpdate(ov)
|
var o = ov.String()
|
||||||
e.Orders = append(e.Orders, o)
|
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
|
return &e
|
||||||
|
|
|
@ -5,6 +5,8 @@ package v3
|
||||||
//go:generate -command DeleteRequest requestgen -method DELETE
|
//go:generate -command DeleteRequest requestgen -method DELETE
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/c9s/requestgen"
|
"github.com/c9s/requestgen"
|
||||||
|
|
||||||
maxapi "github.com/c9s/bbgo/pkg/exchange/max/maxapi"
|
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}
|
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 {
|
func (s *OrderService) NewOrderCancelRequest() *OrderCancelRequest {
|
||||||
return &OrderCancelRequest{client: s.Client}
|
return &OrderCancelRequest{client: s.Client}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +94,21 @@ type WalletOrderCancelAllRequest struct {
|
||||||
groupID *uint32 `param:"groupID"`
|
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
|
//go:generate PostRequest -url "/api/v3/order" -type OrderCancelRequest -responseType .Order
|
||||||
type OrderCancelRequest struct {
|
type OrderCancelRequest struct {
|
||||||
client requestgen.AuthenticatedAPIClient
|
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