From 96362a49362053f241d94d176c8dd764c6ee34df Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 22 Feb 2021 18:45:44 +0800 Subject: [PATCH] max: add rewards api and example --- examples/max-rewards/main.go | 45 +++++++++ pkg/exchange/max/maxapi/datatype.go | 22 +++++ pkg/exchange/max/maxapi/restapi.go | 6 +- pkg/exchange/max/maxapi/reward.go | 145 ++++++++++++++++++++++++++++ pkg/exchange/max/maxapi/trade.go | 119 +++++++++++------------ 5 files changed, 271 insertions(+), 66 deletions(-) create mode 100644 examples/max-rewards/main.go create mode 100644 pkg/exchange/max/maxapi/datatype.go create mode 100644 pkg/exchange/max/maxapi/reward.go diff --git a/examples/max-rewards/main.go b/examples/max-rewards/main.go new file mode 100644 index 0000000000..dbe978db0f --- /dev/null +++ b/examples/max-rewards/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "context" + "log" + "os" + + maxapi "github.com/c9s/bbgo/pkg/exchange/max/maxapi" +) + +func main() { + + key := os.Getenv("MAX_API_KEY") + secret := os.Getenv("MAX_API_SECRET") + + api := maxapi.NewRestClient(maxapi.ProductionAPIURL) + api.Auth(key, secret) + + ctx := context.Background() + + var req *maxapi.RewardsRequest + + if len(os.Args) > 1 { + pathType := os.Args[1] + rewardType, err := maxapi.ParseRewardType(pathType) + if err != nil { + log.Fatal(err) + } + + req = api.RewardService.NewRewardsByTypeRequest(rewardType) + } else { + req = api.RewardService.NewRewardsRequest() + } + // req.From(1613931192) + // req.From(1613240048) + + rewards, err := req.Do(ctx) + if err != nil { + log.Fatal(err) + } + + for _, reward := range rewards { + log.Printf("%+v\n", reward) + } +} diff --git a/pkg/exchange/max/maxapi/datatype.go b/pkg/exchange/max/maxapi/datatype.go new file mode 100644 index 0000000000..bef497e243 --- /dev/null +++ b/pkg/exchange/max/maxapi/datatype.go @@ -0,0 +1,22 @@ +package max + +import ( + "encoding/json" + "time" +) + +type Timestamp time.Time + +func (t Timestamp) String() string { + return time.Time(t).String() +} + +func (t *Timestamp) UnmarshalJSON(o []byte) error { + var timestamp int64 + if err := json.Unmarshal(o, ×tamp); err != nil { + return err + } + + *t = Timestamp(time.Unix(timestamp, 0)) + return nil +} diff --git a/pkg/exchange/max/maxapi/restapi.go b/pkg/exchange/max/maxapi/restapi.go index 1effd73cf7..77b53c2862 100644 --- a/pkg/exchange/max/maxapi/restapi.go +++ b/pkg/exchange/max/maxapi/restapi.go @@ -62,6 +62,7 @@ type RestClient struct { PublicService *PublicService TradeService *TradeService OrderService *OrderService + RewardService *RewardService // OrderBookService *OrderBookService // MaxTokenService *MaxTokenService // MaxKLineService *KLineService @@ -83,10 +84,9 @@ func NewRestClientWithHttpClient(baseURL string, httpClient *http.Client) *RestC client.TradeService = &TradeService{client} client.PublicService = &PublicService{client} client.OrderService = &OrderService{client} - // client.OrderBookService = &OrderBookService{client} + client.RewardService = &RewardService{client} + // client.MaxTokenService = &MaxTokenService{client} - // client.MaxKLineService = &KLineService{client} - // client.CreditService = &CreditService{client} client.initNonce() return client } diff --git a/pkg/exchange/max/maxapi/reward.go b/pkg/exchange/max/maxapi/reward.go new file mode 100644 index 0000000000..06038aa7c1 --- /dev/null +++ b/pkg/exchange/max/maxapi/reward.go @@ -0,0 +1,145 @@ +package max + +import ( + "context" + "encoding/json" + "fmt" +) + +type RewardType string + +const ( + RewardAirdrop = RewardType("airdrop_reward") + RewardCommission = RewardType("commission") + RewardHolding = RewardType("holding_reward") + RewardMining = RewardType("mining_reward") + RewardTrading = RewardType("trading_reward") + RewardVipRebate = RewardType("vip_rebate") +) + +func ParseRewardType(s string) (RewardType, error) { + switch s { + case "airdrop_reward": + return RewardAirdrop, nil + case "commission": + return RewardCommission, nil + case "holding_reward": + return RewardHolding, nil + case "mining_reward": + return RewardMining, nil + case "trading_reward": + return RewardTrading, nil + case "vip_rebate": + return RewardVipRebate, nil + + } + + return RewardType(""), fmt.Errorf("unknown reward type: %s", s) +} + +func (t *RewardType) UnmarshalJSON(o []byte) error { + var s string + var err = json.Unmarshal(o, &s) + if err != nil { + return err + } + + rt, err := ParseRewardType(s) + if err != nil { + return err + } + + *t = rt + return nil +} + +type Reward struct { + UUID string `json:"uuid"` + Type RewardType `json:"type"` + Currency string `json:"currency"` + Amount string `json:"amount"` + State string `json:"state"` + Note string `json:"note"` + + // Unix timestamp in seconds + CreatedAt Timestamp `json:"created_at"` +} + +type RewardService struct { + client *RestClient +} + +func (s *RewardService) NewRewardsRequest() *RewardsRequest { + return &RewardsRequest{client: s.client} +} + +func (s *RewardService) NewRewardsByTypeRequest(pathType RewardType) *RewardsRequest { + return &RewardsRequest{client: s.client, pathType: &pathType} +} + +type RewardsRequest struct { + client *RestClient + + pathType *RewardType + + currency *string + + // From Unix-timestamp + from *int64 + + // To Unix-timestamp + to *int64 +} + +func (r *RewardsRequest) Currency(currency string) *RewardsRequest { + r.currency = ¤cy + return r +} + +func (r *RewardsRequest) From(from int64) *RewardsRequest { + r.from = &from + return r +} + +func (r *RewardsRequest) To(to int64) *RewardsRequest { + r.to = &to + return r +} + +func (r *RewardsRequest) Do(ctx context.Context) (rewards []Reward, err error) { + payload := map[string]interface{}{} + + if r.currency != nil { + payload["currency"] = r.currency + } + + if r.to != nil { + payload["to"] = r.to + } + + if r.from != nil { + payload["from"] = r.from + } + + refURL := "v2/rewards" + + if r.pathType != nil { + refURL += "/" + string(*r.pathType) + } + + req, err := r.client.newAuthenticatedRequest("GET", refURL, payload) + if err != nil { + return rewards, err + } + + response, err := r.client.sendRequest(req) + if err != nil { + return rewards, err + } + + if err := response.DecodeJSON(&rewards); err != nil { + return rewards, err + } + + return rewards, err +} diff --git a/pkg/exchange/max/maxapi/trade.go b/pkg/exchange/max/maxapi/trade.go index a9c0a07abc..37360e7989 100644 --- a/pkg/exchange/max/maxapi/trade.go +++ b/pkg/exchange/max/maxapi/trade.go @@ -4,6 +4,8 @@ import ( "context" "net/url" "strconv" + + "github.com/pkg/errors" ) type MarkerInfo struct { @@ -109,25 +111,6 @@ func (options *QueryTradeOptions) Params() url.Values { return params } -func (s *TradeService) MyTrades(options QueryTradeOptions) ([]Trade, error) { - req, err := s.client.newAuthenticatedRequest("GET", "v2/trades/my", options.Map()) - if err != nil { - return nil, err - } - - response, err := s.client.sendRequest(req) - if err != nil { - return nil, err - } - - var v []Trade - if err := response.DecodeJSON(&v); err != nil { - return nil, err - } - - return v, nil -} - func (s *TradeService) NewPrivateTradeRequest() *PrivateTradeRequest { return &PrivateTradeRequest{client: s.client} } @@ -137,110 +120,120 @@ type PrivateRequestParams struct { Path string `json:"path"` } -type PrivateTradeRequestParams struct { - *PrivateRequestParams +type PrivateTradeRequest struct { + client *RestClient - Market string `json:"market"` + market *string // Timestamp is the seconds elapsed since Unix epoch, set to return trades executed before the time only - Timestamp int64 `json:"timestamp,omitempty"` + timestamp *int64 // From field is a trade id, set ot return trades created after the trade - From int64 `json:"from,omitempty"` + from *int64 // To field trade id, set to return trades created before the trade - To int64 `json:"to,omitempty"` - - OrderBy string `json:"order_by,omitempty"` + to *int64 - // default to false - Pagination bool `json:"pagination"` + orderBy *string - Limit int64 `json:"limit,omitempty"` + pagination *bool - Offset int64 `json:"offset,omitempty"` -} + limit *int64 -type PrivateTradeRequest struct { - client *RestClient - params PrivateTradeRequestParams + offset *int64 } func (r *PrivateTradeRequest) Market(market string) *PrivateTradeRequest { - r.params.Market = market + r.market = &market return r } func (r *PrivateTradeRequest) From(from int64) *PrivateTradeRequest { - r.params.From = from + r.from = &from return r } func (r *PrivateTradeRequest) Timestamp(t int64) *PrivateTradeRequest { - r.params.Timestamp = t + r.timestamp = &t return r } func (r *PrivateTradeRequest) To(to int64) *PrivateTradeRequest { - r.params.To = to + r.to = &to return r } func (r *PrivateTradeRequest) Limit(limit int64) *PrivateTradeRequest { - r.params.Limit = limit + r.limit = &limit return r } func (r *PrivateTradeRequest) Offset(offset int64) *PrivateTradeRequest { - r.params.Offset = offset + r.offset = &offset return r } func (r *PrivateTradeRequest) Pagination(p bool) *PrivateTradeRequest { - r.params.Pagination = p + r.pagination = &p return r } func (r *PrivateTradeRequest) OrderBy(orderBy string) *PrivateTradeRequest { - r.params.OrderBy = orderBy + r.orderBy = &orderBy return r } func (r *PrivateTradeRequest) Do(ctx context.Context) (trades []Trade, err error) { - req, err := r.client.newAuthenticatedRequest("GET", "v2/trades/my", &r.params) - if err != nil { - return trades, err + if r.market == nil { + return nil, errors.New("parameter market is mandatory") } - response, err := r.client.sendRequest(req) - if err != nil { - return trades, err + payload := map[string]interface{}{ + "market": r.market, } - if err := response.DecodeJSON(&trades); err != nil { - return trades, err + if r.timestamp != nil { + payload["timestamp"] = r.timestamp } - return trades, err -} + if r.from != nil { + payload["from"] = r.from + } -func (s *TradeService) Trades(options QueryTradeOptions) ([]Trade, error) { - var params = options.Params() + if r.to != nil { + payload["to"] = r.to + } - req, err := s.client.newRequest("GET", "v2/trades", params, nil) + if r.orderBy != nil { + payload["orderBy"] = r.orderBy + } + + if r.pagination != nil { + payload["pagination"] = r.pagination + } + + if r.limit != nil { + payload["limit"] = r.limit + } + + if r.offset != nil { + payload["offset"] = r.offset + } + + req, err := r.client.newAuthenticatedRequest("GET", "v2/trades/my", payload) if err != nil { - return nil, err + return trades, err } - response, err := s.client.sendRequest(req) + response, err := r.client.sendRequest(req) if err != nil { - return nil, err + return trades, err } - var v []Trade - if err := response.DecodeJSON(&v); err != nil { - return nil, err + if err := response.DecodeJSON(&trades); err != nil { + return trades, err } - return v, nil + return trades, err } +