mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 06:53:52 +00:00
Merge pull request #1419 from c9s/edwin/bitget/fix-xmaker-error
FIX: [bitget] use the now - 90 days instead of return err if since is 90 days earlier
This commit is contained in:
commit
afc864dfc4
|
@ -130,8 +130,8 @@ the implementation.
|
|||
- OKEx Spot Exchange
|
||||
- Kucoin Spot Exchange
|
||||
- MAX Spot Exchange (located in Taiwan)
|
||||
- Bitget Exchange (In Progress)
|
||||
- Bybit Exchange (In Progress)
|
||||
- Bitget Exchange
|
||||
- Bybit Exchange
|
||||
|
||||
## Documentation and General Topics
|
||||
|
||||
|
|
|
@ -34,15 +34,16 @@ func TestClient(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
|
||||
t.Run("GetUnfilledOrdersRequest", func(t *testing.T) {
|
||||
req := client.NewGetUnfilledOrdersRequest().StartTime(1)
|
||||
startTime := time.Now().Add(-30 * 24 * time.Hour)
|
||||
req := client.NewGetUnfilledOrdersRequest().StartTime(startTime)
|
||||
resp, err := req.Do(ctx)
|
||||
assert.NoError(t, err)
|
||||
t.Logf("resp: %+v", resp)
|
||||
})
|
||||
|
||||
t.Run("GetHistoryOrdersRequest", func(t *testing.T) {
|
||||
// market buy
|
||||
req, err := client.NewGetHistoryOrdersRequest().Symbol("APEUSDT").Do(ctx)
|
||||
startTime := time.Now().Add(-30 * 24 * time.Hour)
|
||||
req, err := client.NewGetHistoryOrdersRequest().Symbol("APEUSDT").StartTime(startTime).Do(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Logf("place order resp: %+v", req)
|
||||
|
@ -61,7 +62,8 @@ func TestClient(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("GetTradeFillsRequest", func(t *testing.T) {
|
||||
req, err := client.NewGetTradeFillsRequest().Symbol("APEUSDT").Do(ctx)
|
||||
startTime := time.Now().Add(-30 * 24 * time.Hour)
|
||||
req, err := client.NewGetTradeFillsRequest().Symbol("APEUSDT").StartTime(startTime).Do(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Logf("get trade fills resp: %+v", req)
|
||||
|
|
|
@ -6,6 +6,7 @@ package bitgetapi
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
|
@ -91,10 +92,10 @@ type GetHistoryOrdersRequest struct {
|
|||
// Limit number default 100 max 100
|
||||
limit *string `param:"limit,query"`
|
||||
// idLessThan requests the content on the page before this ID (older data), the value input should be the orderId of the corresponding interface.
|
||||
idLessThan *string `param:"idLessThan,query"`
|
||||
startTime *int64 `param:"startTime,query"`
|
||||
endTime *int64 `param:"endTime,query"`
|
||||
orderId *string `param:"orderId,query"`
|
||||
idLessThan *string `param:"idLessThan,query"`
|
||||
startTime *time.Time `param:"startTime,milliseconds,query"`
|
||||
endTime *time.Time `param:"endTime,milliseconds,query"`
|
||||
orderId *string `param:"orderId,query"`
|
||||
}
|
||||
|
||||
func (c *Client) NewGetHistoryOrdersRequest() *GetHistoryOrdersRequest {
|
||||
|
|
|
@ -10,6 +10,8 @@ import (
|
|||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (g *GetHistoryOrdersRequest) Symbol(symbol string) *GetHistoryOrdersRequest {
|
||||
|
@ -27,12 +29,12 @@ func (g *GetHistoryOrdersRequest) IdLessThan(idLessThan string) *GetHistoryOrder
|
|||
return g
|
||||
}
|
||||
|
||||
func (g *GetHistoryOrdersRequest) StartTime(startTime int64) *GetHistoryOrdersRequest {
|
||||
func (g *GetHistoryOrdersRequest) StartTime(startTime time.Time) *GetHistoryOrdersRequest {
|
||||
g.startTime = &startTime
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GetHistoryOrdersRequest) EndTime(endTime int64) *GetHistoryOrdersRequest {
|
||||
func (g *GetHistoryOrdersRequest) EndTime(endTime time.Time) *GetHistoryOrdersRequest {
|
||||
g.endTime = &endTime
|
||||
return g
|
||||
}
|
||||
|
@ -74,7 +76,8 @@ func (g *GetHistoryOrdersRequest) GetQueryParameters() (url.Values, error) {
|
|||
startTime := *g.startTime
|
||||
|
||||
// assign parameter of startTime
|
||||
params["startTime"] = startTime
|
||||
// convert time.Time to milliseconds time stamp
|
||||
params["startTime"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
|
||||
} else {
|
||||
}
|
||||
// check endTime field -> json key endTime
|
||||
|
@ -82,7 +85,8 @@ func (g *GetHistoryOrdersRequest) GetQueryParameters() (url.Values, error) {
|
|||
endTime := *g.endTime
|
||||
|
||||
// assign parameter of endTime
|
||||
params["endTime"] = endTime
|
||||
// convert time.Time to milliseconds time stamp
|
||||
params["endTime"] = strconv.FormatInt(endTime.UnixNano()/int64(time.Millisecond), 10)
|
||||
} else {
|
||||
}
|
||||
// check orderId field -> json key orderId
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package bitgetapi
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/c9s/requestgen"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
|
@ -59,10 +61,10 @@ type GetTradeFillsRequest struct {
|
|||
// Limit number default 100 max 100
|
||||
limit *string `param:"limit,query"`
|
||||
// idLessThan requests the content on the page before this ID (older data), the value input should be the orderId of the corresponding interface.
|
||||
idLessThan *string `param:"idLessThan,query"`
|
||||
startTime *int64 `param:"startTime,query"`
|
||||
endTime *int64 `param:"endTime,query"`
|
||||
orderId *string `param:"orderId,query"`
|
||||
idLessThan *string `param:"idLessThan,query"`
|
||||
startTime *time.Time `param:"startTime,milliseconds,query"`
|
||||
endTime *time.Time `param:"endTime,milliseconds,query"`
|
||||
orderId *string `param:"orderId,query"`
|
||||
}
|
||||
|
||||
func (s *Client) NewGetTradeFillsRequest() *GetTradeFillsRequest {
|
||||
|
|
|
@ -10,6 +10,8 @@ import (
|
|||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (s *GetTradeFillsRequest) Symbol(symbol string) *GetTradeFillsRequest {
|
||||
|
@ -27,12 +29,12 @@ func (s *GetTradeFillsRequest) IdLessThan(idLessThan string) *GetTradeFillsReque
|
|||
return s
|
||||
}
|
||||
|
||||
func (s *GetTradeFillsRequest) StartTime(startTime int64) *GetTradeFillsRequest {
|
||||
func (s *GetTradeFillsRequest) StartTime(startTime time.Time) *GetTradeFillsRequest {
|
||||
s.startTime = &startTime
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *GetTradeFillsRequest) EndTime(endTime int64) *GetTradeFillsRequest {
|
||||
func (s *GetTradeFillsRequest) EndTime(endTime time.Time) *GetTradeFillsRequest {
|
||||
s.endTime = &endTime
|
||||
return s
|
||||
}
|
||||
|
@ -71,7 +73,8 @@ func (s *GetTradeFillsRequest) GetQueryParameters() (url.Values, error) {
|
|||
startTime := *s.startTime
|
||||
|
||||
// assign parameter of startTime
|
||||
params["startTime"] = startTime
|
||||
// convert time.Time to milliseconds time stamp
|
||||
params["startTime"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
|
||||
} else {
|
||||
}
|
||||
// check endTime field -> json key endTime
|
||||
|
@ -79,7 +82,8 @@ func (s *GetTradeFillsRequest) GetQueryParameters() (url.Values, error) {
|
|||
endTime := *s.endTime
|
||||
|
||||
// assign parameter of endTime
|
||||
params["endTime"] = endTime
|
||||
// convert time.Time to milliseconds time stamp
|
||||
params["endTime"] = strconv.FormatInt(endTime.UnixNano()/int64(time.Millisecond), 10)
|
||||
} else {
|
||||
}
|
||||
// check orderId field -> json key orderId
|
||||
|
@ -185,6 +189,12 @@ func (s *GetTradeFillsRequest) GetSlugsMap() (map[string]string, error) {
|
|||
return slugs, nil
|
||||
}
|
||||
|
||||
// GetPath returns the request path of the API
|
||||
func (s *GetTradeFillsRequest) GetPath() string {
|
||||
return "/api/v2/spot/trade/fills"
|
||||
}
|
||||
|
||||
// Do generates the request object and send the request object to the API endpoint
|
||||
func (s *GetTradeFillsRequest) Do(ctx context.Context) ([]Trade, error) {
|
||||
|
||||
// no body params
|
||||
|
@ -194,7 +204,9 @@ func (s *GetTradeFillsRequest) Do(ctx context.Context) ([]Trade, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
apiURL := "/api/v2/spot/trade/fills"
|
||||
var apiURL string
|
||||
|
||||
apiURL = s.GetPath()
|
||||
|
||||
req, err := s.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||
if err != nil {
|
||||
|
@ -211,6 +223,15 @@ func (s *GetTradeFillsRequest) Do(ctx context.Context) ([]Trade, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
type responseValidator interface {
|
||||
Validate() error
|
||||
}
|
||||
validator, ok := interface{}(apiResponse).(responseValidator)
|
||||
if ok {
|
||||
if err := validator.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var data []Trade
|
||||
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -4,6 +4,8 @@ package bitgetapi
|
|||
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/c9s/requestgen"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
|
@ -39,10 +41,10 @@ type GetUnfilledOrdersRequest struct {
|
|||
// Limit number default 100 max 100
|
||||
limit *string `param:"limit,query"`
|
||||
// idLessThan requests the content on the page before this ID (older data), the value input should be the orderId of the corresponding interface.
|
||||
idLessThan *string `param:"idLessThan,query"`
|
||||
startTime *int64 `param:"startTime,query"`
|
||||
endTime *int64 `param:"endTime,query"`
|
||||
orderId *string `param:"orderId,query"`
|
||||
idLessThan *string `param:"idLessThan,query"`
|
||||
startTime *time.Time `param:"startTime,milliseconds,query"`
|
||||
endTime *time.Time `param:"endTime,milliseconds,query"`
|
||||
orderId *string `param:"orderId,query"`
|
||||
}
|
||||
|
||||
func (c *Client) NewGetUnfilledOrdersRequest() *GetUnfilledOrdersRequest {
|
||||
|
|
|
@ -10,6 +10,8 @@ import (
|
|||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (g *GetUnfilledOrdersRequest) Symbol(symbol string) *GetUnfilledOrdersRequest {
|
||||
|
@ -27,12 +29,12 @@ func (g *GetUnfilledOrdersRequest) IdLessThan(idLessThan string) *GetUnfilledOrd
|
|||
return g
|
||||
}
|
||||
|
||||
func (g *GetUnfilledOrdersRequest) StartTime(startTime int64) *GetUnfilledOrdersRequest {
|
||||
func (g *GetUnfilledOrdersRequest) StartTime(startTime time.Time) *GetUnfilledOrdersRequest {
|
||||
g.startTime = &startTime
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GetUnfilledOrdersRequest) EndTime(endTime int64) *GetUnfilledOrdersRequest {
|
||||
func (g *GetUnfilledOrdersRequest) EndTime(endTime time.Time) *GetUnfilledOrdersRequest {
|
||||
g.endTime = &endTime
|
||||
return g
|
||||
}
|
||||
|
@ -74,7 +76,8 @@ func (g *GetUnfilledOrdersRequest) GetQueryParameters() (url.Values, error) {
|
|||
startTime := *g.startTime
|
||||
|
||||
// assign parameter of startTime
|
||||
params["startTime"] = startTime
|
||||
// convert time.Time to milliseconds time stamp
|
||||
params["startTime"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
|
||||
} else {
|
||||
}
|
||||
// check endTime field -> json key endTime
|
||||
|
@ -82,7 +85,8 @@ func (g *GetUnfilledOrdersRequest) GetQueryParameters() (url.Values, error) {
|
|||
endTime := *g.endTime
|
||||
|
||||
// assign parameter of endTime
|
||||
params["endTime"] = endTime
|
||||
// convert time.Time to milliseconds time stamp
|
||||
params["endTime"] = strconv.FormatInt(endTime.UnixNano()/int64(time.Millisecond), 10)
|
||||
} else {
|
||||
}
|
||||
// check orderId field -> json key orderId
|
||||
|
@ -188,6 +192,12 @@ func (g *GetUnfilledOrdersRequest) GetSlugsMap() (map[string]string, error) {
|
|||
return slugs, nil
|
||||
}
|
||||
|
||||
// GetPath returns the request path of the API
|
||||
func (g *GetUnfilledOrdersRequest) GetPath() string {
|
||||
return "/api/v2/spot/trade/unfilled-orders"
|
||||
}
|
||||
|
||||
// Do generates the request object and send the request object to the API endpoint
|
||||
func (g *GetUnfilledOrdersRequest) Do(ctx context.Context) ([]UnfilledOrder, error) {
|
||||
|
||||
// no body params
|
||||
|
@ -197,7 +207,9 @@ func (g *GetUnfilledOrdersRequest) Do(ctx context.Context) ([]UnfilledOrder, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
apiURL := "/api/v2/spot/trade/unfilled-orders"
|
||||
var apiURL string
|
||||
|
||||
apiURL = g.GetPath()
|
||||
|
||||
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||
if err != nil {
|
||||
|
@ -213,6 +225,16 @@ func (g *GetUnfilledOrdersRequest) Do(ctx context.Context) ([]UnfilledOrder, err
|
|||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
type responseValidator interface {
|
||||
Validate() error
|
||||
}
|
||||
validator, ok := interface{}(apiResponse).(responseValidator)
|
||||
if ok {
|
||||
if err := validator.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var data []UnfilledOrder
|
||||
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -21,10 +21,10 @@ const (
|
|||
|
||||
PlatformToken = "BGB"
|
||||
|
||||
queryLimit = 100
|
||||
defaultKLineLimit = 100
|
||||
maxOrderIdLen = 36
|
||||
queryMaxDuration = 90 * 24 * time.Hour
|
||||
queryLimit = 100
|
||||
defaultKLineLimit = 100
|
||||
maxOrderIdLen = 36
|
||||
maxHistoricalDataQueryPeriod = 90 * 24 * time.Hour
|
||||
)
|
||||
|
||||
var log = logrus.WithFields(logrus.Fields{
|
||||
|
@ -123,7 +123,7 @@ func (e *Exchange) QueryTicker(ctx context.Context, symbol string) (*types.Ticke
|
|||
return nil, fmt.Errorf("unexpected length of query single symbol: %+v", resp)
|
||||
}
|
||||
|
||||
ticker := toGlobalTicker(resp[1])
|
||||
ticker := toGlobalTicker(resp[0])
|
||||
return &ticker, nil
|
||||
}
|
||||
|
||||
|
@ -286,10 +286,10 @@ func (e *Exchange) SubmitOrder(ctx context.Context, order types.SubmitOrder) (cr
|
|||
|
||||
// we support only GTC/PostOnly, this is because:
|
||||
// 1. We support only SPOT trading.
|
||||
// 2. The query oepn/closed order does not including the `force` in SPOT.
|
||||
// 2. The query open/closed order does not include the `force` in SPOT.
|
||||
// If we support FOK/IOC, but you can't query them, that would be unreasonable.
|
||||
// The other case to consider is 'PostOnly', which is a trade-off because we want to support 'xmaker'.
|
||||
if order.TimeInForce != types.TimeInForceGTC {
|
||||
if len(order.TimeInForce) != 0 && order.TimeInForce != types.TimeInForceGTC {
|
||||
return nil, fmt.Errorf("time-in-force %s not supported", order.TimeInForce)
|
||||
}
|
||||
req.Force(v2.OrderForceGTC)
|
||||
|
@ -397,18 +397,24 @@ func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders [
|
|||
// QueryClosedOrders queries closed order by time range(`CreatedTime`) and id. The order of the response is in descending order.
|
||||
// If you need to retrieve all data, please utilize the function pkg/exchange/batch.ClosedOrderBatchQuery.
|
||||
//
|
||||
// REMARK: If your start time is 90 days earlier, we will update it to now - 90 days.
|
||||
// ** Since is inclusive, Until is exclusive. If you use a time range to query, you must provide both a start time and an end time. **
|
||||
// ** Since and Until cannot exceed 90 days. **
|
||||
// ** Since from the last 90 days can be queried. **
|
||||
// ** Since from the last 90 days can be queried **
|
||||
func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since, until time.Time, lastOrderID uint64) (orders []types.Order, err error) {
|
||||
if time.Since(since) > queryMaxDuration {
|
||||
return nil, fmt.Errorf("start time from the last 90 days can be queried, got: %s", since)
|
||||
newSince := since
|
||||
now := time.Now()
|
||||
|
||||
if time.Since(newSince) > maxHistoricalDataQueryPeriod {
|
||||
newSince = now.Add(-maxHistoricalDataQueryPeriod)
|
||||
log.Warnf("!!!BITGET EXCHANGE API NOTICE!!! The closed order API cannot query data beyond 90 days from the current date, update %s -> %s", since, newSince)
|
||||
}
|
||||
if until.Before(since) {
|
||||
return nil, fmt.Errorf("end time %s before start %s", until, since)
|
||||
if until.Before(newSince) {
|
||||
log.Warnf("!!!BITGET EXCHANGE API NOTICE!!! The 'until' comes before 'since', update until to now(%s -> %s).", until, now)
|
||||
until = now
|
||||
}
|
||||
if until.Sub(since) > queryMaxDuration {
|
||||
return nil, fmt.Errorf("the start time %s and end time %s cannot exceed 90 days", since, until)
|
||||
if until.Sub(newSince) > maxHistoricalDataQueryPeriod {
|
||||
return nil, fmt.Errorf("the start time %s and end time %s cannot exceed 90 days", newSince, until)
|
||||
}
|
||||
if lastOrderID != 0 {
|
||||
log.Warn("!!!BITGET EXCHANGE API NOTICE!!! The order of response is in descending order, so the last order id not supported.")
|
||||
|
@ -420,8 +426,8 @@ func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since,
|
|||
res, err := e.v2client.NewGetHistoryOrdersRequest().
|
||||
Symbol(symbol).
|
||||
Limit(strconv.Itoa(queryLimit)).
|
||||
StartTime(since.UnixMilli()).
|
||||
EndTime(until.UnixMilli()).
|
||||
StartTime(newSince).
|
||||
EndTime(until).
|
||||
Do(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to call get order histories error: %w", err)
|
||||
|
@ -451,7 +457,7 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err
|
|||
}
|
||||
|
||||
for _, order := range orders {
|
||||
req := e.client.NewCancelOrderRequest()
|
||||
req := e.v2client.NewCancelOrderRequest()
|
||||
|
||||
reqId := ""
|
||||
switch {
|
||||
|
@ -472,7 +478,7 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err
|
|||
continue
|
||||
}
|
||||
|
||||
req.Symbol(order.Market.Symbol)
|
||||
req.Symbol(order.Symbol)
|
||||
|
||||
if err := cancelOrderRateLimiter.Wait(ctx); err != nil {
|
||||
errs = multierr.Append(errs, fmt.Errorf("cancel order rate limiter wait, order id: %s, error: %w", order.ClientOrderID, err))
|
||||
|
@ -485,8 +491,8 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err
|
|||
}
|
||||
|
||||
// sanity check
|
||||
if res.OrderId != reqId && res.ClientOrderId != reqId {
|
||||
errs = multierr.Append(errs, fmt.Errorf("order id mismatch, exp: %s, respOrderId: %s, respClientOrderId: %s", reqId, res.OrderId, res.ClientOrderId))
|
||||
if res.OrderId.String() != reqId && res.ClientOrderId != reqId {
|
||||
errs = multierr.Append(errs, fmt.Errorf("order id mismatch, exp: %s, respOrderId: %d, respClientOrderId: %s", reqId, res.OrderId, res.ClientOrderId))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -498,6 +504,7 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err
|
|||
// using (`CreatedTime`) as the search criteria.
|
||||
// If you need to retrieve all data, please utilize the function pkg/exchange/batch.TradeBatchQuery.
|
||||
//
|
||||
// REMARK: If your start time is 90 days earlier, we will update it to now - 90 days.
|
||||
// ** StartTime is inclusive, EndTime is exclusive. If you use the EndTime, the StartTime is required. **
|
||||
// ** StartTime and EndTime cannot exceed 90 days. **
|
||||
func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *types.TradeQueryOptions) (trades []types.Trade, err error) {
|
||||
|
@ -508,24 +515,27 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
|
|||
req := e.v2client.NewGetTradeFillsRequest()
|
||||
req.Symbol(symbol)
|
||||
|
||||
var newStartTime time.Time
|
||||
if options.StartTime != nil {
|
||||
if time.Since(*options.StartTime) > queryMaxDuration {
|
||||
return nil, fmt.Errorf("start time from the last 90 days can be queried, got: %s", options.StartTime)
|
||||
newStartTime = *options.StartTime
|
||||
if time.Since(newStartTime) > maxHistoricalDataQueryPeriod {
|
||||
newStartTime = time.Now().Add(-maxHistoricalDataQueryPeriod)
|
||||
log.Warnf("!!!BITGET EXCHANGE API NOTICE!!! The trade API cannot query data beyond 90 days from the current date, update %s -> %s", *options.StartTime, newStartTime)
|
||||
}
|
||||
req.StartTime(options.StartTime.UnixMilli())
|
||||
req.StartTime(newStartTime)
|
||||
}
|
||||
|
||||
if options.EndTime != nil {
|
||||
if options.StartTime == nil {
|
||||
if newStartTime.IsZero() {
|
||||
return nil, errors.New("start time is required for query trades if you take end time")
|
||||
}
|
||||
if options.EndTime.Before(*options.StartTime) {
|
||||
return nil, fmt.Errorf("end time %s before start %s", *options.EndTime, *options.StartTime)
|
||||
if options.EndTime.Before(newStartTime) {
|
||||
return nil, fmt.Errorf("end time %s before start %s", *options.EndTime, newStartTime)
|
||||
}
|
||||
if options.EndTime.Sub(*options.StartTime) > queryMaxDuration {
|
||||
return nil, fmt.Errorf("start time %s and end time %s cannot greater than 90 days", options.StartTime, options.EndTime)
|
||||
if options.EndTime.Sub(newStartTime) > maxHistoricalDataQueryPeriod {
|
||||
return nil, fmt.Errorf("start time %s and end time %s cannot greater than 90 days", newStartTime, options.EndTime)
|
||||
}
|
||||
req.EndTime(options.EndTime.UnixMilli())
|
||||
req.EndTime(*options.EndTime)
|
||||
}
|
||||
|
||||
limit := options.Limit
|
||||
|
|
|
@ -15,30 +15,6 @@ const DateFormat = "2006-01-02"
|
|||
|
||||
type ExchangeName string
|
||||
|
||||
func (n *ExchangeName) Value() (driver.Value, error) {
|
||||
return n.String(), nil
|
||||
}
|
||||
|
||||
func (n *ExchangeName) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch s {
|
||||
case "max", "binance", "okex", "kucoin":
|
||||
*n = ExchangeName(s)
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown or unsupported exchange name: %s, valid names are: max, binance, okex, kucoin", s)
|
||||
}
|
||||
|
||||
func (n ExchangeName) String() string {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
const (
|
||||
ExchangeMax ExchangeName = "max"
|
||||
ExchangeBinance ExchangeName = "binance"
|
||||
|
@ -59,15 +35,43 @@ var SupportedExchanges = []ExchangeName{
|
|||
// note: we are not using "backtest"
|
||||
}
|
||||
|
||||
func ValidExchangeName(a string) (ExchangeName, error) {
|
||||
aa := strings.ToLower(a)
|
||||
for _, n := range SupportedExchanges {
|
||||
if string(n) == aa {
|
||||
return n, nil
|
||||
}
|
||||
func (n *ExchangeName) Value() (driver.Value, error) {
|
||||
return n.String(), nil
|
||||
}
|
||||
|
||||
func (n *ExchangeName) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("invalid exchange name: %s", a)
|
||||
*n = ExchangeName(s)
|
||||
if !n.IsValid() {
|
||||
return fmt.Errorf("%s is an invalid exchange name", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n ExchangeName) IsValid() bool {
|
||||
switch n {
|
||||
case ExchangeBinance, ExchangeBitget, ExchangeBybit, ExchangeMax, ExchangeOKEx, ExchangeKucoin:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (n ExchangeName) String() string {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
func ValidExchangeName(a string) (ExchangeName, error) {
|
||||
exName := ExchangeName(strings.ToLower(a))
|
||||
if !exName.IsValid() {
|
||||
return "", fmt.Errorf("invalid exchange name: %s", a)
|
||||
}
|
||||
|
||||
return exName, nil
|
||||
}
|
||||
|
||||
type ExchangeMinimal interface {
|
||||
|
|
|
@ -41,3 +41,10 @@ func (s *StrInt64) UnmarshalJSON(body []byte) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StrInt64) String() string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return strconv.FormatInt(int64(*s), 10)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user