exchanges: Refactor time handling and other minor improvements (#1948)

* exchanges: Refactor time handling and other minor improvements

- Updated Kraken wrapper to utilise new time handling methods.
- Simplified Kucoin types by removing unnecessary structures and using direct JSON unmarshalling.
- Improved websocket handling in Kucoin to directly parse candlestick data.
- Modified Lbank types to use the new time representation.
- Adjusted Poloniex wrapper and types to utilise the new time handling.
- Updated Yobit types and wrapper to reflect changes in time representation.
- Introduced DateTime type for better handling of specific time formats.
- Added tests for DateTime unmarshalling to ensure correctness.
- Rid UTC().Unix and UTC().UnixMilli as it's not needed
- Correct Huobi timestamp usage for some endpoints.
- Rid RFC3339 time parsing since Go does that automatically.

* exchanges: Refactor JSON unmarshalling for various types and improve test coverage

* linter: Update error message in TestGetKlines

* refactor: Simplify JSON unmarshalling in MovementHistory and improve test assertions in GetKlines

* refactor: Improve JSON unmarshalling for channel name and clarify comment in wsProcessOpenOrders

* refactor: Update time handling in Huobi types to use types.Time for createdAt fields and relax GetLiquidationOrders test

* refactor: Move wsTicker, wsSpread, wsTrades, and wsCandle types to kraken_types.go for better organistion

* refactor: Add validation for underlying parameter in GetExpirationTime and update tests
This commit is contained in:
Adrian Gallagher
2025-07-01 09:11:55 +10:00
committed by GitHub
parent 48a66c9faa
commit 3cc9a2b9e0
92 changed files with 2488 additions and 3276 deletions

View File

@@ -21,6 +21,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/types"
)
const (
@@ -752,14 +753,13 @@ func (g *Gateio) GetMySpotTradingHistory(ctx context.Context, p currency.Pair, o
// GetServerTime retrieves current server time
func (g *Gateio) GetServerTime(ctx context.Context, _ asset.Item) (time.Time, error) {
resp := struct {
ServerTime int64 `json:"server_time"`
}{}
err := g.SendHTTPRequest(ctx, exchange.RestSpot, publicGetServerTimeEPL, gateioSpotServerTime, &resp)
if err != nil {
var resp struct {
ServerTime types.Time `json:"server_time"`
}
if err := g.SendHTTPRequest(ctx, exchange.RestSpot, publicGetServerTimeEPL, gateioSpotServerTime, &resp); err != nil {
return time.Time{}, err
}
return time.Unix(resp.ServerTime, 0), nil
return resp.ServerTime.Time(), nil
}
// CountdownCancelorders Countdown cancel orders
@@ -876,6 +876,11 @@ func (g *Gateio) CancelPriceTriggeredOrder(ctx context.Context, orderID string)
// GenerateSignature returns hash for authenticated requests
func (g *Gateio) GenerateSignature(secret, method, path, query string, body any, dtime time.Time) (string, error) {
rawQuery, err := url.QueryUnescape(query)
if err != nil {
return "", err
}
h := sha512.New()
if body != nil {
val, err := json.Marshal(body)
@@ -886,12 +891,8 @@ func (g *Gateio) GenerateSignature(secret, method, path, query string, body any,
}
h.Write(nil)
hashedPayload := hex.EncodeToString(h.Sum(nil))
t := strconv.FormatInt(dtime.Unix(), 10)
rawQuery, err := url.QueryUnescape(query)
if err != nil {
return "", err
}
msg := method + "\n" + path + "\n" + rawQuery + "\n" + hashedPayload + "\n" + t
msg := method + "\n" + path + "\n" + rawQuery + "\n" + hashedPayload + "\n" + strconv.FormatInt(dtime.Unix(), 10)
mac := hmac.New(sha512.New, []byte(secret))
mac.Write([]byte(msg))
return hex.EncodeToString(mac.Sum(nil)), nil
@@ -3116,15 +3117,18 @@ func (g *Gateio) GetAllOptionsUnderlyings(ctx context.Context) ([]OptionUnderlyi
// GetExpirationTime return the expiration time for the provided underlying.
func (g *Gateio) GetExpirationTime(ctx context.Context, underlying string) (time.Time, error) {
var timestamp []float64
err := g.SendHTTPRequest(ctx, exchange.RestSpot, publicExpirationOptionsEPL, gateioOptionExpiration+"?underlying="+underlying, &timestamp)
if underlying == "" {
return time.Time{}, errInvalidUnderlying
}
var timestamps []types.Time
err := g.SendHTTPRequest(ctx, exchange.RestSpot, publicExpirationOptionsEPL, gateioOptionExpiration+"?underlying="+underlying, &timestamps)
if err != nil {
return time.Time{}, err
}
if len(timestamp) == 0 {
if len(timestamps) == 0 {
return time.Time{}, errNoValidResponseFromServer
}
return time.Unix(int64(timestamp[0]), 0), nil
return timestamps[0].Time(), nil
}
// GetAllContractOfUnderlyingWithinExpiryDate retrieves list of contracts of the specified underlying and expiry time.

View File

@@ -1473,9 +1473,10 @@ func TestGetAllOptionsUnderlyings(t *testing.T) {
func TestGetExpirationTime(t *testing.T) {
t.Parallel()
if _, err := g.GetExpirationTime(t.Context(), "BTC_USDT"); err != nil {
t.Errorf("%s GetExpirationTime() error %v", g.Name, err)
}
_, err := g.GetExpirationTime(t.Context(), "")
assert.ErrorIs(t, err, errInvalidUnderlying)
_, err = g.GetExpirationTime(t.Context(), "BTC_USDT")
assert.NoError(t, err, "GetExpirationTime should not error")
}
func TestGetAllContractOfUnderlyingWithinExpiryDate(t *testing.T) {

View File

@@ -32,6 +32,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/subscription"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/types"
)
const (
@@ -433,7 +434,7 @@ func (g *Gateio) processOrderbookSnapshot(incoming []byte, lastPushed time.Time)
func (g *Gateio) processSpotOrders(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsSpotOrder `json:"result"`
@@ -481,7 +482,7 @@ func (g *Gateio) processUserPersonalTrades(data []byte) error {
}
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsUserPersonalTrade `json:"result"`
@@ -512,7 +513,7 @@ func (g *Gateio) processUserPersonalTrades(data []byte) error {
func (g *Gateio) processSpotBalances(ctx context.Context, data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsSpotBalance `json:"result"`
@@ -545,7 +546,7 @@ func (g *Gateio) processSpotBalances(ctx context.Context, data []byte) error {
func (g *Gateio) processMarginBalances(ctx context.Context, data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsMarginBalance `json:"result"`
@@ -577,7 +578,7 @@ func (g *Gateio) processMarginBalances(ctx context.Context, data []byte) error {
func (g *Gateio) processFundingBalances(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFundingBalance `json:"result"`
@@ -592,7 +593,7 @@ func (g *Gateio) processFundingBalances(data []byte) error {
func (g *Gateio) processCrossMarginBalance(ctx context.Context, data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsCrossMarginBalance `json:"result"`
@@ -624,7 +625,7 @@ func (g *Gateio) processCrossMarginBalance(ctx context.Context, data []byte) err
func (g *Gateio) processCrossMarginLoans(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result WsCrossMarginLoan `json:"result"`

View File

@@ -21,6 +21,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/subscription"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/types"
)
const (
@@ -299,7 +300,7 @@ func (g *Gateio) generateFuturesPayload(ctx context.Context, conn websocket.Conn
func (g *Gateio) processFuturesTickers(data []byte, assetType asset.Item) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFutureTicker `json:"result"`
@@ -319,7 +320,7 @@ func (g *Gateio) processFuturesTickers(data []byte, assetType asset.Item) error
Last: resp.Result[x].Last.Float64(),
AssetType: assetType,
Pair: resp.Result[x].Contract,
LastUpdated: time.Unix(resp.Time, 0),
LastUpdated: resp.Time.Time(),
}
}
g.Websocket.DataHandler <- tickerPriceDatas
@@ -333,7 +334,7 @@ func (g *Gateio) processFuturesTrades(data []byte, assetType asset.Item) error {
}
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesTrades `json:"result"`
@@ -360,7 +361,7 @@ func (g *Gateio) processFuturesTrades(data []byte, assetType asset.Item) error {
func (g *Gateio) processFuturesCandlesticks(data []byte, assetType asset.Item) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []FuturesCandlestick `json:"result"`
@@ -514,7 +515,7 @@ func (g *Gateio) processFuturesOrderbookSnapshot(event string, incoming []byte,
func (g *Gateio) processFuturesOrdersPushData(data []byte, assetType asset.Item) ([]order.Detail, error) {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesOrder `json:"result"`
@@ -567,7 +568,7 @@ func (g *Gateio) procesFuturesUserTrades(data []byte, assetType asset.Item) erro
}
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesUserTrade `json:"result"`
@@ -594,7 +595,7 @@ func (g *Gateio) procesFuturesUserTrades(data []byte, assetType asset.Item) erro
func (g *Gateio) processFuturesLiquidatesNotification(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesLiquidationNotification `json:"result"`
@@ -609,7 +610,7 @@ func (g *Gateio) processFuturesLiquidatesNotification(data []byte) error {
func (g *Gateio) processFuturesAutoDeleveragesNotification(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesAutoDeleveragesNotification `json:"result"`
@@ -624,7 +625,7 @@ func (g *Gateio) processFuturesAutoDeleveragesNotification(data []byte) error {
func (g *Gateio) processPositionCloseData(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsPositionClose `json:"result"`
@@ -639,7 +640,7 @@ func (g *Gateio) processPositionCloseData(data []byte) error {
func (g *Gateio) processBalancePushData(ctx context.Context, data []byte, assetType asset.Item) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsBalance `json:"result"`
@@ -675,7 +676,7 @@ func (g *Gateio) processBalancePushData(ctx context.Context, data []byte, assetT
func (g *Gateio) processFuturesReduceRiskLimitNotification(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesReduceRiskLimitNotification `json:"result"`
@@ -690,7 +691,7 @@ func (g *Gateio) processFuturesReduceRiskLimitNotification(data []byte) error {
func (g *Gateio) processFuturesPositionsNotification(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesPosition `json:"result"`
@@ -705,7 +706,7 @@ func (g *Gateio) processFuturesPositionsNotification(data []byte) error {
func (g *Gateio) processFuturesAutoOrderPushData(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesAutoOrder `json:"result"`

View File

@@ -23,6 +23,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/log"
"github.com/thrasher-corp/gocryptotrader/types"
)
const (
@@ -387,7 +388,7 @@ func (g *Gateio) processOptionsTradesPushData(data []byte) error {
return nil
}
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsOptionsTrades `json:"result"`
@@ -453,7 +454,7 @@ func (g *Gateio) processOptionsContractPushData(incoming []byte) error {
func (g *Gateio) processOptionsCandlestickPushData(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsOptionsContractCandlestick `json:"result"`
@@ -604,7 +605,7 @@ func (g *Gateio) processOptionsOrderbookSnapshotPushData(event string, incoming
func (g *Gateio) processOptionsOrderPushData(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsOptionsOrder `json:"result"`
@@ -646,7 +647,7 @@ func (g *Gateio) processOptionsUserTradesPushData(data []byte) error {
return nil
}
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsOptionsUserTrade `json:"result"`
@@ -672,7 +673,7 @@ func (g *Gateio) processOptionsUserTradesPushData(data []byte) error {
func (g *Gateio) processOptionsLiquidatesPushData(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsOptionsLiquidates `json:"result"`
@@ -687,7 +688,7 @@ func (g *Gateio) processOptionsLiquidatesPushData(data []byte) error {
func (g *Gateio) processOptionsUsersPersonalSettlementsPushData(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsOptionsUserSettlement `json:"result"`
@@ -702,7 +703,7 @@ func (g *Gateio) processOptionsUsersPersonalSettlementsPushData(data []byte) err
func (g *Gateio) processOptionsPositionPushData(data []byte) error {
resp := struct {
Time int64 `json:"time"`
Time types.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsOptionsPosition `json:"result"`