mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-31 15:10:42 +00:00
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:
@@ -58,7 +58,6 @@ const (
|
||||
|
||||
bitstampRateInterval = time.Minute * 10
|
||||
bitstampRequestRate = 8000
|
||||
bitstampTimeLayout = "2006-1-2 15:04:05"
|
||||
)
|
||||
|
||||
// Bitstamp is the overarching type across the bitstamp package
|
||||
@@ -260,53 +259,14 @@ func (b *Bitstamp) GetBalance(ctx context.Context) (Balances, error) {
|
||||
|
||||
// GetUserTransactions returns an array of transactions
|
||||
func (b *Bitstamp) GetUserTransactions(ctx context.Context, currencyPair string) ([]UserTransactions, error) {
|
||||
type Response struct {
|
||||
Date string `json:"datetime"`
|
||||
TransactionID int64 `json:"id"`
|
||||
Type int64 `json:"type,string"`
|
||||
USD types.Number `json:"usd"`
|
||||
EUR types.Number `json:"eur"`
|
||||
XRP types.Number `json:"xrp"`
|
||||
BTC types.Number `json:"btc"`
|
||||
BTCUSD types.Number `json:"btc_usd"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
var response []Response
|
||||
var resp []UserTransactions
|
||||
var err error
|
||||
if currencyPair == "" {
|
||||
if err := b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUserTransactions,
|
||||
true,
|
||||
url.Values{},
|
||||
&response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUserTransactions, true, url.Values{}, &resp)
|
||||
} else {
|
||||
if err := b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUserTransactions+"/"+currencyPair,
|
||||
true,
|
||||
url.Values{},
|
||||
&response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, bitstampAPIUserTransactions+"/"+currencyPair, true, url.Values{}, &resp)
|
||||
}
|
||||
|
||||
transactions := make([]UserTransactions, len(response))
|
||||
for x := range response {
|
||||
transactions[x] = UserTransactions{
|
||||
Date: response[x].Date,
|
||||
TransactionID: response[x].TransactionID,
|
||||
Type: response[x].Type,
|
||||
EUR: response[x].EUR.Float64(),
|
||||
XRP: response[x].XRP.Float64(),
|
||||
USD: response[x].USD.Float64(),
|
||||
BTC: response[x].BTC.Float64(),
|
||||
BTCUSD: response[x].BTCUSD.Float64(),
|
||||
Fee: response[x].Fee,
|
||||
OrderID: response[x].OrderID,
|
||||
}
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// GetOpenOrders returns all open orders on the exchange
|
||||
@@ -635,10 +595,6 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange
|
||||
return json.Unmarshal(interim, result)
|
||||
}
|
||||
|
||||
func parseTime(dateTime string) (time.Time, error) {
|
||||
return time.Parse(bitstampTimeLayout, dateTime)
|
||||
}
|
||||
|
||||
func filterOrderbookZeroBidPrice(ob *orderbook.Book) {
|
||||
if len(ob.Bids) == 0 || ob.Bids[len(ob.Bids)-1].Price != 0 {
|
||||
return
|
||||
|
||||
@@ -364,11 +364,11 @@ func TestGetOpenOrders(t *testing.T) {
|
||||
if mockTests {
|
||||
assert.NotEmpty(t, o, "Orders should not be empty")
|
||||
for _, res := range o {
|
||||
assert.Equal(t, "2022-01-31 14:43:15", res.DateTime, "DateTime should match")
|
||||
assert.Equal(t, time.Date(2022, 1, 31, 14, 43, 15, 0, time.UTC), res.DateTime.Time(), "DateTime should match")
|
||||
assert.Equal(t, int64(1234123412341234), res.ID, "ID should match")
|
||||
assert.Equal(t, 0.50000000, res.Amount, "Amount should match")
|
||||
assert.Equal(t, 100.00, res.Price, "Price should match")
|
||||
assert.Equal(t, 0, res.Type, "Type should match")
|
||||
assert.Equal(t, int64(0), res.Type, "Type should match")
|
||||
assert.Equal(t, 0.50000000, res.AmountAtCreate, "AmountAtCreate should match")
|
||||
assert.Equal(t, 110.00, res.LimitPrice, "LimitPrice should match")
|
||||
assert.Equal(t, "1234123412341234", res.ClientOrderID, "ClientOrderID should match")
|
||||
@@ -388,14 +388,14 @@ func TestGetOrderStatus(t *testing.T) {
|
||||
assert.ErrorContains(t, err, "Order not found")
|
||||
} else {
|
||||
require.NoError(t, err, "GetOrderStatus must not error")
|
||||
assert.Equal(t, "2022-01-31 14:43:15", o.DateTime, "DateTime should match")
|
||||
assert.Equal(t, time.Date(2022, 1, 31, 14, 43, 15, 0, time.UTC), o.DateTime.Time(), "DateTime should match")
|
||||
assert.Equal(t, "1458532827766784", o.ID, "OrderID should match")
|
||||
assert.Equal(t, 200.00, o.AmountRemaining, "AmountRemaining should match")
|
||||
assert.Equal(t, int64(0), o.Type, "Type should match")
|
||||
assert.Equal(t, "0.50000000", o.ClientOrderID, "ClientOrderID should match")
|
||||
assert.Equal(t, "BTC/USD", o.Market, "Market should match")
|
||||
for _, tr := range o.Transactions {
|
||||
assert.Equal(t, "2022-01-31 14:43:15", tr.DateTime, "DateTime should match")
|
||||
assert.Equal(t, time.Date(2022, 1, 31, 14, 43, 15, 0, time.UTC), tr.DateTime.Time(), "DateTime should match")
|
||||
assert.Equal(t, 50.00, tr.Price, "Price should match")
|
||||
assert.Equal(t, 101.00, tr.FromCurrency, "FromCurrency should match")
|
||||
assert.Equal(t, 1.0, tr.ToCurrency, "ToCurrency should match")
|
||||
@@ -417,7 +417,7 @@ func TestGetWithdrawalRequests(t *testing.T) {
|
||||
for _, req := range r {
|
||||
assert.Equal(t, int64(1), req.OrderID, "OrderId should match")
|
||||
assert.Equal(t, "aMDHooGmAkyrsaQiKhAORhSNTmoRzxqWIO", req.Address, "Address should match")
|
||||
assert.Equal(t, "2022-01-31 16:07:32", req.Date, "Date should match")
|
||||
assert.Equal(t, time.Date(2022, 1, 31, 16, 7, 32, 0, time.UTC), req.Date.Time(), "Date should match")
|
||||
assert.Equal(t, currency.BTC, req.Currency, "Currency should match")
|
||||
assert.Equal(t, 0.00006000, req.Amount, "Amount should match")
|
||||
assert.Equal(t, "NsOeFbQhRnpGzNIThWGBTkQwRJqTNOGPVhYavrVyMfkAyMUmIlUpFIwGTzSvpeOP", req.TransactionID, "TransactionID should match")
|
||||
@@ -713,24 +713,6 @@ func TestGetDepositAddress(t *testing.T) {
|
||||
assert.NotEmpty(t, a.Tag, "Tag should not be empty")
|
||||
}
|
||||
|
||||
func TestParseTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tm, err := parseTime("2019-10-18 01:55:14")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if tm.Year() != 2019 ||
|
||||
tm.Month() != 10 ||
|
||||
tm.Day() != 18 ||
|
||||
tm.Hour() != 1 ||
|
||||
tm.Minute() != 55 ||
|
||||
tm.Second() != 14 {
|
||||
t.Error("invalid time values")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWsSubscription(t *testing.T) {
|
||||
pressXToJSON := []byte(`{
|
||||
"event": "bts:subscribe",
|
||||
|
||||
@@ -23,18 +23,18 @@ const (
|
||||
|
||||
// Ticker holds ticker information
|
||||
type Ticker struct {
|
||||
Last float64 `json:"last,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Vwap float64 `json:"vwap,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
Bid float64 `json:"bid,string"`
|
||||
Ask float64 `json:"ask,string"`
|
||||
Timestamp int64 `json:"timestamp,string"`
|
||||
Open float64 `json:"open,string"`
|
||||
Open24 float64 `json:"open_24,string"`
|
||||
Side orderSide `json:"side,string"`
|
||||
PercentChange24 float64 `json:"percent_change_24,string"`
|
||||
Last float64 `json:"last,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Vwap float64 `json:"vwap,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
Bid float64 `json:"bid,string"`
|
||||
Ask float64 `json:"ask,string"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
Open float64 `json:"open,string"`
|
||||
Open24 float64 `json:"open_24,string"`
|
||||
Side orderSide `json:"side,string"`
|
||||
PercentChange24 float64 `json:"percent_change_24,string"`
|
||||
}
|
||||
|
||||
// OrderbookBase holds singular price information
|
||||
@@ -63,11 +63,11 @@ type TradingPair struct {
|
||||
|
||||
// Transactions holds transaction data
|
||||
type Transactions struct {
|
||||
Date int64 `json:"date,string"`
|
||||
TradeID int64 `json:"tid,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Type int `json:"type,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Date types.Time `json:"date"`
|
||||
TradeID int64 `json:"tid,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Type int `json:"type,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
}
|
||||
|
||||
// EURUSDConversionRate holds buy sell conversion rate information
|
||||
@@ -101,49 +101,49 @@ type Balances map[string]Balance
|
||||
|
||||
// UserTransactions holds user transaction information
|
||||
type UserTransactions struct {
|
||||
Date string `json:"datetime"`
|
||||
TransactionID int64 `json:"id"`
|
||||
Type int64 `json:"type,string"`
|
||||
USD float64 `json:"usd"`
|
||||
EUR float64 `json:"eur"`
|
||||
BTC float64 `json:"btc"`
|
||||
XRP float64 `json:"xrp"`
|
||||
BTCUSD float64 `json:"btc_usd"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
Date types.DateTime `json:"datetime"`
|
||||
TransactionID int64 `json:"id"`
|
||||
Type int64 `json:"type,string"`
|
||||
USD types.Number `json:"usd"`
|
||||
EUR types.Number `json:"eur"`
|
||||
BTC types.Number `json:"btc"`
|
||||
XRP types.Number `json:"xrp"`
|
||||
BTCUSD types.Number `json:"btc_usd"`
|
||||
Fee types.Number `json:"fee"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
}
|
||||
|
||||
// Order holds current open order data
|
||||
type Order struct {
|
||||
ID int64 `json:"id,string"`
|
||||
DateTime string `json:"datetime"`
|
||||
Type int `json:"type,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
AmountAtCreate float64 `json:"amount_at_create,string"`
|
||||
Currency string `json:"currency_pair"`
|
||||
LimitPrice float64 `json:"limit_price,string"`
|
||||
ClientOrderID string `json:"client_order_id"`
|
||||
Market string `json:"market"`
|
||||
ID int64 `json:"id,string"`
|
||||
DateTime types.DateTime `json:"datetime"`
|
||||
Type int64 `json:"type,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
AmountAtCreate float64 `json:"amount_at_create,string"`
|
||||
Currency string `json:"currency_pair"`
|
||||
LimitPrice float64 `json:"limit_price,string"`
|
||||
ClientOrderID string `json:"client_order_id"`
|
||||
Market string `json:"market"`
|
||||
}
|
||||
|
||||
// OrderStatus holds order status information
|
||||
type OrderStatus struct {
|
||||
AmountRemaining float64 `json:"amount_remaining,string"`
|
||||
Type int64 `json:"type"`
|
||||
ID string `json:"id"`
|
||||
DateTime string `json:"datetime"`
|
||||
Status string `json:"status"`
|
||||
ClientOrderID string `json:"client_order_id"`
|
||||
Market string `json:"market"`
|
||||
AmountRemaining float64 `json:"amount_remaining,string"`
|
||||
Type int64 `json:"type"`
|
||||
ID string `json:"id"`
|
||||
DateTime types.DateTime `json:"datetime"`
|
||||
Status string `json:"status"`
|
||||
ClientOrderID string `json:"client_order_id"`
|
||||
Market string `json:"market"`
|
||||
Transactions []struct {
|
||||
TradeID int64 `json:"tid"`
|
||||
FromCurrency float64 `json:"{from_currency},string"`
|
||||
ToCurrency float64 `json:"{to_currency},string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
DateTime string `json:"datetime"`
|
||||
Type int `json:"type"`
|
||||
TradeID int64 `json:"tid"`
|
||||
FromCurrency float64 `json:"{from_currency},string"`
|
||||
ToCurrency float64 `json:"{to_currency},string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
DateTime types.DateTime `json:"datetime"`
|
||||
Type int64 `json:"type"`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,16 +163,16 @@ type DepositAddress struct {
|
||||
|
||||
// WithdrawalRequests holds request information on withdrawals
|
||||
type WithdrawalRequests struct {
|
||||
OrderID int64 `json:"id"`
|
||||
Date string `json:"datetime"`
|
||||
Type int64 `json:"type"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Status int64 `json:"status"`
|
||||
Currency currency.Code `json:"currency"`
|
||||
Address string `json:"address"`
|
||||
TransactionID string `json:"transaction_id"`
|
||||
Network string `json:"network"`
|
||||
TxID int64 `json:"txid"`
|
||||
OrderID int64 `json:"id"`
|
||||
Date types.DateTime `json:"datetime"`
|
||||
Type int64 `json:"type"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Status int64 `json:"status"`
|
||||
Currency currency.Code `json:"currency"`
|
||||
Address string `json:"address"`
|
||||
TransactionID string `json:"transaction_id"`
|
||||
Network string `json:"network"`
|
||||
TxID int64 `json:"txid"`
|
||||
}
|
||||
|
||||
// CryptoWithdrawalResponse response from a crypto withdrawal request
|
||||
@@ -228,16 +228,16 @@ type websocketTradeResponse struct {
|
||||
}
|
||||
|
||||
type websocketTradeData struct {
|
||||
Microtimestamp string `json:"microtimestamp"`
|
||||
Amount float64 `json:"amount"`
|
||||
BuyOrderID int64 `json:"buy_order_id"`
|
||||
SellOrderID int64 `json:"sell_order_id"`
|
||||
AmountStr string `json:"amount_str"`
|
||||
PriceStr string `json:"price_str"`
|
||||
Timestamp int64 `json:"timestamp,string"`
|
||||
Price float64 `json:"price"`
|
||||
Type int `json:"type"`
|
||||
ID int64 `json:"id"`
|
||||
Microtimestamp string `json:"microtimestamp"`
|
||||
Amount float64 `json:"amount"`
|
||||
BuyOrderID int64 `json:"buy_order_id"`
|
||||
SellOrderID int64 `json:"sell_order_id"`
|
||||
AmountStr string `json:"amount_str"`
|
||||
PriceStr string `json:"price_str"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
Price float64 `json:"price"`
|
||||
Type int `json:"type"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// WebsocketAuthResponse holds the auth token for subscribing to auth channels
|
||||
|
||||
@@ -158,7 +158,7 @@ func (b *Bitstamp) handleWSTrade(msg []byte) error {
|
||||
side = order.Sell
|
||||
}
|
||||
return trade.AddTradesToBuffer(trade.Data{
|
||||
Timestamp: time.Unix(wsTradeTemp.Data.Timestamp, 0),
|
||||
Timestamp: wsTradeTemp.Data.Timestamp.Time(),
|
||||
CurrencyPair: p,
|
||||
AssetType: asset.Spot,
|
||||
Exchange: b.Name,
|
||||
|
||||
@@ -264,7 +264,7 @@ func (b *Bitstamp) UpdateTicker(ctx context.Context, p currency.Pair, a asset.It
|
||||
Volume: tick.Volume,
|
||||
Open: tick.Open,
|
||||
Pair: fPair,
|
||||
LastUpdated: time.Unix(tick.Timestamp, 0),
|
||||
LastUpdated: tick.Timestamp.Time(),
|
||||
ExchangeName: b.Name,
|
||||
AssetType: a,
|
||||
})
|
||||
@@ -386,15 +386,10 @@ func (b *Bitstamp) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _
|
||||
}
|
||||
resp := make([]exchange.WithdrawalHistory, 0, len(withdrawals))
|
||||
for i := range withdrawals {
|
||||
var tm time.Time
|
||||
tm, err = parseTime(withdrawals[i].Date)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getWithdrawalsHistory unable to parse Date field: %w", err)
|
||||
}
|
||||
if c.IsEmpty() || c.Equal(withdrawals[i].Currency) {
|
||||
resp = append(resp, exchange.WithdrawalHistory{
|
||||
Status: strconv.FormatInt(withdrawals[i].Status, 10),
|
||||
Timestamp: tm,
|
||||
Timestamp: withdrawals[i].Date.Time(),
|
||||
Currency: withdrawals[i].Currency.String(),
|
||||
Amount: withdrawals[i].Amount,
|
||||
TransferType: strconv.FormatInt(withdrawals[i].Type, 10),
|
||||
@@ -432,7 +427,7 @@ func (b *Bitstamp) GetRecentTrades(ctx context.Context, p currency.Pair, assetTy
|
||||
Side: s,
|
||||
Price: tradeData[i].Price,
|
||||
Amount: tradeData[i].Amount,
|
||||
Timestamp: time.Unix(tradeData[i].Date, 0),
|
||||
Timestamp: tradeData[i].Date.Time(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,10 +526,6 @@ func (b *Bitstamp) GetOrderInfo(ctx context.Context, orderID string, _ currency.
|
||||
Amount: o.Transactions[i].ToCurrency,
|
||||
}
|
||||
}
|
||||
orderDate, err := time.Parse(time.DateTime, o.DateTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status, err := order.StringToOrderStatus(o.Status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -542,7 +533,7 @@ func (b *Bitstamp) GetOrderInfo(ctx context.Context, orderID string, _ currency.
|
||||
return &order.Detail{
|
||||
RemainingAmount: o.AmountRemaining,
|
||||
OrderID: o.ID,
|
||||
Date: orderDate,
|
||||
Date: o.DateTime.Time(),
|
||||
Trades: th,
|
||||
Status: status,
|
||||
}, nil
|
||||
@@ -677,13 +668,6 @@ func (b *Bitstamp) GetActiveOrders(ctx context.Context, req *order.MultiOrderReq
|
||||
orderSide = order.Sell
|
||||
}
|
||||
|
||||
var tm time.Time
|
||||
tm, err = parseTime(resp[i].DateTime)
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys,
|
||||
"%s GetActiveOrders unable to parse time: %s\n", b.Name, err)
|
||||
}
|
||||
|
||||
var p currency.Pair
|
||||
if currPair == "all" {
|
||||
// Currency pairs are returned as format "currency_pair": "BTC/USD"
|
||||
@@ -702,7 +686,7 @@ func (b *Bitstamp) GetActiveOrders(ctx context.Context, req *order.MultiOrderReq
|
||||
Price: resp[i].Price,
|
||||
Type: order.Limit,
|
||||
Side: orderSide,
|
||||
Date: tm,
|
||||
Date: resp[i].DateTime.Time(),
|
||||
Pair: p,
|
||||
Exchange: b.Name,
|
||||
}
|
||||
@@ -776,16 +760,9 @@ func (b *Bitstamp) GetOrderHistory(ctx context.Context, req *order.MultiOrderReq
|
||||
format.Delimiter)
|
||||
}
|
||||
|
||||
var tm time.Time
|
||||
tm, err = parseTime(resp[i].Date)
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys,
|
||||
"%s GetOrderHistory unable to parse time: %s\n", b.Name, err)
|
||||
}
|
||||
|
||||
orders = append(orders, order.Detail{
|
||||
OrderID: strconv.FormatInt(resp[i].OrderID, 10),
|
||||
Date: tm,
|
||||
Date: resp[i].Date.Time(),
|
||||
Exchange: b.Name,
|
||||
Pair: currPair,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user