mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 08:45:16 +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
|
- OKEx Spot Exchange
|
||||||
- Kucoin Spot Exchange
|
- Kucoin Spot Exchange
|
||||||
- MAX Spot Exchange (located in Taiwan)
|
- MAX Spot Exchange (located in Taiwan)
|
||||||
- Bitget Exchange (In Progress)
|
- Bitget Exchange
|
||||||
- Bybit Exchange (In Progress)
|
- Bybit Exchange
|
||||||
|
|
||||||
## Documentation and General Topics
|
## Documentation and General Topics
|
||||||
|
|
||||||
|
|
|
@ -34,15 +34,16 @@ func TestClient(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
t.Run("GetUnfilledOrdersRequest", func(t *testing.T) {
|
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)
|
resp, err := req.Do(ctx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
t.Logf("resp: %+v", resp)
|
t.Logf("resp: %+v", resp)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetHistoryOrdersRequest", func(t *testing.T) {
|
t.Run("GetHistoryOrdersRequest", func(t *testing.T) {
|
||||||
// market buy
|
startTime := time.Now().Add(-30 * 24 * time.Hour)
|
||||||
req, err := client.NewGetHistoryOrdersRequest().Symbol("APEUSDT").Do(ctx)
|
req, err := client.NewGetHistoryOrdersRequest().Symbol("APEUSDT").StartTime(startTime).Do(ctx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
t.Logf("place order resp: %+v", req)
|
t.Logf("place order resp: %+v", req)
|
||||||
|
@ -61,7 +62,8 @@ func TestClient(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetTradeFillsRequest", func(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)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
t.Logf("get trade fills resp: %+v", req)
|
t.Logf("get trade fills resp: %+v", req)
|
||||||
|
|
|
@ -6,6 +6,7 @@ package bitgetapi
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
@ -91,10 +92,10 @@ type GetHistoryOrdersRequest struct {
|
||||||
// Limit number default 100 max 100
|
// Limit number default 100 max 100
|
||||||
limit *string `param:"limit,query"`
|
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 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"`
|
idLessThan *string `param:"idLessThan,query"`
|
||||||
startTime *int64 `param:"startTime,query"`
|
startTime *time.Time `param:"startTime,milliseconds,query"`
|
||||||
endTime *int64 `param:"endTime,query"`
|
endTime *time.Time `param:"endTime,milliseconds,query"`
|
||||||
orderId *string `param:"orderId,query"`
|
orderId *string `param:"orderId,query"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) NewGetHistoryOrdersRequest() *GetHistoryOrdersRequest {
|
func (c *Client) NewGetHistoryOrdersRequest() *GetHistoryOrdersRequest {
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g *GetHistoryOrdersRequest) Symbol(symbol string) *GetHistoryOrdersRequest {
|
func (g *GetHistoryOrdersRequest) Symbol(symbol string) *GetHistoryOrdersRequest {
|
||||||
|
@ -27,12 +29,12 @@ func (g *GetHistoryOrdersRequest) IdLessThan(idLessThan string) *GetHistoryOrder
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GetHistoryOrdersRequest) StartTime(startTime int64) *GetHistoryOrdersRequest {
|
func (g *GetHistoryOrdersRequest) StartTime(startTime time.Time) *GetHistoryOrdersRequest {
|
||||||
g.startTime = &startTime
|
g.startTime = &startTime
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GetHistoryOrdersRequest) EndTime(endTime int64) *GetHistoryOrdersRequest {
|
func (g *GetHistoryOrdersRequest) EndTime(endTime time.Time) *GetHistoryOrdersRequest {
|
||||||
g.endTime = &endTime
|
g.endTime = &endTime
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
@ -74,7 +76,8 @@ func (g *GetHistoryOrdersRequest) GetQueryParameters() (url.Values, error) {
|
||||||
startTime := *g.startTime
|
startTime := *g.startTime
|
||||||
|
|
||||||
// assign parameter of 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 {
|
} else {
|
||||||
}
|
}
|
||||||
// check endTime field -> json key endTime
|
// check endTime field -> json key endTime
|
||||||
|
@ -82,7 +85,8 @@ func (g *GetHistoryOrdersRequest) GetQueryParameters() (url.Values, error) {
|
||||||
endTime := *g.endTime
|
endTime := *g.endTime
|
||||||
|
|
||||||
// assign parameter of 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 {
|
} else {
|
||||||
}
|
}
|
||||||
// check orderId field -> json key orderId
|
// check orderId field -> json key orderId
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package bitgetapi
|
package bitgetapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/c9s/requestgen"
|
"github.com/c9s/requestgen"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
@ -59,10 +61,10 @@ type GetTradeFillsRequest struct {
|
||||||
// Limit number default 100 max 100
|
// Limit number default 100 max 100
|
||||||
limit *string `param:"limit,query"`
|
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 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"`
|
idLessThan *string `param:"idLessThan,query"`
|
||||||
startTime *int64 `param:"startTime,query"`
|
startTime *time.Time `param:"startTime,milliseconds,query"`
|
||||||
endTime *int64 `param:"endTime,query"`
|
endTime *time.Time `param:"endTime,milliseconds,query"`
|
||||||
orderId *string `param:"orderId,query"`
|
orderId *string `param:"orderId,query"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Client) NewGetTradeFillsRequest() *GetTradeFillsRequest {
|
func (s *Client) NewGetTradeFillsRequest() *GetTradeFillsRequest {
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *GetTradeFillsRequest) Symbol(symbol string) *GetTradeFillsRequest {
|
func (s *GetTradeFillsRequest) Symbol(symbol string) *GetTradeFillsRequest {
|
||||||
|
@ -27,12 +29,12 @@ func (s *GetTradeFillsRequest) IdLessThan(idLessThan string) *GetTradeFillsReque
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GetTradeFillsRequest) StartTime(startTime int64) *GetTradeFillsRequest {
|
func (s *GetTradeFillsRequest) StartTime(startTime time.Time) *GetTradeFillsRequest {
|
||||||
s.startTime = &startTime
|
s.startTime = &startTime
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GetTradeFillsRequest) EndTime(endTime int64) *GetTradeFillsRequest {
|
func (s *GetTradeFillsRequest) EndTime(endTime time.Time) *GetTradeFillsRequest {
|
||||||
s.endTime = &endTime
|
s.endTime = &endTime
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -71,7 +73,8 @@ func (s *GetTradeFillsRequest) GetQueryParameters() (url.Values, error) {
|
||||||
startTime := *s.startTime
|
startTime := *s.startTime
|
||||||
|
|
||||||
// assign parameter of 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 {
|
} else {
|
||||||
}
|
}
|
||||||
// check endTime field -> json key endTime
|
// check endTime field -> json key endTime
|
||||||
|
@ -79,7 +82,8 @@ func (s *GetTradeFillsRequest) GetQueryParameters() (url.Values, error) {
|
||||||
endTime := *s.endTime
|
endTime := *s.endTime
|
||||||
|
|
||||||
// assign parameter of 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 {
|
} else {
|
||||||
}
|
}
|
||||||
// check orderId field -> json key orderId
|
// check orderId field -> json key orderId
|
||||||
|
@ -185,6 +189,12 @@ func (s *GetTradeFillsRequest) GetSlugsMap() (map[string]string, error) {
|
||||||
return slugs, nil
|
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) {
|
func (s *GetTradeFillsRequest) Do(ctx context.Context) ([]Trade, error) {
|
||||||
|
|
||||||
// no body params
|
// no body params
|
||||||
|
@ -194,7 +204,9 @@ func (s *GetTradeFillsRequest) Do(ctx context.Context) ([]Trade, error) {
|
||||||
return nil, err
|
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)
|
req, err := s.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -211,6 +223,15 @@ func (s *GetTradeFillsRequest) Do(ctx context.Context) ([]Trade, error) {
|
||||||
return nil, err
|
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
|
var data []Trade
|
||||||
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
|
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -4,6 +4,8 @@ package bitgetapi
|
||||||
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
|
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/c9s/requestgen"
|
"github.com/c9s/requestgen"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
@ -39,10 +41,10 @@ type GetUnfilledOrdersRequest struct {
|
||||||
// Limit number default 100 max 100
|
// Limit number default 100 max 100
|
||||||
limit *string `param:"limit,query"`
|
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 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"`
|
idLessThan *string `param:"idLessThan,query"`
|
||||||
startTime *int64 `param:"startTime,query"`
|
startTime *time.Time `param:"startTime,milliseconds,query"`
|
||||||
endTime *int64 `param:"endTime,query"`
|
endTime *time.Time `param:"endTime,milliseconds,query"`
|
||||||
orderId *string `param:"orderId,query"`
|
orderId *string `param:"orderId,query"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) NewGetUnfilledOrdersRequest() *GetUnfilledOrdersRequest {
|
func (c *Client) NewGetUnfilledOrdersRequest() *GetUnfilledOrdersRequest {
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g *GetUnfilledOrdersRequest) Symbol(symbol string) *GetUnfilledOrdersRequest {
|
func (g *GetUnfilledOrdersRequest) Symbol(symbol string) *GetUnfilledOrdersRequest {
|
||||||
|
@ -27,12 +29,12 @@ func (g *GetUnfilledOrdersRequest) IdLessThan(idLessThan string) *GetUnfilledOrd
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GetUnfilledOrdersRequest) StartTime(startTime int64) *GetUnfilledOrdersRequest {
|
func (g *GetUnfilledOrdersRequest) StartTime(startTime time.Time) *GetUnfilledOrdersRequest {
|
||||||
g.startTime = &startTime
|
g.startTime = &startTime
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GetUnfilledOrdersRequest) EndTime(endTime int64) *GetUnfilledOrdersRequest {
|
func (g *GetUnfilledOrdersRequest) EndTime(endTime time.Time) *GetUnfilledOrdersRequest {
|
||||||
g.endTime = &endTime
|
g.endTime = &endTime
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
@ -74,7 +76,8 @@ func (g *GetUnfilledOrdersRequest) GetQueryParameters() (url.Values, error) {
|
||||||
startTime := *g.startTime
|
startTime := *g.startTime
|
||||||
|
|
||||||
// assign parameter of 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 {
|
} else {
|
||||||
}
|
}
|
||||||
// check endTime field -> json key endTime
|
// check endTime field -> json key endTime
|
||||||
|
@ -82,7 +85,8 @@ func (g *GetUnfilledOrdersRequest) GetQueryParameters() (url.Values, error) {
|
||||||
endTime := *g.endTime
|
endTime := *g.endTime
|
||||||
|
|
||||||
// assign parameter of 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 {
|
} else {
|
||||||
}
|
}
|
||||||
// check orderId field -> json key orderId
|
// check orderId field -> json key orderId
|
||||||
|
@ -188,6 +192,12 @@ func (g *GetUnfilledOrdersRequest) GetSlugsMap() (map[string]string, error) {
|
||||||
return slugs, nil
|
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) {
|
func (g *GetUnfilledOrdersRequest) Do(ctx context.Context) ([]UnfilledOrder, error) {
|
||||||
|
|
||||||
// no body params
|
// no body params
|
||||||
|
@ -197,7 +207,9 @@ func (g *GetUnfilledOrdersRequest) Do(ctx context.Context) ([]UnfilledOrder, err
|
||||||
return nil, 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)
|
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -213,6 +225,16 @@ func (g *GetUnfilledOrdersRequest) Do(ctx context.Context) ([]UnfilledOrder, err
|
||||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||||
return nil, err
|
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
|
var data []UnfilledOrder
|
||||||
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
|
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -21,10 +21,10 @@ const (
|
||||||
|
|
||||||
PlatformToken = "BGB"
|
PlatformToken = "BGB"
|
||||||
|
|
||||||
queryLimit = 100
|
queryLimit = 100
|
||||||
defaultKLineLimit = 100
|
defaultKLineLimit = 100
|
||||||
maxOrderIdLen = 36
|
maxOrderIdLen = 36
|
||||||
queryMaxDuration = 90 * 24 * time.Hour
|
maxHistoricalDataQueryPeriod = 90 * 24 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logrus.WithFields(logrus.Fields{
|
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)
|
return nil, fmt.Errorf("unexpected length of query single symbol: %+v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker := toGlobalTicker(resp[1])
|
ticker := toGlobalTicker(resp[0])
|
||||||
return &ticker, nil
|
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:
|
// we support only GTC/PostOnly, this is because:
|
||||||
// 1. We support only SPOT trading.
|
// 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.
|
// 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'.
|
// 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)
|
return nil, fmt.Errorf("time-in-force %s not supported", order.TimeInForce)
|
||||||
}
|
}
|
||||||
req.Force(v2.OrderForceGTC)
|
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.
|
// 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.
|
// 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 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 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) {
|
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 {
|
newSince := since
|
||||||
return nil, fmt.Errorf("start time from the last 90 days can be queried, got: %s", 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) {
|
if until.Before(newSince) {
|
||||||
return nil, fmt.Errorf("end time %s before start %s", until, since)
|
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 {
|
if until.Sub(newSince) > maxHistoricalDataQueryPeriod {
|
||||||
return nil, fmt.Errorf("the start time %s and end time %s cannot exceed 90 days", since, until)
|
return nil, fmt.Errorf("the start time %s and end time %s cannot exceed 90 days", newSince, until)
|
||||||
}
|
}
|
||||||
if lastOrderID != 0 {
|
if lastOrderID != 0 {
|
||||||
log.Warn("!!!BITGET EXCHANGE API NOTICE!!! The order of response is in descending order, so the last order id not supported.")
|
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().
|
res, err := e.v2client.NewGetHistoryOrdersRequest().
|
||||||
Symbol(symbol).
|
Symbol(symbol).
|
||||||
Limit(strconv.Itoa(queryLimit)).
|
Limit(strconv.Itoa(queryLimit)).
|
||||||
StartTime(since.UnixMilli()).
|
StartTime(newSince).
|
||||||
EndTime(until.UnixMilli()).
|
EndTime(until).
|
||||||
Do(ctx)
|
Do(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to call get order histories error: %w", err)
|
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 {
|
for _, order := range orders {
|
||||||
req := e.client.NewCancelOrderRequest()
|
req := e.v2client.NewCancelOrderRequest()
|
||||||
|
|
||||||
reqId := ""
|
reqId := ""
|
||||||
switch {
|
switch {
|
||||||
|
@ -472,7 +478,7 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Symbol(order.Market.Symbol)
|
req.Symbol(order.Symbol)
|
||||||
|
|
||||||
if err := cancelOrderRateLimiter.Wait(ctx); err != nil {
|
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))
|
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
|
// sanity check
|
||||||
if res.OrderId != reqId && res.ClientOrderId != reqId {
|
if res.OrderId.String() != reqId && res.ClientOrderId != reqId {
|
||||||
errs = multierr.Append(errs, fmt.Errorf("order id mismatch, exp: %s, respOrderId: %s, respClientOrderId: %s", reqId, res.OrderId, res.ClientOrderId))
|
errs = multierr.Append(errs, fmt.Errorf("order id mismatch, exp: %s, respOrderId: %d, respClientOrderId: %s", reqId, res.OrderId, res.ClientOrderId))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -498,6 +504,7 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err
|
||||||
// using (`CreatedTime`) as the search criteria.
|
// using (`CreatedTime`) as the search criteria.
|
||||||
// If you need to retrieve all data, please utilize the function pkg/exchange/batch.TradeBatchQuery.
|
// 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 is inclusive, EndTime is exclusive. If you use the EndTime, the StartTime is required. **
|
||||||
// ** StartTime and EndTime cannot exceed 90 days. **
|
// ** StartTime and EndTime cannot exceed 90 days. **
|
||||||
func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *types.TradeQueryOptions) (trades []types.Trade, err error) {
|
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 := e.v2client.NewGetTradeFillsRequest()
|
||||||
req.Symbol(symbol)
|
req.Symbol(symbol)
|
||||||
|
|
||||||
|
var newStartTime time.Time
|
||||||
if options.StartTime != nil {
|
if options.StartTime != nil {
|
||||||
if time.Since(*options.StartTime) > queryMaxDuration {
|
newStartTime = *options.StartTime
|
||||||
return nil, fmt.Errorf("start time from the last 90 days can be queried, got: %s", 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.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")
|
return nil, errors.New("start time is required for query trades if you take end time")
|
||||||
}
|
}
|
||||||
if options.EndTime.Before(*options.StartTime) {
|
if options.EndTime.Before(newStartTime) {
|
||||||
return nil, fmt.Errorf("end time %s before start %s", *options.EndTime, *options.StartTime)
|
return nil, fmt.Errorf("end time %s before start %s", *options.EndTime, newStartTime)
|
||||||
}
|
}
|
||||||
if options.EndTime.Sub(*options.StartTime) > queryMaxDuration {
|
if options.EndTime.Sub(newStartTime) > maxHistoricalDataQueryPeriod {
|
||||||
return nil, fmt.Errorf("start time %s and end time %s cannot greater than 90 days", options.StartTime, options.EndTime)
|
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
|
limit := options.Limit
|
||||||
|
|
|
@ -15,30 +15,6 @@ const DateFormat = "2006-01-02"
|
||||||
|
|
||||||
type ExchangeName string
|
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 (
|
const (
|
||||||
ExchangeMax ExchangeName = "max"
|
ExchangeMax ExchangeName = "max"
|
||||||
ExchangeBinance ExchangeName = "binance"
|
ExchangeBinance ExchangeName = "binance"
|
||||||
|
@ -59,15 +35,43 @@ var SupportedExchanges = []ExchangeName{
|
||||||
// note: we are not using "backtest"
|
// note: we are not using "backtest"
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidExchangeName(a string) (ExchangeName, error) {
|
func (n *ExchangeName) Value() (driver.Value, error) {
|
||||||
aa := strings.ToLower(a)
|
return n.String(), nil
|
||||||
for _, n := range SupportedExchanges {
|
}
|
||||||
if string(n) == aa {
|
|
||||||
return n, 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 {
|
type ExchangeMinimal interface {
|
||||||
|
|
|
@ -41,3 +41,10 @@ func (s *StrInt64) UnmarshalJSON(body []byte) error {
|
||||||
|
|
||||||
return nil
|
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