gateio: websocket updates (#1282)

* gateio: websocket updates

* Update exchanges/gateio/gateio_websocket.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* revert that trick

* glorious:nits

* Update exchanges/gateio/gateio_ws_futures.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_ws_futures.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_websocket.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_websocket.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_ws_option.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_ws_option.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_websocket.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_websocket.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* thrasher-nits

* Update exchanges/gateio/gateio_ws_futures.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_ws_futures.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* thrasher: nits rides again chapter 2 volume 1

* rm unmarshaljson method for orderbook

* use gateio time where it can and update tests

* math.trunc and lower time frame on big books

* :eagle

---------

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2023-08-04 17:39:38 +10:00
committed by GitHub
parent 1f786f01f5
commit 43a8044b44
11 changed files with 516 additions and 697 deletions

View File

@@ -45,13 +45,8 @@ func (p *Pair) UnmarshalJSON(d []byte) error {
return nil
}
newPair, err := NewPairFromString(pair)
if err != nil {
return err
}
*p = newPair
return nil
*p, err = NewPairFromString(pair)
return err
}
// MarshalJSON conforms type to the marshaler interface

View File

@@ -669,7 +669,7 @@ func (b *Bithumb) WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdr
if err := withdrawRequest.Validate(); err != nil {
return nil, err
}
if math.Mod(withdrawRequest.Amount, 1) != 0 {
if math.Trunc(withdrawRequest.Amount) != withdrawRequest.Amount {
return nil, errors.New("currency KRW does not support decimal places")
}
if !withdrawRequest.Currency.Equal(currency.KRW) {

View File

@@ -728,7 +728,7 @@ func (b *Bitmex) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Submi
return nil, err
}
if math.Mod(s.Amount, 1) != 0 {
if math.Trunc(s.Amount) != s.Amount {
return nil,
errors.New("order contract amount can not have decimals")
}
@@ -763,7 +763,7 @@ func (b *Bitmex) ModifyOrder(ctx context.Context, action *order.Modify) (*order.
return nil, err
}
if math.Mod(action.Amount, 1) != 0 {
if math.Trunc(action.Amount) != action.Amount {
return nil, errors.New("contract amount can not have decimals")
}

View File

@@ -3,6 +3,7 @@ package gateio
import (
"encoding/json"
"fmt"
"math"
"strconv"
"time"
)
@@ -19,11 +20,11 @@ func (a *gateioTime) UnmarshalJSON(data []byte) error {
var standard int64
switch val := value.(type) {
case float64:
standard = int64(val)
case int64:
standard = val
case int32:
standard = int64(val)
if math.Trunc(val) != val {
standard = int64(val * 1e3) // Account for 1684981731.098
} else {
standard = int64(val)
}
case string:
if val == "" {
return nil
@@ -32,6 +33,10 @@ func (a *gateioTime) UnmarshalJSON(data []byte) error {
if err != nil {
return err
}
if math.Trunc(parsedValue) != parsedValue {
*a = gateioTime(time.UnixMicro(int64(parsedValue * 1e3))) // Account for "1691122380942.173000" microseconds
return nil
}
standard = int64(parsedValue)
default:
return fmt.Errorf("cannot unmarshal %T into gateioTime", val)
@@ -45,7 +50,7 @@ func (a *gateioTime) UnmarshalJSON(data []byte) error {
}
// Time represents a time instance.
func (a *gateioTime) Time() time.Time { return time.Time(*a) }
func (a gateioTime) Time() time.Time { return time.Time(a) }
type gateioNumericalValue float64
@@ -75,59 +80,4 @@ func (a *gateioNumericalValue) UnmarshalJSON(data []byte) error {
}
// Float64 returns float64 value from gateioNumericalValue instance.
func (a *gateioNumericalValue) Float64() float64 { return float64(*a) }
// UnmarshalJSON to deserialize timestamp information and create OrderbookItem instance from the list of asks and bids data.
func (a *Orderbook) UnmarshalJSON(data []byte) error {
type Alias Orderbook
type askorbid struct {
Price gateioNumericalValue `json:"p"`
Size float64 `json:"s"`
}
chil := &struct {
*Alias
Current float64 `json:"current"`
Update float64 `json:"update"`
Asks []askorbid `json:"asks"`
Bids []askorbid `json:"bids"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, &chil)
if err != nil {
return err
}
a.Current = time.UnixMilli(int64(chil.Current * 1000))
a.Update = time.UnixMilli(int64(chil.Update * 1000))
a.Asks = make([]OrderbookItem, len(chil.Asks))
a.Bids = make([]OrderbookItem, len(chil.Bids))
for x := range chil.Asks {
a.Asks[x] = OrderbookItem{
Amount: chil.Asks[x].Size,
Price: chil.Asks[x].Price.Float64(),
}
}
for x := range chil.Bids {
a.Bids[x] = OrderbookItem{
Amount: chil.Bids[x].Size,
Price: chil.Bids[x].Price.Float64(),
}
}
return nil
}
// UnmarshalJSON deserialises the JSON info, including the timestamp
func (a *WsUserPersonalTrade) UnmarshalJSON(data []byte) error {
type Alias WsUserPersonalTrade
chil := &struct {
*Alias
CreateTimeMicroS float64 `json:"create_time_ms,string"`
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, chil); err != nil {
return err
}
a.CreateTimeMicroS = time.UnixMicro(int64(chil.CreateTimeMicroS * 1000))
return nil
}
func (a gateioNumericalValue) Float64() float64 { return float64(a) }

View File

@@ -3206,9 +3206,9 @@ func TestSettlement(t *testing.T) {
func TestParseGateioMilliSecTimeUnmarshal(t *testing.T) {
t.Parallel()
var timeWhenTesting int64 = 1684981731098
timeWhenTestingString := "1684981731098"
timeWhenTestingString := `"1684981731098"` // Normal string
integerJSON := `{"number": 1684981731098}`
float64JSON := `{"number": 1684981731098.234}`
float64JSON := `{"number": 1684981731.098}`
time := time.UnixMilli(timeWhenTesting)
var in gateioTime
@@ -3245,18 +3245,19 @@ func TestParseGateioMilliSecTimeUnmarshal(t *testing.T) {
func TestParseGateioTimeUnmarshal(t *testing.T) {
t.Parallel()
var timeWhenTesting int64 = 1684981731
timeWhenTestingString := "1684981731"
timeWhenTestingString := `"1684981731"`
integerJSON := `{"number": 1684981731}`
float64JSON := `{"number": 1684981731.234}`
timeWhenTestingStringMicroSecond := `"1691122380942.173000"`
time := time.Unix(timeWhenTesting, 0)
whenTime := time.Unix(timeWhenTesting, 0)
var in gateioTime
err := json.Unmarshal([]byte(timeWhenTestingString), &in)
if err != nil {
t.Fatal(err)
}
if !in.Time().Equal(time) {
t.Fatalf("found %v, but expected %v", in.Time(), time)
if !in.Time().Equal(whenTime) {
t.Fatalf("found %v, but expected %v", in.Time(), whenTime)
}
inInteger := struct {
Number gateioTime `json:"number"`
@@ -3265,8 +3266,8 @@ func TestParseGateioTimeUnmarshal(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !inInteger.Number.Time().Equal(time) {
t.Fatalf("found %v, but expected %v", inInteger.Number.Time(), time)
if !inInteger.Number.Time().Equal(whenTime) {
t.Fatalf("found %v, but expected %v", inInteger.Number.Time(), whenTime)
}
inFloat64 := struct {
@@ -3276,8 +3277,18 @@ func TestParseGateioTimeUnmarshal(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !inFloat64.Number.Time().Equal(time) {
t.Fatalf("found %v, but expected %v", inFloat64.Number.Time(), time)
msTime := time.UnixMilli(1684981731234)
if !inFloat64.Number.Time().Equal(time.UnixMilli(1684981731234)) {
t.Fatalf("found %v, but expected %v", inFloat64.Number.Time(), msTime)
}
var microSeconds gateioTime
err = json.Unmarshal([]byte(timeWhenTestingStringMicroSecond), &microSeconds)
if err != nil {
t.Fatal(err)
}
if !microSeconds.Time().Equal(time.UnixMicro(1691122380942173)) {
t.Fatalf("found %v, but expected %v", microSeconds.Time(), time.UnixMicro(1691122380942173))
}
}

View File

@@ -1,6 +1,7 @@
package gateio
import (
"encoding/json"
"strconv"
"time"
@@ -535,8 +536,8 @@ type OrderbookData struct {
func (a *OrderbookData) MakeOrderbook() (*Orderbook, error) {
ob := &Orderbook{
ID: a.ID,
Current: a.Current.Time(),
Update: a.Update.Time(),
Current: a.Current,
Update: a.Update,
}
ob.Asks = make([]OrderbookItem, len(a.Asks))
ob.Bids = make([]OrderbookItem, len(a.Bids))
@@ -550,7 +551,7 @@ func (a *OrderbookData) MakeOrderbook() (*Orderbook, error) {
return nil, err
}
ob.Asks[x] = OrderbookItem{
Price: price,
Price: gateioNumericalValue(price),
Amount: amount,
}
}
@@ -564,7 +565,7 @@ func (a *OrderbookData) MakeOrderbook() (*Orderbook, error) {
return nil, err
}
ob.Bids[x] = OrderbookItem{
Price: price,
Price: gateioNumericalValue(price),
Amount: amount,
}
}
@@ -573,15 +574,15 @@ func (a *OrderbookData) MakeOrderbook() (*Orderbook, error) {
// OrderbookItem stores an orderbook item
type OrderbookItem struct {
Price float64 `json:"p"`
Amount float64 `json:"s"`
Price gateioNumericalValue `json:"p"`
Amount float64 `json:"s"`
}
// Orderbook stores the orderbook data
type Orderbook struct {
ID int64 `json:"id"`
Current time.Time `json:"current"` // The timestamp of the response data being generated (in milliseconds)
Update time.Time `json:"update"` // The timestamp of when the orderbook last changed (in milliseconds)
Current gateioTime `json:"current"` // The timestamp of the response data being generated (in milliseconds)
Update gateioTime `json:"update"` // The timestamp of when the orderbook last changed (in milliseconds)
Bids []OrderbookItem `json:"bids"`
Asks []OrderbookItem `json:"asks"`
}
@@ -904,20 +905,20 @@ type SwapCurrencies struct {
// MyOptionSettlement represents option private settlement
type MyOptionSettlement struct {
Size float64 `json:"size"`
SettleProfit float64 `json:"settle_profit,string"`
Contract string `json:"contract"`
StrikePrice float64 `json:"strike_price,string"`
Time time.Time `json:"time"`
SettlePrice float64 `json:"settle_price,string"`
Underlying string `json:"underlying"`
RealisedPnl string `json:"realised_pnl"`
Fee float64 `json:"fee,string"`
Size float64 `json:"size"`
SettleProfit float64 `json:"settle_profit,string"`
Contract string `json:"contract"`
StrikePrice float64 `json:"strike_price,string"`
Time gateioTime `json:"time"`
SettlePrice float64 `json:"settle_price,string"`
Underlying string `json:"underlying"`
RealisedPnl string `json:"realised_pnl"`
Fee float64 `json:"fee,string"`
}
// OptionsTicker represents tickers of options contracts
type OptionsTicker struct {
Name string `json:"name"`
Name currency.Pair `json:"name"`
LastPrice gateioNumericalValue `json:"last_price"`
MarkPrice gateioNumericalValue `json:"mark_price"`
PositionSize float64 `json:"position_size"`
@@ -1088,16 +1089,16 @@ type MultiChainAddressItem struct {
// DepositRecord represents deposit record item
type DepositRecord struct {
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
Currency string `json:"currency"`
Address string `json:"address"`
TransactionID string `json:"txid"`
Amount float64 `json:"amount,string"`
Memo string `json:"memo"`
Status string `json:"status"`
Chain string `json:"chain"`
Fee float64 `json:"fee,string"`
ID string `json:"id"`
Timestamp gateioTime `json:"timestamp"`
Currency string `json:"currency"`
Address string `json:"address"`
TransactionID string `json:"txid"`
Amount float64 `json:"amount,string"`
Memo string `json:"memo"`
Status string `json:"status"`
Chain string `json:"chain"`
Fee float64 `json:"fee,string"`
}
// TransferCurrencyParam represents currency transfer.
@@ -1989,35 +1990,35 @@ type WsEventResponse struct {
// WsResponse represents generalized websocket push data from the server.
type WsResponse struct {
ID int64 `json:"id"`
Time int64 `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result interface{} `json:"result"`
ID int64 `json:"id"`
Time int64 `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result json.RawMessage `json:"result"`
}
// WsTicker websocket ticker information.
type WsTicker struct {
CurrencyPair string `json:"currency_pair"`
Last float64 `json:"last,string"`
LowestAsk float64 `json:"lowest_ask,string"`
HighestBid float64 `json:"highest_bid,string"`
ChangePercentage float64 `json:"change_percentage,string"`
BaseVolume float64 `json:"base_volume,string"`
QuoteVolume float64 `json:"quote_volume,string"`
High24H float64 `json:"high_24h,string"`
Low24H float64 `json:"low_24h,string"`
CurrencyPair currency.Pair `json:"currency_pair"`
Last float64 `json:"last,string"`
LowestAsk float64 `json:"lowest_ask,string"`
HighestBid float64 `json:"highest_bid,string"`
ChangePercentage float64 `json:"change_percentage,string"`
BaseVolume float64 `json:"base_volume,string"`
QuoteVolume float64 `json:"quote_volume,string"`
High24H float64 `json:"high_24h,string"`
Low24H float64 `json:"low_24h,string"`
}
// WsTrade represents a websocket push data response for a trade
type WsTrade struct {
ID int64 `json:"id"`
CreateTime int64 `json:"create_time"`
CreateTimeMs float64 `json:"create_time_ms,string"`
Side string `json:"side"`
CurrencyPair string `json:"currency_pair"`
Amount float64 `json:"amount,string"`
Price float64 `json:"price,string"`
ID int64 `json:"id"`
CreateTime gateioTime `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
Side string `json:"side"`
CurrencyPair currency.Pair `json:"currency_pair"`
Amount float64 `json:"amount,string"`
Price float64 `json:"price,string"`
}
// WsCandlesticks represents the candlestick data for spot, margin and cross margin trades pushed through the websocket channel.
@@ -2033,34 +2034,34 @@ type WsCandlesticks struct {
// WsOrderbookTickerData represents the websocket orderbook best bid or best ask push data
type WsOrderbookTickerData struct {
UpdateTimeMS int64 `json:"t"`
UpdateOrderID int64 `json:"u"`
CurrencyPair string `json:"s"`
BestBidPrice float64 `json:"b,string"`
BestBidAmount float64 `json:"B,string"`
BestAskPrice float64 `json:"a,string"`
BestAskAmount float64 `json:"A,string"`
UpdateTimeMS int64 `json:"t"`
UpdateOrderID int64 `json:"u"`
CurrencyPair currency.Pair `json:"s"`
BestBidPrice float64 `json:"b,string"`
BestBidAmount float64 `json:"B,string"`
BestAskPrice float64 `json:"a,string"`
BestAskAmount float64 `json:"A,string"`
}
// WsOrderbookUpdate represents websocket orderbook update push data
type WsOrderbookUpdate struct {
UpdateTimeMs gateioTime `json:"t"`
IgnoreField string `json:"e"`
UpdateTime gateioTime `json:"E"`
CurrencyPair string `json:"s"`
FirstOrderbookUpdatedID int64 `json:"U"` // First update order book id in this event since last update
LastOrderbookUpdatedID int64 `json:"u"`
Bids [][2]string `json:"b"`
Asks [][2]string `json:"a"`
UpdateTimeMs gateioTime `json:"t"`
IgnoreField string `json:"e"`
UpdateTime gateioTime `json:"E"`
CurrencyPair currency.Pair `json:"s"`
FirstOrderbookUpdatedID int64 `json:"U"` // First update order book id in this event since last update
LastOrderbookUpdatedID int64 `json:"u"`
Bids [][2]string `json:"b"`
Asks [][2]string `json:"a"`
}
// WsOrderbookSnapshot represents a websocket orderbook snapshot push data
type WsOrderbookSnapshot struct {
UpdateTimeMs gateioTime `json:"t"`
LastUpdateID int64 `json:"lastUpdateId"`
CurrencyPair string `json:"s"`
Bids [][2]string `json:"bids"`
Asks [][2]string `json:"asks"`
UpdateTimeMs gateioTime `json:"t"`
LastUpdateID int64 `json:"lastUpdateId"`
CurrencyPair currency.Pair `json:"s"`
Bids [][2]string `json:"bids"`
Asks [][2]string `json:"asks"`
}
// WsSpotOrder represents an order push data through the websocket channel.
@@ -2071,7 +2072,7 @@ type WsSpotOrder struct {
Succeeded bool `json:"succeeded,omitempty"`
Label string `json:"label,omitempty"`
Message string `json:"message,omitempty"`
CurrencyPair string `json:"currency_pair,omitempty"`
CurrencyPair currency.Pair `json:"currency_pair,omitempty"`
Type string `json:"type,omitempty"`
Account string `json:"account,omitempty"`
Side string `json:"side,omitempty"`
@@ -2097,20 +2098,20 @@ type WsSpotOrder struct {
// WsUserPersonalTrade represents a user's personal trade pushed through the websocket connection.
type WsUserPersonalTrade struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
OrderID string `json:"order_id"`
CurrencyPair string `json:"currency_pair"`
CreateTime int64 `json:"create_time"`
CreateTimeMicroS time.Time `json:"create_time_ms"`
Side string `json:"side"`
Amount float64 `json:"amount,string"`
Role string `json:"role"`
Price float64 `json:"price,string"`
Fee float64 `json:"fee,string"`
PointFee float64 `json:"point_fee,string"`
GtFee string `json:"gt_fee"`
Text string `json:"text"`
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
OrderID string `json:"order_id"`
CurrencyPair currency.Pair `json:"currency_pair"`
CreateTime int64 `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
Side string `json:"side"`
Amount float64 `json:"amount,string"`
Role string `json:"role"`
Price float64 `json:"price,string"`
Fee float64 `json:"fee,string"`
PointFee float64 `json:"point_fee,string"`
GtFee string `json:"gt_fee"`
Text string `json:"text"`
}
// WsSpotBalance represents a spot balance.
@@ -2174,33 +2175,33 @@ type WsCrossMarginLoan struct {
// WsFutureTicker represents a futures push data.
type WsFutureTicker struct {
Contract string `json:"contract"`
Last float64 `json:"last,string"`
ChangePercentage string `json:"change_percentage"`
FundingRate string `json:"funding_rate"`
FundingRateIndicative string `json:"funding_rate_indicative"`
MarkPrice float64 `json:"mark_price,string"`
IndexPrice float64 `json:"index_price,string"`
TotalSize float64 `json:"total_size,string"`
Volume24H float64 `json:"volume_24h,string"`
Volume24HBtc float64 `json:"volume_24h_btc,string"`
Volume24HUsd float64 `json:"volume_24h_usd,string"`
QuantoBaseRate string `json:"quanto_base_rate"`
Volume24HQuote float64 `json:"volume_24h_quote,string"`
Volume24HSettle string `json:"volume_24h_settle"`
Volume24HBase float64 `json:"volume_24h_base,string"`
Low24H float64 `json:"low_24h,string"`
High24H float64 `json:"high_24h,string"`
Contract currency.Pair `json:"contract"`
Last float64 `json:"last,string"`
ChangePercentage string `json:"change_percentage"`
FundingRate string `json:"funding_rate"`
FundingRateIndicative string `json:"funding_rate_indicative"`
MarkPrice float64 `json:"mark_price,string"`
IndexPrice float64 `json:"index_price,string"`
TotalSize float64 `json:"total_size,string"`
Volume24H float64 `json:"volume_24h,string"`
Volume24HBtc float64 `json:"volume_24h_btc,string"`
Volume24HUsd float64 `json:"volume_24h_usd,string"`
QuantoBaseRate string `json:"quanto_base_rate"`
Volume24HQuote float64 `json:"volume_24h_quote,string"`
Volume24HSettle string `json:"volume_24h_settle"`
Volume24HBase float64 `json:"volume_24h_base,string"`
Low24H float64 `json:"low_24h,string"`
High24H float64 `json:"high_24h,string"`
}
// WsFuturesTrades represents a list of trades push data
type WsFuturesTrades struct {
Size float64 `json:"size"`
ID int64 `json:"id"`
CreateTime gateioTime `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
Price float64 `json:"price,string"`
Contract string `json:"contract"`
Size float64 `json:"size"`
ID int64 `json:"id"`
CreateTime gateioTime `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
Price float64 `json:"price,string"`
Contract currency.Pair `json:"contract"`
}
// WsFuturesOrderbookTicker represents the orderbook ticker push data
@@ -2216,10 +2217,10 @@ type WsFuturesOrderbookTicker struct {
// WsFuturesAndOptionsOrderbookUpdate represents futures and options account orderbook update push data
type WsFuturesAndOptionsOrderbookUpdate struct {
TimestampInMs int64 `json:"t"`
ContractName string `json:"s"`
FirstUpdatedID int64 `json:"U"`
LastUpdatedID int64 `json:"u"`
TimestampInMs int64 `json:"t"`
ContractName currency.Pair `json:"s"`
FirstUpdatedID int64 `json:"U"`
LastUpdatedID int64 `json:"u"`
Bids []struct {
Price float64 `json:"p,string"`
Size float64 `json:"s"`
@@ -2232,9 +2233,9 @@ type WsFuturesAndOptionsOrderbookUpdate struct {
// WsFuturesOrderbookSnapshot represents a futures orderbook snapshot push data
type WsFuturesOrderbookSnapshot struct {
TimestampInMs gateioTime `json:"t"`
Contract string `json:"contract"`
OrderbookID int64 `json:"id"`
TimestampInMs gateioTime `json:"t"`
Contract currency.Pair `json:"contract"`
OrderbookID int64 `json:"id"`
Asks []struct {
Price float64 `json:"p,string"`
Size float64 `json:"s"`
@@ -2255,44 +2256,44 @@ type WsFuturesOrderbookUpdateEvent struct {
// WsFuturesOrder represents futures order
type WsFuturesOrder struct {
Contract string `json:"contract"`
CreateTime gateioTime `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
FillPrice float64 `json:"fill_price"`
FinishAs string `json:"finish_as"`
FinishTime int64 `json:"finish_time"`
FinishTimeMs gateioTime `json:"finish_time_ms"`
Iceberg int64 `json:"iceberg"`
ID int64 `json:"id"`
IsClose bool `json:"is_close"`
IsLiq bool `json:"is_liq"`
IsReduceOnly bool `json:"is_reduce_only"`
Left float64 `json:"left"`
Mkfr float64 `json:"mkfr"`
Price float64 `json:"price"`
Refr int64 `json:"refr"`
Refu int64 `json:"refu"`
Size float64 `json:"size"`
Status string `json:"status"`
Text string `json:"text"`
TimeInForce string `json:"tif"`
Tkfr float64 `json:"tkfr"`
User string `json:"user"`
Contract currency.Pair `json:"contract"`
CreateTime gateioTime `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
FillPrice float64 `json:"fill_price"`
FinishAs string `json:"finish_as"`
FinishTime int64 `json:"finish_time"`
FinishTimeMs gateioTime `json:"finish_time_ms"`
Iceberg int64 `json:"iceberg"`
ID int64 `json:"id"`
IsClose bool `json:"is_close"`
IsLiq bool `json:"is_liq"`
IsReduceOnly bool `json:"is_reduce_only"`
Left float64 `json:"left"`
Mkfr float64 `json:"mkfr"`
Price float64 `json:"price"`
Refr int64 `json:"refr"`
Refu int64 `json:"refu"`
Size float64 `json:"size"`
Status string `json:"status"`
Text string `json:"text"`
TimeInForce string `json:"tif"`
Tkfr float64 `json:"tkfr"`
User string `json:"user"`
}
// WsFuturesUserTrade represents a futures account user trade push data
type WsFuturesUserTrade struct {
ID string `json:"id"`
CreateTime gateioTime `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
Contract string `json:"contract"`
OrderID string `json:"order_id"`
Size float64 `json:"size"`
Price float64 `json:"price,string"`
Role string `json:"role"`
Text string `json:"text"`
Fee float64 `json:"fee"`
PointFee int64 `json:"point_fee"`
ID string `json:"id"`
CreateTime gateioTime `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
Contract currency.Pair `json:"contract"`
OrderID string `json:"order_id"`
Size float64 `json:"size"`
Price float64 `json:"price,string"`
Role string `json:"role"`
Text string `json:"text"`
Fee float64 `json:"fee"`
PointFee int64 `json:"point_fee"`
}
// WsFuturesLiquidationNotification represents a liquidation notification push data
@@ -2431,11 +2432,11 @@ type WsOptionUnderlyingTicker struct {
// WsOptionsTrades represents options trades for websocket push data.
type WsOptionsTrades struct {
ID int64 `json:"id"`
CreateTime gateioTime `json:"create_time"`
Contract string `json:"contract"`
Size float64 `json:"size"`
Price float64 `json:"price"`
ID int64 `json:"id"`
CreateTime gateioTime `json:"create_time"`
Contract currency.Pair `json:"contract"`
Size float64 `json:"size"`
Price float64 `json:"price"`
// Added in options websocket push data
CreateTimeMs gateioTime `json:"create_time_ms"`
@@ -2529,9 +2530,9 @@ type WsOptionsOrderbookTicker struct {
// WsOptionsOrderbookSnapshot represents the options orderbook snapshot push data.
type WsOptionsOrderbookSnapshot struct {
Timestamp gateioTime `json:"t"`
Contract string `json:"contract"`
ID int64 `json:"id"`
Timestamp gateioTime `json:"t"`
Contract currency.Pair `json:"contract"`
ID int64 `json:"id"`
Asks []struct {
Price float64 `json:"p,string"`
Size float64 `json:"s"`
@@ -2544,42 +2545,42 @@ type WsOptionsOrderbookSnapshot struct {
// WsOptionsOrder represents options order push data.
type WsOptionsOrder struct {
ID int64 `json:"id"`
Contract string `json:"contract"`
CreateTime int64 `json:"create_time"`
FillPrice float64 `json:"fill_price"`
FinishAs string `json:"finish_as"`
Iceberg float64 `json:"iceberg"`
IsClose bool `json:"is_close"`
IsLiq bool `json:"is_liq"`
IsReduceOnly bool `json:"is_reduce_only"`
Left float64 `json:"left"`
Mkfr float64 `json:"mkfr"`
Price float64 `json:"price"`
Refr float64 `json:"refr"`
Refu float64 `json:"refu"`
Size float64 `json:"size"`
Status string `json:"status"`
Text string `json:"text"`
Tif string `json:"tif"`
Tkfr float64 `json:"tkfr"`
Underlying string `json:"underlying"`
User string `json:"user"`
CreationTime gateioTime `json:"time"`
CreationTimeMs gateioTime `json:"time_ms"`
ID int64 `json:"id"`
Contract currency.Pair `json:"contract"`
CreateTime int64 `json:"create_time"`
FillPrice float64 `json:"fill_price"`
FinishAs string `json:"finish_as"`
Iceberg float64 `json:"iceberg"`
IsClose bool `json:"is_close"`
IsLiq bool `json:"is_liq"`
IsReduceOnly bool `json:"is_reduce_only"`
Left float64 `json:"left"`
Mkfr float64 `json:"mkfr"`
Price float64 `json:"price"`
Refr float64 `json:"refr"`
Refu float64 `json:"refu"`
Size float64 `json:"size"`
Status string `json:"status"`
Text string `json:"text"`
Tif string `json:"tif"`
Tkfr float64 `json:"tkfr"`
Underlying string `json:"underlying"`
User string `json:"user"`
CreationTime gateioTime `json:"time"`
CreationTimeMs gateioTime `json:"time_ms"`
}
// WsOptionsUserTrade represents user's personal trades of option account.
type WsOptionsUserTrade struct {
ID string `json:"id"`
Underlying string `json:"underlying"`
OrderID string `json:"order"`
Contract string `json:"contract"`
CreateTime gateioTime `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
Price float64 `json:"price,string"`
Role string `json:"role"`
Size float64 `json:"size"`
ID string `json:"id"`
Underlying string `json:"underlying"`
OrderID string `json:"order"`
Contract currency.Pair `json:"contract"`
CreateTime gateioTime `json:"create_time"`
CreateTimeMs gateioTime `json:"create_time_ms"`
Price float64 `json:"price,string"`
Role string `json:"role"`
Size float64 `json:"size"`
}
// WsOptionsLiquidates represents the liquidates push data of option account.

View File

@@ -52,7 +52,7 @@ var defaultSubscriptions = []string{
spotTickerChannel,
spotCandlesticksChannel,
spotTradesChannel,
spotOrderbookChannel,
spotOrderbookTickerChannel,
}
var fetchedCurrencyPairSnapshotOrderbook = make(map[string]bool)
@@ -66,14 +66,11 @@ func (g *Gateio) WsConnect() error {
if err != nil {
return err
}
var dialer websocket.Dialer
err = g.Websocket.Conn.Dial(&dialer, http.Header{})
err = g.Websocket.Conn.Dial(&websocket.Dialer{}, http.Header{})
if err != nil {
return err
}
pingMessage, err := json.Marshal(WsInput{
Channel: spotPingChannel,
})
pingMessage, err := json.Marshal(WsInput{Channel: spotPingChannel})
if err != nil {
return err
}
@@ -113,34 +110,32 @@ func (g *Gateio) wsReadConnData() {
}
func (g *Gateio) wsHandleData(respRaw []byte) error {
var result WsResponse
var eventResponse WsEventResponse
err := json.Unmarshal(respRaw, &eventResponse)
if err == nil &&
(eventResponse.Result != nil || eventResponse.Error != nil) &&
(eventResponse.Event == "subscribe" || eventResponse.Event == "unsubscribe") {
if !g.Websocket.Match.IncomingWithData(eventResponse.ID, respRaw) {
return fmt.Errorf("couldn't match subscription message with ID: %d", eventResponse.ID)
}
return nil
}
err = json.Unmarshal(respRaw, &result)
var push WsResponse
err := json.Unmarshal(respRaw, &push)
if err != nil {
return err
}
switch result.Channel {
if push.Event == "subscribe" || push.Event == "unsubscribe" {
if !g.Websocket.Match.IncomingWithData(push.ID, respRaw) {
return fmt.Errorf("couldn't match subscription message with ID: %d", push.ID)
}
return nil
}
switch push.Channel { // TODO: Convert function params below to only use push.Result
case spotTickerChannel:
return g.processTicker(respRaw)
return g.processTicker(push.Result, push.Time)
case spotTradesChannel:
return g.processTrades(respRaw)
return g.processTrades(push.Result)
case spotCandlesticksChannel:
return g.processCandlestick(respRaw)
return g.processCandlestick(push.Result)
case spotOrderbookTickerChannel:
return g.processOrderbookTicker(respRaw)
return g.processOrderbookTicker(push.Result)
case spotOrderbookUpdateChannel:
return g.processOrderbookUpdate(respRaw)
return g.processOrderbookUpdate(push.Result)
case spotOrderbookChannel:
return g.processOrderbookSnapshot(respRaw)
return g.processOrderbookSnapshot(push.Result)
case spotOrdersChannel:
return g.processSpotOrders(respRaw)
case spotUserTradesChannel:
@@ -165,32 +160,26 @@ func (g *Gateio) wsHandleData(respRaw []byte) error {
return nil
}
func (g *Gateio) processTicker(data []byte) error {
var response WsResponse
tickerData := &WsTicker{}
response.Result = tickerData
err := json.Unmarshal(data, &response)
if err != nil {
return err
}
currencyPair, err := currency.NewPairFromString(tickerData.CurrencyPair)
func (g *Gateio) processTicker(incoming []byte, pushTime int64) error {
var data WsTicker
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
tickerPrice := ticker.Price{
ExchangeName: g.Name,
Volume: tickerData.BaseVolume,
QuoteVolume: tickerData.QuoteVolume,
High: tickerData.High24H,
Low: tickerData.Low24H,
Last: tickerData.Last,
Bid: tickerData.HighestBid,
Ask: tickerData.LowestAsk,
Volume: data.BaseVolume,
QuoteVolume: data.QuoteVolume,
High: data.High24H,
Low: data.Low24H,
Last: data.Last,
Bid: data.HighestBid,
Ask: data.LowestAsk,
AssetType: asset.Spot,
Pair: currencyPair,
LastUpdated: time.Unix(response.Time, 0),
Pair: data.CurrencyPair,
LastUpdated: time.Unix(pushTime, 0),
}
assetPairEnabled := g.listOfAssetsCurrencyPairEnabledFor(currencyPair)
assetPairEnabled := g.listOfAssetsCurrencyPairEnabledFor(data.CurrencyPair)
if assetPairEnabled[asset.Spot] {
g.Websocket.DataHandler <- &tickerPrice
}
@@ -207,33 +196,28 @@ func (g *Gateio) processTicker(data []byte) error {
return nil
}
func (g *Gateio) processTrades(data []byte) error {
var response WsResponse
tradeData := &WsTrade{}
response.Result = tradeData
err := json.Unmarshal(data, &response)
func (g *Gateio) processTrades(incoming []byte) error {
var data WsTrade
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
currencyPair, err := currency.NewPairFromString(tradeData.CurrencyPair)
if err != nil {
return err
}
side, err := order.StringToOrderSide(tradeData.Side)
side, err := order.StringToOrderSide(data.Side)
if err != nil {
return err
}
spotTradeData := trade.Data{
Timestamp: time.UnixMicro(int64(tradeData.CreateTimeMs * 1e3)), // the timestamp data is coming as a floating number.
CurrencyPair: currencyPair,
Timestamp: data.CreateTimeMs.Time(),
CurrencyPair: data.CurrencyPair,
AssetType: asset.Spot,
Exchange: g.Name,
Price: tradeData.Price,
Amount: tradeData.Amount,
Price: data.Price,
Amount: data.Amount,
Side: side,
TID: strconv.FormatInt(tradeData.ID, 10),
TID: strconv.FormatInt(data.ID, 10),
}
assetPairEnabled := g.listOfAssetsCurrencyPairEnabledFor(currencyPair)
assetPairEnabled := g.listOfAssetsCurrencyPairEnabledFor(data.CurrencyPair)
if assetPairEnabled[asset.Spot] {
err = trade.AddTradesToBuffer(g.Name, spotTradeData)
if err != nil {
@@ -259,15 +243,13 @@ func (g *Gateio) processTrades(data []byte) error {
return nil
}
func (g *Gateio) processCandlestick(data []byte) error {
var response WsResponse
candleData := &WsCandlesticks{}
response.Result = candleData
err := json.Unmarshal(data, &response)
func (g *Gateio) processCandlestick(incoming []byte) error {
var data WsCandlesticks
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
icp := strings.Split(candleData.NameOfSubscription, currency.UnderscoreDelimiter)
icp := strings.Split(data.NameOfSubscription, currency.UnderscoreDelimiter)
if len(icp) < 3 {
return errors.New("malformed candlestick websocket push data")
}
@@ -279,13 +261,13 @@ func (g *Gateio) processCandlestick(data []byte) error {
Pair: currencyPair,
AssetType: asset.Spot,
Exchange: g.Name,
StartTime: time.Unix(candleData.Timestamp, 0),
StartTime: time.Unix(data.Timestamp, 0),
Interval: icp[0],
OpenPrice: candleData.OpenPrice,
ClosePrice: candleData.ClosePrice,
HighPrice: candleData.HighestPrice,
LowPrice: candleData.LowestPrice,
Volume: candleData.TotalVolume,
OpenPrice: data.OpenPrice,
ClosePrice: data.ClosePrice,
HighPrice: data.HighestPrice,
LowPrice: data.LowestPrice,
Volume: data.TotalVolume,
}
assetPairEnabled := g.listOfAssetsCurrencyPairEnabledFor(currencyPair)
if assetPairEnabled[asset.Spot] {
@@ -304,34 +286,33 @@ func (g *Gateio) processCandlestick(data []byte) error {
return nil
}
func (g *Gateio) processOrderbookTicker(data []byte) error {
var response WsResponse
tickerData := &WsOrderbookTickerData{}
response.Result = tickerData
err := json.Unmarshal(data, &response)
func (g *Gateio) processOrderbookTicker(incoming []byte) error {
var data WsOrderbookTickerData
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
g.Websocket.DataHandler <- tickerData
return nil
return g.Websocket.Orderbook.LoadSnapshot(&orderbook.Base{
Exchange: g.Name,
Pair: data.CurrencyPair,
Asset: asset.Spot,
LastUpdated: time.UnixMilli(data.UpdateTimeMS),
Bids: []orderbook.Item{{Price: data.BestBidPrice, Amount: data.BestBidAmount}},
Asks: []orderbook.Item{{Price: data.BestAskPrice, Amount: data.BestAskAmount}},
})
}
func (g *Gateio) processOrderbookUpdate(data []byte) error {
var response WsResponse
update := new(WsOrderbookUpdate)
response.Result = update
err := json.Unmarshal(data, &response)
func (g *Gateio) processOrderbookUpdate(incoming []byte) error {
var data WsOrderbookUpdate
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
pair, err := currency.NewPairFromString(update.CurrencyPair)
if err != nil {
return err
}
assetPairEnabled := g.listOfAssetsCurrencyPairEnabledFor(pair)
if !fetchedCurrencyPairSnapshotOrderbook[update.CurrencyPair] {
assetPairEnabled := g.listOfAssetsCurrencyPairEnabledFor(data.CurrencyPair)
if !fetchedCurrencyPairSnapshotOrderbook[data.CurrencyPair.String()] {
var orderbooks *orderbook.Base
orderbooks, err = g.FetchOrderbook(context.Background(), pair, asset.Spot) // currency pair orderbook data for Spot, Margin, and Cross Margin is same
orderbooks, err = g.FetchOrderbook(context.Background(), data.CurrencyPair, asset.Spot) // currency pair orderbook data for Spot, Margin, and Cross Margin is same
if err != nil {
return err
}
@@ -347,43 +328,33 @@ func (g *Gateio) processOrderbookUpdate(data []byte) error {
return err
}
}
fetchedCurrencyPairSnapshotOrderbook[update.CurrencyPair] = true
fetchedCurrencyPairSnapshotOrderbook[data.CurrencyPair.String()] = true
}
updates := orderbook.Update{
UpdateTime: update.UpdateTimeMs.Time(),
Pair: pair,
UpdateTime: data.UpdateTimeMs.Time(),
Pair: data.CurrencyPair,
}
updates.Bids = make([]orderbook.Item, len(update.Bids))
updates.Asks = make([]orderbook.Item, len(update.Asks))
var price float64
var amount float64
for x := range updates.Asks {
price, err = strconv.ParseFloat(update.Asks[x][0], 64)
updates.Asks = make([]orderbook.Item, len(data.Asks))
for x := range data.Asks {
updates.Asks[x].Price, err = strconv.ParseFloat(data.Asks[x][0], 64)
if err != nil {
return err
}
amount, err = strconv.ParseFloat(update.Asks[x][1], 64)
updates.Asks[x].Amount, err = strconv.ParseFloat(data.Asks[x][1], 64)
if err != nil {
return err
}
updates.Asks[x] = orderbook.Item{
Amount: amount,
Price: price,
}
}
for x := range updates.Bids {
price, err = strconv.ParseFloat(update.Bids[x][0], 64)
updates.Bids = make([]orderbook.Item, len(data.Bids))
for x := range data.Bids {
updates.Bids[x].Price, err = strconv.ParseFloat(data.Bids[x][0], 64)
if err != nil {
return err
}
amount, err = strconv.ParseFloat(update.Bids[x][1], 64)
updates.Bids[x].Amount, err = strconv.ParseFloat(data.Bids[x][1], 64)
if err != nil {
return err
}
updates.Bids[x] = orderbook.Item{
Amount: amount,
Price: price,
}
}
if len(updates.Asks) == 0 && len(updates.Bids) == 0 {
return nil
@@ -414,58 +385,42 @@ func (g *Gateio) processOrderbookUpdate(data []byte) error {
return nil
}
func (g *Gateio) processOrderbookSnapshot(data []byte) error {
var response WsResponse
snapshot := &WsOrderbookSnapshot{}
response.Result = snapshot
err := json.Unmarshal(data, &response)
func (g *Gateio) processOrderbookSnapshot(incoming []byte) error {
var data WsOrderbookSnapshot
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
pair, err := currency.NewPairFromString(snapshot.CurrencyPair)
if err != nil {
return err
}
assetPairEnabled := g.listOfAssetsCurrencyPairEnabledFor(pair)
assetPairEnabled := g.listOfAssetsCurrencyPairEnabledFor(data.CurrencyPair)
bases := orderbook.Base{
Exchange: g.Name,
Pair: pair,
Pair: data.CurrencyPair,
Asset: asset.Spot,
LastUpdated: snapshot.UpdateTimeMs.Time(),
LastUpdateID: snapshot.LastUpdateID,
LastUpdated: data.UpdateTimeMs.Time(),
LastUpdateID: data.LastUpdateID,
VerifyOrderbook: g.CanVerifyOrderbook,
}
bases.Bids = make([]orderbook.Item, len(snapshot.Bids))
bases.Asks = make([]orderbook.Item, len(snapshot.Asks))
var price float64
var amount float64
for x := range bases.Asks {
price, err = strconv.ParseFloat(snapshot.Asks[x][0], 64)
bases.Asks = make([]orderbook.Item, len(data.Asks))
for x := range data.Asks {
bases.Asks[x].Price, err = strconv.ParseFloat(data.Asks[x][0], 64)
if err != nil {
return err
}
amount, err = strconv.ParseFloat(snapshot.Asks[x][1], 64)
bases.Asks[x].Amount, err = strconv.ParseFloat(data.Asks[x][1], 64)
if err != nil {
return err
}
bases.Asks[x] = orderbook.Item{
Amount: amount,
Price: price,
}
}
for x := range bases.Bids {
price, err = strconv.ParseFloat(snapshot.Bids[x][0], 64)
bases.Bids = make([]orderbook.Item, len(data.Bids))
for x := range data.Bids {
bases.Bids[x].Price, err = strconv.ParseFloat(data.Bids[x][0], 64)
if err != nil {
return err
}
amount, err = strconv.ParseFloat(snapshot.Bids[x][1], 64)
bases.Bids[x].Amount, err = strconv.ParseFloat(data.Bids[x][1], 64)
if err != nil {
return err
}
bases.Bids[x] = orderbook.Item{
Amount: amount,
Price: price,
}
}
if assetPairEnabled[asset.Spot] {
err = g.Websocket.Orderbook.LoadSnapshot(&bases)
@@ -505,10 +460,6 @@ func (g *Gateio) processSpotOrders(data []byte) error {
}
details := make([]order.Detail, len(resp.Result))
for x := range resp.Result {
pair, err := currency.NewPairFromString(resp.Result[x].CurrencyPair)
if err != nil {
return err
}
side, err := order.StringToOrderSide(resp.Result[x].Side)
if err != nil {
return err
@@ -527,7 +478,7 @@ func (g *Gateio) processSpotOrders(data []byte) error {
OrderID: resp.Result[x].ID,
Side: side,
Type: orderType,
Pair: pair,
Pair: resp.Result[x].CurrencyPair,
Cost: resp.Result[x].Fee,
AssetType: a,
Price: resp.Result[x].Price,
@@ -553,18 +504,14 @@ func (g *Gateio) processUserPersonalTrades(data []byte) error {
}
fills := make([]fill.Data, len(resp.Result))
for x := range fills {
currencyPair, err := currency.NewPairFromString(resp.Result[x].CurrencyPair)
if err != nil {
return err
}
side, err := order.StringToOrderSide(resp.Result[x].Side)
if err != nil {
return err
}
fills[x] = fill.Data{
Timestamp: resp.Result[x].CreateTimeMicroS,
Timestamp: resp.Result[x].CreateTimeMs.Time(),
Exchange: g.Name,
CurrencyPair: currencyPair,
CurrencyPair: resp.Result[x].CurrencyPair,
Side: side,
OrderID: resp.Result[x].OrderID,
TradeID: strconv.FormatInt(resp.Result[x].ID, 10),
@@ -691,29 +638,28 @@ func (g *Gateio) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, e
spotBalancesChannel}...)
}
var subscriptions []stream.ChannelSubscription
var pairs []currency.Pair
var crossMarginPairs, spotPairs currency.Pairs
marginPairs, err := g.GetEnabledPairs(asset.Margin)
if err != nil {
return nil, err
}
crossMarginPairs, err = g.GetEnabledPairs(asset.CrossMargin)
if err != nil {
return nil, err
}
spotPairs, err = g.GetEnabledPairs(asset.Spot)
if err != nil {
return nil, err
}
var err error
for i := range channelsToSubscribe {
var pairs []currency.Pair
var assetType asset.Item
switch channelsToSubscribe[i] {
case marginBalancesChannel:
pairs = marginPairs
assetType = asset.Margin
pairs, err = g.GetEnabledPairs(asset.Margin)
case crossMarginBalanceChannel:
pairs = crossMarginPairs
assetType = asset.CrossMargin
pairs, err = g.GetEnabledPairs(asset.CrossMargin)
default:
pairs = spotPairs
assetType = asset.Spot
pairs, err = g.GetEnabledPairs(asset.Spot)
}
if err != nil {
if errors.Is(err, asset.ErrNotEnabled) {
continue // Skip if asset is not enabled.
}
return nil, err
}
for j := range pairs {
params := make(map[string]interface{})
switch channelsToSubscribe[i] {
@@ -723,7 +669,7 @@ func (g *Gateio) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, e
case spotCandlesticksChannel:
params["interval"] = kline.FiveMin
case spotOrderbookUpdateChannel:
params["interval"] = kline.ThousandMilliseconds
params["interval"] = kline.HundredMilliseconds
}
if spotTradesChannel == channelsToSubscribe[i] {
if !g.IsSaveTradeDataEnabled() {
@@ -738,6 +684,7 @@ func (g *Gateio) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, e
subscriptions = append(subscriptions, stream.ChannelSubscription{
Channel: channelsToSubscribe[i],
Currency: fpair.Upper(),
Asset: assetType,
Params: params,
})
}
@@ -788,8 +735,9 @@ func (g *Gateio) generatePayload(event string, channelsToSubscribe []stream.Chan
return nil, err
}
}
var batch *[]string
var intervalString string
payloads := make([]WsInput, len(channelsToSubscribe))
payloads := make([]WsInput, 0, len(channelsToSubscribe))
for i := range channelsToSubscribe {
var auth *WsAuthInput
timestamp := time.Now()
@@ -863,7 +811,8 @@ func (g *Gateio) generatePayload(event string, channelsToSubscribe []stream.Chan
}
params = append(params, intervalString)
}
payloads[i] = WsInput{
payload := WsInput{
ID: g.Websocket.Conn.GenerateMessageID(false),
Event: event,
Channel: channelsToSubscribe[i].Channel,
@@ -871,6 +820,21 @@ func (g *Gateio) generatePayload(event string, channelsToSubscribe []stream.Chan
Auth: auth,
Time: timestamp.Unix(),
}
if channelsToSubscribe[i].Channel == "spot.book_ticker" {
// To get all orderbook assets subscribed it needs to be batched and
// only spot.book_ticker can be batched, if not it will take about
// half an hour for initital sync.
if batch != nil {
*batch = append(*batch, params...)
} else {
// Sets up pointer to the field for the outbound payload.
payloads = append(payloads, payload)
batch = &payloads[len(payloads)-1].Payload
}
continue
}
payloads = append(payloads, payload)
}
return payloads, nil
}

View File

@@ -331,20 +331,16 @@ func (g *Gateio) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item
return nil, err
}
for x := range tickers {
if tickers[x].Name != fPair.String() {
if !tickers[x].Name.Equal(fPair) {
continue
}
var cp currency.Pair
cp, err = currency.NewPairFromString(strings.ReplaceAll(tickers[x].Name, currency.DashDelimiter, currency.UnderscoreDelimiter))
if err != nil {
return nil, err
}
cp.Quote = currency.NewCode(strings.ReplaceAll(cp.Quote.String(), currency.UnderscoreDelimiter, currency.DashDelimiter))
cleanQuote := strings.ReplaceAll(tickers[x].Name.Quote.String(), currency.UnderscoreDelimiter, currency.DashDelimiter)
tickers[x].Name.Quote = currency.NewCode(cleanQuote)
if err != nil {
return nil, err
}
tickerData = &ticker.Price{
Pair: cp,
Pair: tickers[x].Name,
Last: tickers[x].LastPrice.Float64(),
Bid: tickers[x].Bid1Price,
Ask: tickers[x].Ask1Price,
@@ -644,17 +640,13 @@ func (g *Gateio) UpdateTickers(ctx context.Context, a asset.Item) error {
return err
}
for x := range tickers {
currencyPair, err := currency.NewPairFromString(tickers[x].Name)
if err != nil {
return err
}
err = ticker.ProcessTicker(&ticker.Price{
Last: tickers[x].LastPrice.Float64(),
Ask: tickers[x].Ask1Price,
AskSize: tickers[x].Ask1Size,
Bid: tickers[x].Bid1Price,
BidSize: tickers[x].Bid1Size,
Pair: currencyPair,
Pair: tickers[x].Name,
ExchangeName: g.Name,
AssetType: a,
})
@@ -724,20 +716,20 @@ func (g *Gateio) UpdateOrderbook(ctx context.Context, p currency.Pair, a asset.I
VerifyOrderbook: g.CanVerifyOrderbook,
Pair: p.Upper(),
LastUpdateID: orderbookNew.ID,
LastUpdated: orderbookNew.Update,
LastUpdated: orderbookNew.Update.Time(),
}
book.Bids = make(orderbook.Items, len(orderbookNew.Bids))
for x := range orderbookNew.Bids {
book.Bids[x] = orderbook.Item{
Amount: orderbookNew.Bids[x].Amount,
Price: orderbookNew.Bids[x].Price,
Price: orderbookNew.Bids[x].Price.Float64(),
}
}
book.Asks = make(orderbook.Items, len(orderbookNew.Asks))
for x := range orderbookNew.Asks {
book.Asks[x] = orderbook.Item{
Amount: orderbookNew.Asks[x].Amount,
Price: orderbookNew.Asks[x].Price,
Price: orderbookNew.Asks[x].Price.Float64(),
}
}
err = book.Process()

View File

@@ -216,33 +216,30 @@ func (g *Gateio) wsFunnelFuturesConnectionData(ws stream.Connection) {
}
func (g *Gateio) wsHandleFuturesData(respRaw []byte, assetType asset.Item) error {
var result WsResponse
var eventResponse WsEventResponse
err := json.Unmarshal(respRaw, &eventResponse)
if err == nil &&
(eventResponse.Result != nil || eventResponse.Error != nil) &&
(eventResponse.Event == "subscribe" || eventResponse.Event == "unsubscribe") {
if !g.Websocket.Match.IncomingWithData(eventResponse.ID, respRaw) {
return fmt.Errorf("couldn't match subscription message with ID: %d", eventResponse.ID)
}
return nil
}
err = json.Unmarshal(respRaw, &result)
var push WsResponse
err := json.Unmarshal(respRaw, &push)
if err != nil {
return err
}
switch result.Channel {
// Futures push datas.
if push.Event == "subscribe" || push.Event == "unsubscribe" {
if !g.Websocket.Match.IncomingWithData(push.ID, respRaw) {
return fmt.Errorf("couldn't match subscription message with ID: %d", push.ID)
}
return nil
}
switch push.Channel {
case futuresTickersChannel:
return g.processFuturesTickers(respRaw, assetType)
case futuresTradesChannel:
return g.processFuturesTrades(respRaw, assetType)
case futuresOrderbookChannel:
return g.processFuturesOrderbookSnapshot(result.Event, respRaw, assetType)
return g.processFuturesOrderbookSnapshot(push.Event, push.Result, assetType, push.Time)
case futuresOrderbookTickerChannel:
return g.processFuturesOrderbookTicker(respRaw)
return g.processFuturesOrderbookTicker(push.Result)
case futuresOrderbookUpdateChannel:
return g.processFuturesAndOptionsOrderbookUpdate(respRaw, assetType)
return g.processFuturesAndOptionsOrderbookUpdate(push.Result, assetType)
case futuresCandlesticksChannel:
return g.processFuturesCandlesticks(respRaw, assetType)
case futuresOrdersChannel:
@@ -426,10 +423,6 @@ func (g *Gateio) processFuturesTickers(data []byte, assetType asset.Item) error
}
tickerPriceDatas := make([]ticker.Price, len(resp.Result))
for x := range resp.Result {
currencyPair, err := currency.NewPairFromString(resp.Result[x].Contract)
if err != nil {
return err
}
tickerPriceDatas[x] = ticker.Price{
ExchangeName: g.Name,
Volume: resp.Result[x].Volume24HBase,
@@ -438,7 +431,7 @@ func (g *Gateio) processFuturesTickers(data []byte, assetType asset.Item) error
Low: resp.Result[x].Low24H,
Last: resp.Result[x].Last,
AssetType: assetType,
Pair: currencyPair,
Pair: resp.Result[x].Contract,
LastUpdated: time.Unix(resp.Time, 0),
}
}
@@ -459,13 +452,9 @@ func (g *Gateio) processFuturesTrades(data []byte, assetType asset.Item) error {
}
trades := make([]trade.Data, len(resp.Result))
for x := range resp.Result {
currencyPair, err := currency.NewPairFromString(resp.Result[x].Contract)
if err != nil {
return err
}
trades[x] = trade.Data{
Timestamp: resp.Result[x].CreateTimeMs.Time(),
CurrencyPair: currencyPair,
CurrencyPair: resp.Result[x].Contract,
AssetType: assetType,
Exchange: g.Name,
Price: resp.Result[x].Price,
@@ -514,37 +503,29 @@ func (g *Gateio) processFuturesCandlesticks(data []byte, assetType asset.Item) e
return nil
}
func (g *Gateio) processFuturesOrderbookTicker(data []byte) error {
var response WsResponse
orderbookTicker := &WsFuturesOrderbookTicker{}
response.Result = orderbookTicker
err := json.Unmarshal(data, &response)
func (g *Gateio) processFuturesOrderbookTicker(incoming []byte) error {
var data WsFuturesOrderbookTicker
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
g.Websocket.DataHandler <- response
g.Websocket.DataHandler <- data
return nil
}
func (g *Gateio) processFuturesAndOptionsOrderbookUpdate(data []byte, assetType asset.Item) error {
var response WsResponse
update := &WsFuturesAndOptionsOrderbookUpdate{}
response.Result = update
err := json.Unmarshal(data, &response)
func (g *Gateio) processFuturesAndOptionsOrderbookUpdate(incoming []byte, assetType asset.Item) error {
var data WsFuturesAndOptionsOrderbookUpdate
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
pair, err := currency.NewPairFromString(update.ContractName)
if err != nil {
return err
}
if (assetType == asset.Options && !fetchedOptionsCurrencyPairSnapshotOrderbook[update.ContractName]) ||
(assetType != asset.Options && !fetchedFuturesCurrencyPairSnapshotOrderbook[update.ContractName]) {
orderbooks, err := g.FetchOrderbook(context.Background(), pair, assetType)
if (assetType == asset.Options && !fetchedOptionsCurrencyPairSnapshotOrderbook[data.ContractName.String()]) ||
(assetType != asset.Options && !fetchedFuturesCurrencyPairSnapshotOrderbook[data.ContractName.String()]) {
orderbooks, err := g.FetchOrderbook(context.Background(), data.ContractName, assetType)
if err != nil {
return err
}
if orderbooks.LastUpdateID < update.FirstUpdatedID || orderbooks.LastUpdateID > update.LastUpdatedID {
if orderbooks.LastUpdateID < data.FirstUpdatedID || orderbooks.LastUpdateID > data.LastUpdatedID {
return nil
}
err = g.Websocket.Orderbook.LoadSnapshot(orderbooks)
@@ -552,29 +533,25 @@ func (g *Gateio) processFuturesAndOptionsOrderbookUpdate(data []byte, assetType
return err
}
if assetType == asset.Options {
fetchedOptionsCurrencyPairSnapshotOrderbook[update.ContractName] = true
fetchedOptionsCurrencyPairSnapshotOrderbook[data.ContractName.String()] = true
} else {
fetchedFuturesCurrencyPairSnapshotOrderbook[update.ContractName] = true
fetchedFuturesCurrencyPairSnapshotOrderbook[data.ContractName.String()] = true
}
}
updates := orderbook.Update{
UpdateTime: time.UnixMilli(update.TimestampInMs),
Pair: pair,
UpdateTime: time.UnixMilli(data.TimestampInMs),
Pair: data.ContractName,
Asset: assetType,
}
updates.Bids = make([]orderbook.Item, len(update.Bids))
updates.Asks = make([]orderbook.Item, len(update.Asks))
for x := range updates.Asks {
updates.Asks[x] = orderbook.Item{
Amount: update.Asks[x].Size,
Price: update.Asks[x].Price,
}
updates.Asks = make([]orderbook.Item, len(data.Asks))
for x := range data.Asks {
updates.Asks[x].Amount = data.Asks[x].Size
updates.Asks[x].Price = data.Asks[x].Price
}
for x := range updates.Bids {
updates.Bids[x] = orderbook.Item{
Amount: update.Bids[x].Size,
Price: update.Bids[x].Price,
}
updates.Bids = make([]orderbook.Item, len(data.Bids))
for x := range data.Bids {
updates.Bids[x].Amount = data.Bids[x].Size
updates.Bids[x].Price = data.Bids[x].Price
}
if len(updates.Asks) == 0 && len(updates.Bids) == 0 {
return errors.New("malformed orderbook data")
@@ -582,71 +559,56 @@ func (g *Gateio) processFuturesAndOptionsOrderbookUpdate(data []byte, assetType
return g.Websocket.Orderbook.Update(&updates)
}
func (g *Gateio) processFuturesOrderbookSnapshot(event string, data []byte, assetType asset.Item) error {
func (g *Gateio) processFuturesOrderbookSnapshot(event string, incoming []byte, assetType asset.Item, pushTime int64) error {
if event == "all" {
var response WsResponse
snapshot := &WsFuturesOrderbookSnapshot{}
response.Result = snapshot
err := json.Unmarshal(data, &response)
if err != nil {
return err
}
pair, err := currency.NewPairFromString(snapshot.Contract)
var data WsFuturesOrderbookSnapshot
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
base := orderbook.Base{
Asset: assetType,
Exchange: g.Name,
Pair: pair,
LastUpdated: snapshot.TimestampInMs.Time(),
Pair: data.Contract,
LastUpdated: data.TimestampInMs.Time(),
VerifyOrderbook: g.CanVerifyOrderbook,
}
base.Bids = make([]orderbook.Item, len(snapshot.Bids))
base.Asks = make([]orderbook.Item, len(snapshot.Asks))
for x := range base.Asks {
base.Asks[x] = orderbook.Item{
Amount: snapshot.Asks[x].Size,
Price: snapshot.Asks[x].Price,
}
base.Asks = make([]orderbook.Item, len(data.Asks))
for x := range data.Asks {
base.Asks[x].Amount = data.Asks[x].Size
base.Asks[x].Price = data.Asks[x].Price
}
for x := range base.Bids {
base.Bids[x] = orderbook.Item{
Amount: snapshot.Bids[x].Size,
Price: snapshot.Bids[x].Price,
}
base.Bids = make([]orderbook.Item, len(data.Bids))
for x := range data.Bids {
base.Bids[x].Amount = data.Bids[x].Size
base.Bids[x].Price = data.Bids[x].Price
}
return g.Websocket.Orderbook.LoadSnapshot(&base)
}
resp := struct {
Time int64 `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesOrderbookUpdateEvent `json:"result"`
}{}
err := json.Unmarshal(data, &resp)
var data []WsFuturesOrderbookUpdateEvent
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
dataMap := map[string][2][]orderbook.Item{}
for x := range resp.Result {
ab, ok := dataMap[resp.Result[x].CurrencyPair]
for x := range data {
ab, ok := dataMap[data[x].CurrencyPair]
if !ok {
ab = [2][]orderbook.Item{}
}
if resp.Result[x].Amount > 0 {
if data[x].Amount > 0 {
ab[1] = append(ab[1], orderbook.Item{
Price: resp.Result[x].Price,
Amount: resp.Result[x].Amount,
Price: data[x].Price,
Amount: data[x].Amount,
})
} else {
ab[0] = append(ab[0], orderbook.Item{
Price: resp.Result[x].Price,
Amount: -resp.Result[x].Amount,
Price: data[x].Price,
Amount: -data[x].Amount,
})
}
if !ok {
dataMap[resp.Result[x].CurrencyPair] = ab
dataMap[data[x].CurrencyPair] = ab
}
}
if len(dataMap) == 0 {
@@ -663,7 +625,7 @@ func (g *Gateio) processFuturesOrderbookSnapshot(event string, data []byte, asse
Asset: assetType,
Exchange: g.Name,
Pair: currencyPair,
LastUpdated: time.Unix(resp.Time, 0),
LastUpdated: time.Unix(pushTime, 0),
VerifyOrderbook: g.CanVerifyOrderbook,
})
if err != nil {
@@ -686,10 +648,6 @@ func (g *Gateio) processFuturesOrdersPushData(data []byte, assetType asset.Item)
}
orderDetails := make([]order.Detail, len(resp.Result))
for x := range resp.Result {
currencyPair, err := currency.NewPairFromString(resp.Result[x].Contract)
if err != nil {
return err
}
status, err := order.StringToOrderStatus(func() string {
if resp.Result[x].Status == "finished" {
return "cancelled"
@@ -704,7 +662,7 @@ func (g *Gateio) processFuturesOrdersPushData(data []byte, assetType asset.Item)
Exchange: g.Name,
OrderID: strconv.FormatInt(resp.Result[x].ID, 10),
Status: status,
Pair: currencyPair,
Pair: resp.Result[x].Contract,
LastUpdated: resp.Result[x].FinishTimeMs.Time(),
Date: resp.Result[x].CreateTimeMs.Time(),
ExecutedAmount: resp.Result[x].Size - resp.Result[x].Left,
@@ -731,14 +689,10 @@ func (g *Gateio) procesFuturesUserTrades(data []byte, assetType asset.Item) erro
}
fills := make([]fill.Data, len(resp.Result))
for x := range resp.Result {
currencyPair, err := currency.NewPairFromString(resp.Result[x].Contract)
if err != nil {
return err
}
fills[x] = fill.Data{
Timestamp: resp.Result[x].CreateTimeMs.Time(),
Exchange: g.Name,
CurrencyPair: currencyPair,
CurrencyPair: resp.Result[x].Contract,
OrderID: resp.Result[x].OrderID,
TradeID: resp.Result[x].ID,
Price: resp.Result[x].Price,

View File

@@ -339,46 +339,44 @@ func (g *Gateio) handleOptionsSubscription(event string, channelsToSubscribe []s
}
func (g *Gateio) wsHandleOptionsData(respRaw []byte) error {
var result WsResponse
var eventResponse WsEventResponse
err := json.Unmarshal(respRaw, &eventResponse)
if err == nil &&
(eventResponse.Result != nil || eventResponse.Error != nil) &&
(eventResponse.Event == "subscribe" || eventResponse.Event == "unsubscribe") {
if !g.Websocket.Match.IncomingWithData(eventResponse.ID, respRaw) {
return fmt.Errorf("couldn't match subscription message with ID: %d", eventResponse.ID)
}
return nil
}
err = json.Unmarshal(respRaw, &result)
var push WsResponse
err := json.Unmarshal(respRaw, &push)
if err != nil {
return err
}
switch result.Channel {
if push.Event == "subscribe" || push.Event == "unsubscribe" {
if !g.Websocket.Match.IncomingWithData(push.ID, respRaw) {
return fmt.Errorf("couldn't match subscription message with ID: %d", push.ID)
}
return nil
}
switch push.Channel {
case optionsContractTickersChannel:
return g.processOptionsContractTickers(respRaw)
return g.processOptionsContractTickers(push.Result)
case optionsUnderlyingTickersChannel:
return g.processOptionsUnderlyingTicker(respRaw)
return g.processOptionsUnderlyingTicker(push.Result)
case optionsTradesChannel,
optionsUnderlyingTradesChannel:
return g.processOptionsTradesPushData(respRaw)
case optionsUnderlyingPriceChannel:
return g.processOptionsUnderlyingPricePushData(respRaw)
return g.processOptionsUnderlyingPricePushData(push.Result)
case optionsMarkPriceChannel:
return g.processOptionsMarkPrice(respRaw)
return g.processOptionsMarkPrice(push.Result)
case optionsSettlementChannel:
return g.processOptionsSettlementPushData(respRaw)
return g.processOptionsSettlementPushData(push.Result)
case optionsContractsChannel:
return g.processOptionsContractPushData(respRaw)
return g.processOptionsContractPushData(push.Result)
case optionsContractCandlesticksChannel,
optionsUnderlyingCandlesticksChannel:
return g.processOptionsCandlestickPushData(respRaw)
case optionsOrderbookChannel:
return g.processOptionsOrderbookSnapshotPushData(result.Event, respRaw)
return g.processOptionsOrderbookSnapshotPushData(push.Event, push.Result, push.Time)
case optionsOrderbookTickerChannel:
return g.processOrderbookTickerPushData(respRaw)
case optionsOrderbookUpdateChannel:
return g.processFuturesAndOptionsOrderbookUpdate(respRaw, asset.Options)
return g.processFuturesAndOptionsOrderbookUpdate(push.Result, asset.Options)
case optionsOrdersChannel:
return g.processOptionsOrderPushData(respRaw)
case optionsUserTradesChannel:
@@ -401,39 +399,32 @@ func (g *Gateio) wsHandleOptionsData(respRaw []byte) error {
}
}
func (g *Gateio) processOptionsContractTickers(data []byte) error {
var response WsResponse
tickerData := OptionsTicker{}
response.Result = &tickerData
err := json.Unmarshal(data, &response)
if err != nil {
return err
}
currencyPair, err := currency.NewPairFromString(tickerData.Name)
func (g *Gateio) processOptionsContractTickers(incoming []byte) error {
var data OptionsTicker
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
g.Websocket.DataHandler <- &ticker.Price{
Pair: currencyPair,
Last: tickerData.LastPrice.Float64(),
Bid: tickerData.Bid1Price,
Ask: tickerData.Ask1Price,
AskSize: tickerData.Ask1Size,
BidSize: tickerData.Bid1Size,
Pair: data.Name,
Last: data.LastPrice.Float64(),
Bid: data.Bid1Price,
Ask: data.Ask1Price,
AskSize: data.Ask1Size,
BidSize: data.Bid1Size,
ExchangeName: g.Name,
AssetType: asset.Options,
}
return nil
}
func (g *Gateio) processOptionsUnderlyingTicker(data []byte) error {
var response WsResponse
response.Result = &WsOptionUnderlyingTicker{}
err := json.Unmarshal(data, &response)
func (g *Gateio) processOptionsUnderlyingTicker(incoming []byte) error {
var data WsOptionUnderlyingTicker
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
g.Websocket.DataHandler <- &response
g.Websocket.DataHandler <- &data
return nil
}
@@ -455,13 +446,9 @@ func (g *Gateio) processOptionsTradesPushData(data []byte) error {
}
trades := make([]trade.Data, len(resp.Result))
for x := range resp.Result {
currencyPair, err := currency.NewPairFromString(resp.Result[x].Contract)
if err != nil {
return err
}
trades[x] = trade.Data{
Timestamp: resp.Result[x].CreateTimeMs.Time(),
CurrencyPair: currencyPair,
CurrencyPair: resp.Result[x].Contract,
AssetType: asset.Options,
Exchange: g.Name,
Price: resp.Result[x].Price,
@@ -472,51 +459,43 @@ func (g *Gateio) processOptionsTradesPushData(data []byte) error {
return g.Websocket.Trade.Update(saveTradeData, trades...)
}
func (g *Gateio) processOptionsUnderlyingPricePushData(data []byte) error {
var response WsResponse
priceD := WsOptionsUnderlyingPrice{}
response.Result = &priceD
err := json.Unmarshal(data, &response)
func (g *Gateio) processOptionsUnderlyingPricePushData(incoming []byte) error {
var data WsOptionsUnderlyingPrice
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
g.Websocket.DataHandler <- &response
g.Websocket.DataHandler <- &data
return nil
}
func (g *Gateio) processOptionsMarkPrice(data []byte) error {
var response WsResponse
markPrice := WsOptionsMarkPrice{}
response.Result = &markPrice
err := json.Unmarshal(data, &response)
func (g *Gateio) processOptionsMarkPrice(incoming []byte) error {
var data WsOptionsMarkPrice
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
g.Websocket.DataHandler <- &response
g.Websocket.DataHandler <- &data
return nil
}
func (g *Gateio) processOptionsSettlementPushData(data []byte) error {
var response WsResponse
settlementData := WsOptionsSettlement{}
response.Result = &settlementData
err := json.Unmarshal(data, &response)
func (g *Gateio) processOptionsSettlementPushData(incoming []byte) error {
var data WsOptionsSettlement
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
g.Websocket.DataHandler <- &response
g.Websocket.DataHandler <- &data
return nil
}
func (g *Gateio) processOptionsContractPushData(data []byte) error {
var response WsResponse
contractData := WsOptionsContract{}
response.Result = &contractData
err := json.Unmarshal(data, &response)
func (g *Gateio) processOptionsContractPushData(incoming []byte) error {
var data WsOptionsContract
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
g.Websocket.DataHandler <- &response
g.Websocket.DataHandler <- &data
return nil
}
@@ -558,83 +537,64 @@ func (g *Gateio) processOptionsCandlestickPushData(data []byte) error {
return nil
}
func (g *Gateio) processOrderbookTickerPushData(data []byte) error {
var response WsResponse
orderbookTicker := WsOptionsOrderbookTicker{}
response.Result = &orderbookTicker
err := json.Unmarshal(data, &orderbookTicker)
func (g *Gateio) processOrderbookTickerPushData(incoming []byte) error {
var data WsOptionsOrderbookTicker
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
g.Websocket.DataHandler <- &response
g.Websocket.DataHandler <- &data
return nil
}
func (g *Gateio) processOptionsOrderbookSnapshotPushData(event string, data []byte) error {
func (g *Gateio) processOptionsOrderbookSnapshotPushData(event string, incoming []byte, pushTime int64) error {
if event == "all" {
var response WsResponse
snapshot := WsOptionsOrderbookSnapshot{}
response.Result = &snapshot
err := json.Unmarshal(data, &response)
if err != nil {
return err
}
pair, err := currency.NewPairFromString(snapshot.Contract)
var data WsOptionsOrderbookSnapshot
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
base := orderbook.Base{
Asset: asset.Options,
Exchange: g.Name,
Pair: pair,
LastUpdated: snapshot.Timestamp.Time(),
Pair: data.Contract,
LastUpdated: data.Timestamp.Time(),
VerifyOrderbook: g.CanVerifyOrderbook,
}
base.Asks = make([]orderbook.Item, len(snapshot.Asks))
base.Bids = make([]orderbook.Item, len(snapshot.Bids))
for x := range base.Asks {
base.Asks[x] = orderbook.Item{
Amount: snapshot.Asks[x].Size,
Price: snapshot.Asks[x].Price,
}
base.Asks = make([]orderbook.Item, len(data.Asks))
for x := range data.Asks {
base.Asks[x].Amount = data.Asks[x].Size
base.Asks[x].Price = data.Asks[x].Price
}
for x := range base.Bids {
base.Bids[x] = orderbook.Item{
Amount: snapshot.Bids[x].Size,
Price: snapshot.Bids[x].Price,
}
base.Bids = make([]orderbook.Item, len(data.Bids))
for x := range data.Bids {
base.Bids[x].Amount = data.Bids[x].Size
base.Bids[x].Price = data.Bids[x].Price
}
return g.Websocket.Orderbook.LoadSnapshot(&base)
}
resp := struct {
Time int64 `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result []WsFuturesOrderbookUpdateEvent `json:"result"`
}{}
err := json.Unmarshal(data, &resp)
var data []WsFuturesOrderbookUpdateEvent
err := json.Unmarshal(incoming, &data)
if err != nil {
return err
}
dataMap := map[string][2][]orderbook.Item{}
for x := range resp.Result {
ab, ok := dataMap[resp.Result[x].CurrencyPair]
for x := range data {
ab, ok := dataMap[data[x].CurrencyPair]
if !ok {
ab = [2][]orderbook.Item{}
}
if resp.Result[x].Amount > 0 {
if data[x].Amount > 0 {
ab[1] = append(ab[1], orderbook.Item{
Price: resp.Result[x].Price,
Amount: resp.Result[x].Amount,
Price: data[x].Price, Amount: data[x].Amount,
})
} else {
ab[0] = append(ab[0], orderbook.Item{
Price: resp.Result[x].Price,
Amount: -resp.Result[x].Amount,
Price: data[x].Price, Amount: -data[x].Amount,
})
}
if !ok {
dataMap[resp.Result[x].CurrencyPair] = ab
dataMap[data[x].CurrencyPair] = ab
}
}
if len(dataMap) == 0 {
@@ -651,7 +611,7 @@ func (g *Gateio) processOptionsOrderbookSnapshotPushData(event string, data []by
Asset: asset.Options,
Exchange: g.Name,
Pair: currencyPair,
LastUpdated: time.Unix(resp.Time, 0),
LastUpdated: time.Unix(pushTime, 0),
VerifyOrderbook: g.CanVerifyOrderbook,
})
if err != nil {
@@ -674,10 +634,6 @@ func (g *Gateio) processOptionsOrderPushData(data []byte) error {
}
orderDetails := make([]order.Detail, len(resp.Result))
for x := range resp.Result {
currencyPair, err := currency.NewPairFromString(resp.Result[x].Contract)
if err != nil {
return err
}
status, err := order.StringToOrderStatus(func() string {
if resp.Result[x].Status == "finished" {
return "cancelled"
@@ -692,7 +648,7 @@ func (g *Gateio) processOptionsOrderPushData(data []byte) error {
Exchange: g.Name,
OrderID: strconv.FormatInt(resp.Result[x].ID, 10),
Status: status,
Pair: currencyPair,
Pair: resp.Result[x].Contract,
Date: resp.Result[x].CreationTimeMs.Time(),
ExecutedAmount: resp.Result[x].Size - resp.Result[x].Left,
Price: resp.Result[x].Price,
@@ -717,14 +673,10 @@ func (g *Gateio) processOptionsUserTradesPushData(data []byte) error {
}
fills := make([]fill.Data, len(resp.Result))
for x := range resp.Result {
currencyPair, err := currency.NewPairFromString(resp.Result[x].Contract)
if err != nil {
return err
}
fills[x] = fill.Data{
Timestamp: resp.Result[x].CreateTimeMs.Time(),
Exchange: g.Name,
CurrencyPair: currencyPair,
CurrencyPair: resp.Result[x].Contract,
OrderID: resp.Result[x].OrderID,
TradeID: resp.Result[x].ID,
Price: resp.Result[x].Price,

View File

@@ -853,7 +853,7 @@ func (ok *Okx) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Mo
return nil, err
}
var err error
if math.Mod(action.Amount, 1) != 0 {
if math.Trunc(action.Amount) != action.Amount {
return nil, errors.New("okx contract amount can not be decimal")
}
format, err := ok.GetPairFormat(action.AssetType, false)