Skip to content

Commit

Permalink
Main websocket programming done
Browse files Browse the repository at this point in the history
  • Loading branch information
cranktakular committed Oct 16, 2024
1 parent 5583f09 commit 7dfbf3c
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 62 deletions.
4 changes: 4 additions & 0 deletions exchanges/bitget/bitget.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ const (
bitgetOrdersAlgoChannel = "orders-algo"
bitgetPositionsChannel = "positions"
bitgetPositionsHistoryChannel = "positions-history"
bitgetAccountCrossedChannel = "account-crossed"
bitgetOrdersCrossedChannel = "orders-crossed"
bitgetAccountIsolatedChannel = "account-isolated"
bitgetOrdersIsolatedChannel = "orders-isolated"

errIntervalNotSupported = "interval not supported"
errAuthenticatedWebsocketDisabled = "%v AuthenticatedWebsocketAPISupport not enabled"
Expand Down
154 changes: 100 additions & 54 deletions exchanges/bitget/bitget_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ type BGBConvHistResp struct {
ToCoin string `json:"toCoin"`
ToAmount float64 `json:"toAmount,string"`
ToCoinPrice float64 `json:"toCoinPrice,string"`
FeeDetail FeeAndCoin `json:"feeDetail"`
FeeDetail []FeeAndCoin `json:"feeDetail"`
Status SuccessBool `json:"status"`
CreationTime UnixTimestamp `json:"cTime"`
}
Expand Down Expand Up @@ -2309,47 +2309,47 @@ type WsOrderBookResponse struct {

// WsFillSpotResponse contains information on a fill response for spot trading
type WsFillSpotResponse struct {
OrderID int64 `json:"orderId,string"`
TradeID int64 `json:"tradeId,string"`
Symbol string `json:"symbol"`
OrderType string `json:"orderType"`
Side string `json:"side"`
PriceAverage float64 `json:"priceAvg,string"`
Size float64 `json:"size,string"`
Amount float64 `json:"amount,string"`
TradeScope string `json:"tradeScope"`
FeeDetail AbridgedFeeDetail `json:"feeDetail"`
CreationTime UnixTimestamp `json:"cTime"`
UpdateTime UnixTimestamp `json:"uTime"`
OrderID int64 `json:"orderId,string"`
TradeID int64 `json:"tradeId,string"`
Symbol string `json:"symbol"`
OrderType string `json:"orderType"`
Side string `json:"side"`
PriceAverage float64 `json:"priceAvg,string"`
Size float64 `json:"size,string"`
Amount float64 `json:"amount,string"`
TradeScope string `json:"tradeScope"`
FeeDetail []AbridgedFeeDetail `json:"feeDetail"`
CreationTime UnixTimestamp `json:"cTime"`
UpdateTime UnixTimestamp `json:"uTime"`
}

// WsOrderSpotResponse contains information on an order response for spot trading
type WsOrderSpotResponse struct {
InstrumentID string `json:"instId"`
OrderID int64 `json:"orderId,string"`
ClientOrderID string `json:"clientOid"`
Price float64 `json:"price,string"`
Size float64 `json:"size,string"`
NewSize float64 `json:"newSize,string"`
Notional float64 `json:"notional,string"`
OrderType string `json:"orderType"`
Force string `json:"force"`
Side string `json:"side"`
FillPrice float64 `json:"fillPrice,string"`
TradeID int64 `json:"tradeId,string"`
BaseVolume float64 `json:"baseVolume,string"`
FillTime UnixTimestamp `json:"fillTime"`
FillFee float64 `json:"fillFee,string"`
FillFeeCoin string `json:"fillFeeCoin"`
TradeScope string `json:"tradeScope"`
AccountBaseVolume float64 `json:"accBaseVolume,string"`
PriceAverage float64 `json:"priceAvg,string"`
Status string `json:"status"`
CreationTime UnixTimestamp `json:"cTime"`
UpdateTime UnixTimestamp `json:"uTime"`
STPMode string `json:"stpMode"`
FeeDetail AbridgedFeeDetail `json:"feeDetail"`
EnterPointSource string `json:"enterPointSource"`
InstrumentID string `json:"instId"`
OrderID int64 `json:"orderId,string"`
ClientOrderID string `json:"clientOid"`
Price float64 `json:"price,string"`
Size float64 `json:"size,string"`
NewSize float64 `json:"newSize,string"`
Notional float64 `json:"notional,string"`
OrderType string `json:"orderType"`
Force string `json:"force"`
Side string `json:"side"`
FillPrice float64 `json:"fillPrice,string"`
TradeID int64 `json:"tradeId,string"`
BaseVolume float64 `json:"baseVolume,string"`
FillTime UnixTimestamp `json:"fillTime"`
FillFee float64 `json:"fillFee,string"`
FillFeeCoin string `json:"fillFeeCoin"`
TradeScope string `json:"tradeScope"`
AccountBaseVolume float64 `json:"accBaseVolume,string"`
PriceAverage float64 `json:"priceAvg,string"`
Status string `json:"status"`
CreationTime UnixTimestamp `json:"cTime"`
UpdateTime UnixTimestamp `json:"uTime"`
STPMode string `json:"stpMode"`
FeeDetail []AbridgedFeeDetail `json:"feeDetail"`
EnterPointSource string `json:"enterPointSource"`
}

// WsTriggerOrderSpotResponse contains information on a trigger order response for spot trading
Expand Down Expand Up @@ -2414,29 +2414,29 @@ type WsPositionResponse struct {

// WsFillFuturesResponse contains information on a fill response for futures trading
type WsFillFuturesResponse struct {
OrderID int64 `json:"orderId,string"`
TradeID int64 `json:"tradeId,string"`
Symbol string `json:"symbol"`
Side string `json:"side"`
OrderType string `json:"orderType"`
PosMode string `json:"posMode"`
Price float64 `json:"price,string"`
BaseVolume float64 `json:"baseVolume,string"`
QuoteVolume float64 `json:"quoteVolume,string"`
Profit float64 `json:"profit,string"`
TradeSide string `json:"tradeSide"`
TradeScope string `json:"tradeScope"`
FeeDetail AbridgedFeeDetail `json:"feeDetail"`
CreationTime UnixTimestamp `json:"cTime"`
UpdateTime UnixTimestamp `json:"uTime"`
OrderID int64 `json:"orderId,string"`
TradeID int64 `json:"tradeId,string"`
Symbol string `json:"symbol"`
Side string `json:"side"`
OrderType string `json:"orderType"`
PosMode string `json:"posMode"`
Price float64 `json:"price,string"`
BaseVolume float64 `json:"baseVolume,string"`
QuoteVolume float64 `json:"quoteVolume,string"`
Profit float64 `json:"profit,string"`
TradeSide string `json:"tradeSide"`
TradeScope string `json:"tradeScope"`
FeeDetail []AbridgedFeeDetail `json:"feeDetail"`
CreationTime UnixTimestamp `json:"cTime"`
UpdateTime UnixTimestamp `json:"uTime"`
}

// WsOrderFuturesResponse contains information on an order response for futures trading
type WsOrderFuturesResponse struct {
FilledQuantity float64 `json:"accBaseVolume,string"`
CreationTime UnixTimestamp `json:"cTime"`
ClientOrderID string `json:"clientOid"`
FeeDetail FeeAndCoin `json:"feeDetail"`
FeeDetail []FeeAndCoin `json:"feeDetail"`
FillFee float64 `json:"fillFee,string"`
FillFeeCoin string `json:"fillFeeCoin"`
FillNotionalUSD float64 `json:"fillNotionalUsd,string"`
Expand Down Expand Up @@ -2523,3 +2523,49 @@ type WsIndexPriceResponse struct {
IndexPrice float64 `json:"indexPrice,string"`
Timestamp UnixTimestamp `json:"ts"`
}

// WsAccountCrossMarginResponse contains information on an account response for cross margin trading
type WsAccountCrossMarginResponse struct {
UpdateTime UnixTimestamp `json:"uTime"`
ID int64 `json:"id,string"`
Coin string `json:"coin"`
Available float64 `json:"available,string"`
Borrow float64 `json:"borrow,string"`
Frozen float64 `json:"frozen,string"`
Interest float64 `json:"interest,string"`
Coupon float64 `json:"coupon,string"`
}

// WsOrderCrossMarginResponse contains information on an order response for margin trading
type WsOrderMarginResponse struct {
Force string `json:"force"`
OrderType string `json:"orderType"`
Price float64 `json:"price,string"`
QuoteSize float64 `json:"quoteSize,string"`
Side string `json:"side"`
FeeDetail []AbridgedFeeDetail `json:"feeDetail"`
EnterPointSource string `json:"enterPointSource"`
Status string `json:"status"`
BaseSize float64 `json:"baseSize,string"`
CreationTime UnixTimestamp `json:"cTime"`
ClientOrderID string `json:"clientOid"`
FillPrice float64 `json:"fillPrice,string"`
BaseVolume float64 `json:"baseVolume,string"`
FillTotalAmount float64 `json:"fillTotalAmount,string"`
LoanType string `json:"loanType"`
OrderID int64 `json:"orderId,string"`
STPMode string `json:"stpMode"`
}

// WsAccountisolatedMarginResponse contains information on an account response for isolated margin trading
type WsAccountIsolatedMarginResponse struct {
UpdateTime UnixTimestamp `json:"uTime"`
ID int64 `json:"id,string"`
Coin string `json:"coin"`
Symbol string `json:"symbol"`
Available float64 `json:"available,string"`
Borrow float64 `json:"borrow,string"`
Frozen float64 `json:"frozen,string"`
Interest float64 `json:"interest,string"`
Coupon float64 `json:"coupon,string"`
}
115 changes: 107 additions & 8 deletions exchanges/bitget/bitget_websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ var subscriptionNames = map[asset.Item]map[string]string{
"positionsHistory": bitgetPositionsHistoryChannel,
},
asset.Margin: {
"indexPrice": bitgetIndexPriceChannel,
"indexPrice": bitgetIndexPriceChannel,
subscription.MyOrdersChannel: bitgetOrdersIsolatedChannel,
"account": bitgetAccountIsolatedChannel,
},
asset.CrossMargin: {
"indexPrice": bitgetIndexPriceChannel,
"indexPrice": bitgetIndexPriceChannel,
subscription.MyOrdersChannel: bitgetOrdersCrossedChannel,
"account": bitgetAccountCrossedChannel,
},
}

Expand All @@ -78,13 +82,17 @@ var defaultSubscriptions = subscription.List{
{Enabled: false, Channel: subscription.MyTradesChannel, Authenticated: true, Asset: asset.Futures},
{Enabled: false, Channel: subscription.MyOrdersChannel, Authenticated: true, Asset: asset.Spot},
{Enabled: false, Channel: subscription.MyOrdersChannel, Authenticated: true, Asset: asset.Futures},
{Enabled: true, Channel: subscription.MyOrdersChannel, Authenticated: true, Asset: asset.Margin},
{Enabled: false, Channel: subscription.MyOrdersChannel, Authenticated: true, Asset: asset.CrossMargin},
{Enabled: false, Channel: "myTriggerOrders", Authenticated: true, Asset: asset.Spot},
{Enabled: false, Channel: "myTriggerOrders", Authenticated: true, Asset: asset.Futures},
{Enabled: false, Channel: "account", Authenticated: true, Asset: asset.Spot},
{Enabled: false, Channel: "account", Authenticated: true, Asset: asset.Futures},
{Enabled: false, Channel: "account", Authenticated: true, Asset: asset.Margin},
{Enabled: false, Channel: "account", Authenticated: true, Asset: asset.CrossMargin},
{Enabled: false, Channel: "positions", Authenticated: true, Asset: asset.Futures},
{Enabled: false, Channel: "positionsHistory", Authenticated: true, Asset: asset.Futures},
{Enabled: true, Channel: "indexPrice", Asset: asset.Margin},
{Enabled: false, Channel: "indexPrice", Asset: asset.Margin},
}

// WsConnect connects to a websocket feed
Expand Down Expand Up @@ -444,13 +452,15 @@ func (bi *Bitget) wsHandleData(respRaw []byte) error {
FillOrKill: fok,
PostOnly: po,
Side: side,
Fee: orders[i].FillFee,
FeeAsset: currency.NewCode(orders[i].FillFeeCoin),
AverageExecutedPrice: orders[i].PriceAverage,
Status: statusDecoder(orders[i].Status),
Date: orders[i].CreationTime.Time(),
LastUpdated: orders[i].UpdateTime.Time(),
}
for x := range orders[i].FeeDetail {
resp[i].Fee += orders[i].FeeDetail[x].TotalFee
resp[i].FeeAsset = currency.NewCode(orders[i].FeeDetail[x].FeeCoin)
}
}
bi.Websocket.DataHandler <- resp
case asset.Futures:
Expand Down Expand Up @@ -492,8 +502,6 @@ func (bi *Bitget) wsHandleData(respRaw []byte) error {
ExecutedAmount: orders[i].FilledQuantity,
Date: orders[i].CreationTime.Time(),
ClientOrderID: orders[i].ClientOrderID,
Fee: orders[i].FillFee,
FeeAsset: currency.NewCode(orders[i].FillFeeCoin),
Leverage: orders[i].Leverage,
MarginType: marginDecoder(orders[i].MarginMode),
OrderID: strconv.FormatInt(orders[i].OrderID, 10),
Expand All @@ -503,6 +511,10 @@ func (bi *Bitget) wsHandleData(respRaw []byte) error {
Status: statusDecoder(orders[i].Status),
LastUpdated: orders[i].UpdateTime.Time(),
}
for x := range orders[i].FeeDetail {
resp[i].Fee += orders[i].FeeDetail[x].Fee
resp[i].FeeAsset = currency.NewCode(orders[i].FeeDetail[x].FeeCoin)
}
}
bi.Websocket.DataHandler <- resp
default:
Expand Down Expand Up @@ -659,6 +671,93 @@ func (bi *Bitget) wsHandleData(respRaw []byte) error {
}
resp = resp[:cur]
bi.Websocket.DataHandler <- resp
case bitgetAccountCrossedChannel:
var acc []WsAccountCrossMarginResponse
err := json.Unmarshal(wsResponse.Data, &acc)
if err != nil {
return err
}
var hold account.Holdings
hold.Exchange = bi.Name
var sub account.SubAccount
hold.Accounts = append(hold.Accounts, sub)
sub.AssetType = asset.CrossMargin
sub.Currencies = make([]account.Balance, len(acc))
for i := range acc {
sub.Currencies[i] = account.Balance{
Currency: currency.NewCode(acc[i].Coin),
Hold: acc[i].Frozen,
Free: acc[i].Available,
Borrowed: acc[i].Borrow,
AvailableWithoutBorrow: acc[i].Available, // Need to check if Bitget actually calculates values this way
Total: acc[i].Available + acc[i].Frozen + acc[i].Borrow + acc[i].Interest + acc[i].Coupon, // Here too
}
}
bi.Websocket.DataHandler <- hold
case bitgetOrdersCrossedChannel, bitgetOrdersIsolatedChannel:
var orders []WsOrderMarginResponse
err := json.Unmarshal(wsResponse.Data, &orders)
if err != nil {
return err
}
resp := make([]order.Detail, len(orders))
pair, err := pairFromStringHelper(wsResponse.Arg.InstrumentID)
if err != nil {
return err
}
for i := range orders {
ioc, fok, po := strategyDecoder(orders[i].Force)
resp[i] = order.Detail{
Exchange: bi.Name,
Pair: pair,
OrderID: strconv.FormatInt(orders[i].OrderID, 10),
ClientOrderID: orders[i].ClientOrderID,
AverageExecutedPrice: orders[i].FillPrice,
Price: orders[i].Price,
Amount: orders[i].BaseSize,
QuoteAmount: orders[i].QuoteSize,
Type: typeDecoder(orders[i].OrderType),
ImmediateOrCancel: ioc,
FillOrKill: fok,
PostOnly: po,
Side: sideDecoder(orders[i].Side),
Status: statusDecoder(orders[i].Status),
Date: orders[i].CreationTime.Time(),
}
for x := range orders[i].FeeDetail {
resp[i].Fee += orders[i].FeeDetail[x].TotalFee
resp[i].FeeAsset = currency.NewCode(orders[i].FeeDetail[x].FeeCoin)
}
if wsResponse.Arg.Channel == bitgetOrdersIsolatedChannel {
resp[i].AssetType = asset.Margin
} else {
resp[i].AssetType = asset.CrossMargin
}
}
bi.Websocket.DataHandler <- resp
case bitgetAccountIsolatedChannel:
var acc []WsAccountIsolatedMarginResponse
err := json.Unmarshal(wsResponse.Data, &acc)
if err != nil {
return err
}
var hold account.Holdings
hold.Exchange = bi.Name
var sub account.SubAccount
hold.Accounts = append(hold.Accounts, sub)
sub.AssetType = asset.Margin
sub.Currencies = make([]account.Balance, len(acc))
for i := range acc {
sub.Currencies[i] = account.Balance{
Currency: currency.NewCode(acc[i].Coin),
Hold: acc[i].Frozen,
Free: acc[i].Available,
Borrowed: acc[i].Borrow,
AvailableWithoutBorrow: acc[i].Available, // Need to check if Bitget actually calculates values this way
Total: acc[i].Available + acc[i].Frozen + acc[i].Borrow + acc[i].Interest + acc[i].Coupon, // Here too
}
}
bi.Websocket.DataHandler <- hold
default:
bi.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: bi.Name + stream.UnhandledMessage + string(respRaw)}
}
Expand Down Expand Up @@ -911,7 +1010,7 @@ func (bi *Bitget) generateDefaultSubscriptions() (subscription.List, error) {
for i := range subs {
subs[i].Channel = subscriptionNames[subs[i].Asset][subs[i].Channel]
switch subs[i].Channel {
case bitgetAccount, bitgetFillChannel, bitgetPositionsChannel, bitgetPositionsHistoryChannel, bitgetIndexPriceChannel:
case bitgetAccount, bitgetFillChannel, bitgetPositionsChannel, bitgetPositionsHistoryChannel, bitgetIndexPriceChannel, bitgetAccountCrossedChannel, bitgetAccountIsolatedChannel: // Not fully sure that bitgetIndexPriceChannel belongs here
default:
subs[i].Pairs = enabledPairs
}
Expand Down

0 comments on commit 7dfbf3c

Please sign in to comment.