mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-24 07:26:47 +00:00
Coinbene: Add swap endpoints and code cleanup/bugfixes (#403)
* Start Coinbene SWAP implementation * Flesh out more API endpoints * Code cleanup * Add more endpoints, bug fixes and order validation checks * More endpoints, tests and bugfixes * Remove omitempty for enabled/available pairs
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
)
|
||||
|
||||
@@ -15,7 +16,8 @@ const (
|
||||
testAPIKey = ""
|
||||
testAPISecret = ""
|
||||
canManipulateRealOrders = false
|
||||
btcusdt = "BTC/USDT"
|
||||
spotTestPair = "BTC/USDT"
|
||||
swapTestPair = "BTCUSDT"
|
||||
)
|
||||
|
||||
var c Coinbene
|
||||
@@ -48,30 +50,6 @@ func areTestAPIKeysSet() bool {
|
||||
return c.AllowAuthenticatedRequest()
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetTicker(btcusdt)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetOrderbook(btcusdt, 100)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTrades(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetTrades(btcusdt)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllPairs(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetAllPairs()
|
||||
@@ -82,18 +60,53 @@ func TestGetAllPairs(t *testing.T) {
|
||||
|
||||
func TestGetPairInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetPairInfo(btcusdt)
|
||||
_, err := c.GetPairInfo(spotTestPair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserBalance(t *testing.T) {
|
||||
func TestGetOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetOrderbook(spotTestPair, 100)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetTicker(spotTestPair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTrades(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetTrades(spotTestPair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAcounntBalances(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys required but not set, skipping test")
|
||||
}
|
||||
_, err := c.GetUserBalance()
|
||||
_, err := c.GetAccountBalances()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountAssetBalance(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys required but not set, skipping test")
|
||||
}
|
||||
_, err := c.GetAccountAssetBalance(currency.BTC.String())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -104,29 +117,38 @@ func TestPlaceOrder(t *testing.T) {
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
_, err := c.PlaceOrder(1, 1, btcusdt, order.Buy.Lower(), "")
|
||||
_, err := c.PlaceSpotOrder(
|
||||
1,
|
||||
1,
|
||||
spotTestPair,
|
||||
order.Buy.Lower(),
|
||||
order.Limit.Lower(),
|
||||
"Sup3rAw3s0m3Cl13ntiDH",
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchOrderInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys required but not set, skipping test")
|
||||
}
|
||||
_, err := c.FetchOrderInfo("adfjashjgsag")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveOrder(t *testing.T) {
|
||||
func TestPlaceOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
_, err := c.RemoveOrder("adfjashjgsag")
|
||||
|
||||
_, err := c.PlaceSpotOrders(
|
||||
[]PlaceOrderRequest{
|
||||
{
|
||||
1,
|
||||
1,
|
||||
spotTestPair,
|
||||
order.Buy.Lower(),
|
||||
order.Limit.Lower(),
|
||||
"Sup3rAw3s0m3Cl13ntiDH",
|
||||
0,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -137,7 +159,7 @@ func TestFetchOpenOrders(t *testing.T) {
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys required but not set, skipping test")
|
||||
}
|
||||
_, err := c.FetchOpenOrders(btcusdt)
|
||||
_, err := c.FetchOpenSpotOrders(spotTestPair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -148,7 +170,52 @@ func TestFetchClosedOrders(t *testing.T) {
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys required but not set, skipping test")
|
||||
}
|
||||
_, err := c.FetchClosedOrders(btcusdt, "")
|
||||
_, err := c.FetchClosedOrders(spotTestPair, "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchOrderInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys required but not set, skipping test")
|
||||
}
|
||||
_, err := c.FetchSpotOrderInfo("adfjashjgsag")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSpotOrderFills(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys required but not set, skipping test")
|
||||
}
|
||||
_, err := c.GetSpotOrderFills("1912131427156307968")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelSpotOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
_, err := c.CancelSpotOrder("adfjashjgsag")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelSpotOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.CancelSpotOrders([]string{"578639816552972288", "578639902896914432"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -157,7 +224,11 @@ func TestFetchClosedOrders(t *testing.T) {
|
||||
func TestUpdateTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
cp := currency.NewPairWithDelimiter("BTC", "USDT", "/")
|
||||
_, err := c.UpdateTicker(cp, "spot")
|
||||
_, err := c.UpdateTicker(cp, asset.Spot)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = c.UpdateTicker(cp, asset.PerpetualSwap)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -177,7 +248,203 @@ func TestGetAccountInfo(t *testing.T) {
|
||||
func TestUpdateOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
cp := currency.NewPairWithDelimiter("BTC", "USDT", "/")
|
||||
_, err := c.UpdateOrderbook(cp, "spot")
|
||||
_, err := c.UpdateOrderbook(cp, asset.Spot)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = c.UpdateOrderbook(cp, asset.PerpetualSwap)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapTickers(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetSwapTickers()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetSwapTicker(swapTestPair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetSwapOrderbook(swapTestPair, 100)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapKlines(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetSwapKlines(swapTestPair,
|
||||
"1573184608",
|
||||
"1573184808",
|
||||
"1")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapTrades(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := c.GetSwapTrades(swapTestPair, 10)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapAccountInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys required but not set, skipping test")
|
||||
}
|
||||
_, err := c.GetSwapAccountInfo()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapPositions(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys required but not set, skipping test")
|
||||
}
|
||||
_, err := c.GetSwapPositions(swapTestPair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlaceSwapOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.PlaceSwapOrder(swapTestPair,
|
||||
order.Buy.Lower(),
|
||||
"limit",
|
||||
"fixed",
|
||||
"12345",
|
||||
1,
|
||||
1,
|
||||
2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelSwapOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.CancelSwapOrder("1337")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOpenSwapOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.GetSwapOpenOrders(swapTestPair, 0, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapOpenOrdersByPage(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.GetSwapOpenOrdersByPage(swapTestPair, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapOrderInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.GetSwapOrderInfo("1337")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapOrderHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.GetSwapOrderHistory("", "", swapTestPair, 1, 10, "", "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapOrderHistoryByOrderID(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.GetSwapOrderHistoryByOrderID("", "", swapTestPair, "", 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelSwapOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.CancelSwapOrders([]string{"578639816552972288", "578639902896914432"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapOrderFills(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.GetSwapOrderFills(swapTestPair, "5807143157122003", 580714315825905664)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapFundingRates(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() || !canManipulateRealOrders {
|
||||
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
|
||||
}
|
||||
|
||||
_, err := c.GetSwapFundingRates(1, 2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package coinbene
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
)
|
||||
|
||||
// TickerData stores ticker data
|
||||
type TickerData struct {
|
||||
Symbol string `json:"symbol"`
|
||||
@@ -11,35 +17,35 @@ type TickerData struct {
|
||||
DailyVolume float64 `json:"volume24h,string"`
|
||||
}
|
||||
|
||||
// TickerResponse stores ticker response data
|
||||
type TickerResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
TickerData `json:"data"`
|
||||
// OrderbookItem stores an individual orderbook item
|
||||
type OrderbookItem struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
Count int64
|
||||
}
|
||||
|
||||
// Orderbook stores orderbook info
|
||||
// Orderbook stores the orderbook data
|
||||
type Orderbook struct {
|
||||
Asks [][]string `json:"asks"`
|
||||
Bids [][]string `json:"bids"`
|
||||
Bids []OrderbookItem
|
||||
Asks []OrderbookItem
|
||||
Symbol string
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
// OrderbookResponse stores data from fetched orderbooks
|
||||
type OrderbookResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Orderbook `json:"data"`
|
||||
// TradeItem stores a single trade
|
||||
type TradeItem struct {
|
||||
CurrencyPair string
|
||||
Price float64
|
||||
Volume float64
|
||||
Direction string
|
||||
TradeTime time.Time
|
||||
}
|
||||
|
||||
// TradeResponse stores trade data
|
||||
type TradeResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Trades [][]string `json:"data"`
|
||||
}
|
||||
// Trades stores trade data
|
||||
type Trades []TradeItem
|
||||
|
||||
// AllPairData stores pair data
|
||||
type AllPairData struct {
|
||||
// PairData stores pair data
|
||||
type PairData struct {
|
||||
Symbol string `json:"symbol"`
|
||||
BaseAsset string `json:"baseAsset"`
|
||||
QuoteAsset string `json:"quoteAsset"`
|
||||
@@ -52,20 +58,6 @@ type AllPairData struct {
|
||||
PriceFluctuation float64 `json:"priceFluctuation,string"`
|
||||
}
|
||||
|
||||
// AllPairResponse stores data for all pairs enabled on exchange
|
||||
type AllPairResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data []AllPairData `json:"data"`
|
||||
}
|
||||
|
||||
// PairResponse stores data for a single queried pair
|
||||
type PairResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data AllPairData `json:"data"`
|
||||
}
|
||||
|
||||
// UserBalanceData stores user balance data
|
||||
type UserBalanceData struct {
|
||||
Asset string `json:"asset"`
|
||||
@@ -74,24 +66,25 @@ type UserBalanceData struct {
|
||||
Total float64 `json:"total,string"`
|
||||
}
|
||||
|
||||
// UserBalanceResponse stores user balance data
|
||||
type UserBalanceResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data []UserBalanceData `json:"data"`
|
||||
// PlaceOrderRequest places an order request
|
||||
type PlaceOrderRequest struct {
|
||||
Price float64
|
||||
Quantity float64
|
||||
Symbol string
|
||||
Direction string
|
||||
OrderType string
|
||||
ClientID string
|
||||
Notional int
|
||||
}
|
||||
|
||||
// PlaceOrderResponse stores data for a placed order
|
||||
type PlaceOrderResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Status string `json:"status"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
OrderID string `json:"orderid"`
|
||||
// CancelOrdersResponse stores data for a cancelled order
|
||||
type CancelOrdersResponse struct {
|
||||
OrderID string `json:"orderId"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// OrderInfoData stores order info
|
||||
type OrderInfoData struct {
|
||||
// OrderInfo stores order info
|
||||
type OrderInfo struct {
|
||||
OrderID string `json:"orderId"`
|
||||
BaseAsset string `json:"baseAsset"`
|
||||
QuoteAsset string `json:"quoteAsset"`
|
||||
@@ -100,7 +93,7 @@ type OrderInfoData struct {
|
||||
Amount float64 `json:"amout,string"`
|
||||
FilledAmount float64 `json:"filledAmount"`
|
||||
TakerRate float64 `json:"takerFeeRate,string"`
|
||||
MakerRate float64 `json:"makerRate,string"`
|
||||
MakerRate float64 `json:"makerFeeRate,string"`
|
||||
AvgPrice float64 `json:"avgPrice,string"`
|
||||
OrderPrice float64 `json:"orderPrice,string"`
|
||||
OrderStatus string `json:"orderStatus"`
|
||||
@@ -108,33 +101,19 @@ type OrderInfoData struct {
|
||||
TotalFee float64 `json:"totalFee"`
|
||||
}
|
||||
|
||||
// OrderInfoResponse stores orderinfo data
|
||||
type OrderInfoResponse struct {
|
||||
Order OrderInfoData `json:"data"`
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
// OrderFills stores the fill info
|
||||
type OrderFills struct {
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
Direction string `json:"direction"`
|
||||
TradeTime time.Time `json:"tradeTime"`
|
||||
FeeByConi float64 `json:"feeByConi,string"`
|
||||
}
|
||||
|
||||
// RemoveOrderResponse stores data for the remove request
|
||||
type RemoveOrderResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
OrderID string `json:"data"`
|
||||
}
|
||||
|
||||
// OpenOrderResponse stores data for open orders
|
||||
type OpenOrderResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
OpenOrders []OrderInfoData `json:"data"`
|
||||
}
|
||||
|
||||
// ClosedOrderResponse stores data for closed orders
|
||||
type ClosedOrderResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data []OrderInfoData `json:"data"`
|
||||
}
|
||||
// OrdersInfo stores a collection of orders
|
||||
type OrdersInfo []OrderInfo
|
||||
|
||||
// WsSub stores subscription data
|
||||
type WsSub struct {
|
||||
@@ -144,17 +123,17 @@ type WsSub struct {
|
||||
|
||||
// WsTickerData stores websocket ticker data
|
||||
type WsTickerData struct {
|
||||
Symbol string `json:"symbol"`
|
||||
LastPrice float64 `json:"lastPrice,string"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
BestAskPrice float64 `json:"bestAskPrice,string"`
|
||||
BestBidPrice float64 `json:"bestBidPrice,string"`
|
||||
BestAskVolume float64 `json:"bestAskVolume,string"`
|
||||
BestBidVolume float64 `json:"bestBidVolume,string"`
|
||||
High24h float64 `json:"high24h,string"`
|
||||
Low24h float64 `json:"low24h,string"`
|
||||
Volume24h float64 `json:"volume,string"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Symbol string `json:"symbol"`
|
||||
LastPrice float64 `json:"lastPrice,string"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
BestAskPrice float64 `json:"bestAskPrice,string"`
|
||||
BestBidPrice float64 `json:"bestBidPrice,string"`
|
||||
BestAskVolume float64 `json:"bestAskVolume,string"`
|
||||
BestBidVolume float64 `json:"bestBidVolume,string"`
|
||||
High24h float64 `json:"high24h,string"`
|
||||
Low24h float64 `json:"low24h,string"`
|
||||
Volume24h float64 `json:"volume24h,string"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// WsTicker stores websocket ticker
|
||||
@@ -169,15 +148,6 @@ type WsTradeList struct {
|
||||
Data [][]string `json:"data"`
|
||||
}
|
||||
|
||||
// WsOrderbook stores websocket orderbook data
|
||||
type WsOrderbook struct {
|
||||
Topic string `json:"topic"`
|
||||
Action string `json:"action"`
|
||||
Data []Orderbook `json:"data"`
|
||||
Version int64 `json:"version,string"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
// WsKline stores websocket kline data
|
||||
type WsKline struct {
|
||||
Topic string `json:"topic"`
|
||||
@@ -186,11 +156,11 @@ type WsKline struct {
|
||||
|
||||
// WsUserData stores websocket user data
|
||||
type WsUserData struct {
|
||||
Asset string `json:"string"`
|
||||
Available float64 `json:"availableBalance"`
|
||||
Locked float64 `json:"frozenBalance"`
|
||||
Total float64 `json:"balance"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Asset string `json:"string"`
|
||||
Available float64 `json:"availableBalance"`
|
||||
Locked float64 `json:"frozenBalance"`
|
||||
Total float64 `json:"balance"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// WsUserInfo stores websocket user info
|
||||
@@ -201,18 +171,18 @@ type WsUserInfo struct {
|
||||
|
||||
// WsPositionData stores websocket info on user's position
|
||||
type WsPositionData struct {
|
||||
AvailableQuantity float64 `json:"availableQuantity"`
|
||||
AveragePrice float64 `json:"avgPrice"`
|
||||
Leverage float64 `json:"leverage"`
|
||||
LiquidationPrice float64 `json:"liquidationPrice"`
|
||||
MarkPrice float64 `json:"markPrice"`
|
||||
PositionMargin float64 `json:"positionMargin"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
RealisedPNL float64 `json:"realisedPnl"`
|
||||
Side string `json:"side"`
|
||||
Symbol string `json:"symbol"`
|
||||
MarginMode int64 `json:"marginMode"`
|
||||
CreateTime string `json:"createTime"`
|
||||
AvailableQuantity float64 `json:"availableQuantity"`
|
||||
AveragePrice float64 `json:"avgPrice"`
|
||||
Leverage float64 `json:"leverage"`
|
||||
LiquidationPrice float64 `json:"liquidationPrice"`
|
||||
MarkPrice float64 `json:"markPrice"`
|
||||
PositionMargin float64 `json:"positionMargin"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
RealisedPNL float64 `json:"realisedPnl"`
|
||||
Side string `json:"side"`
|
||||
Symbol string `json:"symbol"`
|
||||
MarginMode int64 `json:"marginMode"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
}
|
||||
|
||||
// WsPosition stores websocket info on user's positions
|
||||
@@ -223,22 +193,22 @@ type WsPosition struct {
|
||||
|
||||
// WsOrderData stores websocket user order data
|
||||
type WsOrderData struct {
|
||||
OrderID string `json:"orderId"`
|
||||
Direction string `json:"direction"`
|
||||
Leverage float64 `json:"leverage"`
|
||||
Symbol string `json:"symbol"`
|
||||
OrderType string `json:"orderType"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
OrderPrice float64 `json:"orderPrice"`
|
||||
OrderValue float64 `json:"orderValue"`
|
||||
Fee float64 `json:"fee"`
|
||||
FilledQuantity float64 `json:"filledQuantity"`
|
||||
AveragePrice float64 `json:"averagePrice"`
|
||||
OrderTime string `json:"orderTime"`
|
||||
Status string `json:"status"`
|
||||
LastFillQuantity float64 `json:"lastFillQuantity"`
|
||||
LastFillPrice float64 `json:"lastFillPrice"`
|
||||
LastFillTime string `json:"lastFillTime"`
|
||||
OrderID string `json:"orderId"`
|
||||
Direction string `json:"direction"`
|
||||
Leverage float64 `json:"leverage"`
|
||||
Symbol string `json:"symbol"`
|
||||
OrderType string `json:"orderType"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
OrderPrice float64 `json:"orderPrice"`
|
||||
OrderValue float64 `json:"orderValue"`
|
||||
Fee float64 `json:"fee"`
|
||||
FilledQuantity float64 `json:"filledQuantity"`
|
||||
AveragePrice float64 `json:"averagePrice"`
|
||||
OrderTime time.Time `json:"orderTime"`
|
||||
Status string `json:"status"`
|
||||
LastFillQuantity float64 `json:"lastFillQuantity"`
|
||||
LastFillPrice float64 `json:"lastFillPrice"`
|
||||
LastFillTime time.Time `json:"lastFillTime"`
|
||||
}
|
||||
|
||||
// WsUserOrders stores websocket user orders' data
|
||||
@@ -246,3 +216,141 @@ type WsUserOrders struct {
|
||||
Topic string `json:"topic"`
|
||||
Data []WsOrderData `json:"data"`
|
||||
}
|
||||
|
||||
// SwapTicker stores the swap ticker info
|
||||
type SwapTicker struct {
|
||||
LastPrice float64 `json:"lastPrice,string"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
BestAskPrice float64 `json:"bestAskPrice,string"`
|
||||
BestBidPrice float64 `json:"bestBidPrice,string"`
|
||||
High24Hour float64 `json:"high24h,string"`
|
||||
Low24Hour float64 `json:"low24h,string"`
|
||||
Volume24Hour float64 `json:"volume24h,string"`
|
||||
BestAskVolume float64 `json:"bestAskVolume,string"`
|
||||
BestBidVolume float64 `json:"bestBidVolume,string"`
|
||||
Turnover float64 `json:"turnover,string"`
|
||||
Timestamp time.Time `json:"timeStamp"`
|
||||
}
|
||||
|
||||
// SwapTickers stores a map of swap tickers
|
||||
type SwapTickers map[string]SwapTicker
|
||||
|
||||
// SwapKlineItem stores an individual kline data item
|
||||
type SwapKlineItem struct {
|
||||
Time time.Time
|
||||
Open float64
|
||||
Close float64
|
||||
High float64
|
||||
Low float64
|
||||
Volume float64
|
||||
Turnover float64
|
||||
BuyVolume float64
|
||||
BuyTurnover float64
|
||||
}
|
||||
|
||||
// SwapKlines stores an array of kline data
|
||||
type SwapKlines []SwapKlineItem
|
||||
|
||||
// SwapTrade stores an individual trade
|
||||
type SwapTrade struct {
|
||||
Price float64
|
||||
Side order.Side
|
||||
Volume float64
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
// SwapTrades stores an array of swap trades
|
||||
type SwapTrades []SwapTrade
|
||||
|
||||
// SwapAccountInfo returns the swap account balance info
|
||||
type SwapAccountInfo struct {
|
||||
AvailableBalance float64 `json:"availableBalance,string"`
|
||||
FrozenBalance float64 `json:"frozenBalance,string"`
|
||||
MarginBalance float64 `json:"marginBalance,string"`
|
||||
MarginRate float64 `json:"marginRate,string"`
|
||||
Balance float64 `json:"balance,string"`
|
||||
UnrealisedPNL float64 `json:"unrealisedPnl,string"`
|
||||
}
|
||||
|
||||
// SwapPosition stores a single swap position's data
|
||||
type SwapPosition struct {
|
||||
AvailableQuantity float64 `json:"availableQuantity,string"`
|
||||
AveragePrice float64 `json:"averagePrice,string"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
DeleveragePercentile int `json:"deleveragePercentile,string"`
|
||||
Leverage int `json:"leverage,string"`
|
||||
LiquidationPrice float64 `json:"liquidationPrice,string"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
PositionMargin float64 `json:"positionMargin,string"`
|
||||
PositionValue float64 `json:"positionValue,string"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
RateOfReturn float64 `json:"roe,string"`
|
||||
Side string `json:"side"`
|
||||
Symbol string `json:"symbol"`
|
||||
UnrealisedProfitAndLoss float64 `json:"UnrealisedPnl,string"`
|
||||
}
|
||||
|
||||
// SwapPositions stores a collection of swap positions
|
||||
type SwapPositions []SwapPosition
|
||||
|
||||
// SwapPlaceOrderResponse stores the response data for placing a swap order
|
||||
type SwapPlaceOrderResponse struct {
|
||||
OrderID string `json:"orderId"`
|
||||
ClientID string `json:"clientId"`
|
||||
}
|
||||
|
||||
// SwapOrder stores the swap order data
|
||||
type SwapOrder struct {
|
||||
OrderID string `json:"orderId"`
|
||||
Direction string `json:"direction"`
|
||||
Leverage int `json:"leverage,string"`
|
||||
OrderType string `json:"orderType"`
|
||||
Quantitity float64 `json:"quantity,string"`
|
||||
OrderPrice float64 `json:"orderPrice,string"`
|
||||
OrderValue float64 `json:"orderValue,string"`
|
||||
Fee float64 `json:"fee"`
|
||||
FilledQuantity float64 `json:"filledQuantity,string"`
|
||||
AveragePrice float64 `json:"averagePrice,string"`
|
||||
OrderTime time.Time `json:"orderTime"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// SwapOrders stores a collection of swap orders
|
||||
type SwapOrders []SwapOrder
|
||||
|
||||
// OrderCancellationResponse returns a list of cancel order status
|
||||
type OrderCancellationResponse struct {
|
||||
OrderID string `json:"orderId"`
|
||||
Code int `json:"code,string"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// OrderPlacementResponse stores the order placement data
|
||||
type OrderPlacementResponse OrderCancellationResponse
|
||||
|
||||
// SwapOrderFill stores a swap orders fill info
|
||||
type SwapOrderFill struct {
|
||||
Symbol string `json:"symbol"`
|
||||
TradeTime time.Time `json:"tradeTime"`
|
||||
TradeID int64 `json:"tradeId,string"`
|
||||
OrderID int64 `json:"orderId,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
ExecType string `json:"execType"`
|
||||
Side string `json:"side"`
|
||||
Quantity float64 `json:"quantity,string"`
|
||||
}
|
||||
|
||||
// SwapOrderFills stores a collection of swap order fills
|
||||
type SwapOrderFills []SwapOrderFill
|
||||
|
||||
// SwapFundingRate stores a collection of funding rates
|
||||
type SwapFundingRate struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
PositionValue float64 `json:"positionValue,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
FeeRate float64 `json:"feeRate,string"`
|
||||
Leverage int64 `json:"leverage"`
|
||||
}
|
||||
|
||||
@@ -138,11 +138,14 @@ func (c *Coinbene) WsDataHandler() {
|
||||
Last: ticker.Data[x].LastPrice,
|
||||
High: ticker.Data[x].High24h,
|
||||
Low: ticker.Data[x].Low24h,
|
||||
Bid: ticker.Data[x].BestBidPrice,
|
||||
Ask: ticker.Data[x].BestAskPrice,
|
||||
Pair: currency.NewPairFromFormattedPairs(ticker.Data[x].Symbol,
|
||||
c.GetEnabledPairs(asset.PerpetualSwap),
|
||||
c.GetPairFormat(asset.PerpetualSwap, true)),
|
||||
Exchange: c.Name,
|
||||
AssetType: asset.PerpetualSwap,
|
||||
Timestamp: ticker.Data[x].Timestamp,
|
||||
}
|
||||
}
|
||||
case strings.Contains(result[topic].(string), "tradeList"):
|
||||
@@ -182,13 +185,22 @@ func (c *Coinbene) WsDataHandler() {
|
||||
Side: tradeList.Data[0][1],
|
||||
}
|
||||
case strings.Contains(result[topic].(string), "orderBook"):
|
||||
var orderBook WsOrderbook
|
||||
orderBook := struct {
|
||||
Topic string `json:"topic"`
|
||||
Action string `json:"action"`
|
||||
Data []struct {
|
||||
Bids [][]string `json:"bids"`
|
||||
Asks [][]string `json:"asks"`
|
||||
Version int64 `json:"version"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
} `json:"data"`
|
||||
}{}
|
||||
err = json.Unmarshal(stream.Raw, &orderBook)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
p := strings.Replace(orderBook.Topic, "tradeList.", "", 1)
|
||||
p := strings.Replace(orderBook.Topic, "orderBook.", "", 1)
|
||||
cp := currency.NewPairFromFormattedPairs(p,
|
||||
c.GetEnabledPairs(asset.PerpetualSwap),
|
||||
c.GetPairFormat(asset.PerpetualSwap, true))
|
||||
@@ -233,6 +245,7 @@ func (c *Coinbene) WsDataHandler() {
|
||||
newOB.AssetType = asset.PerpetualSwap
|
||||
newOB.Pair = cp
|
||||
newOB.ExchangeName = c.Name
|
||||
newOB.LastUpdated = orderBook.Data[0].Timestamp
|
||||
err = c.Websocket.Orderbook.LoadSnapshot(&newOB)
|
||||
if err != nil {
|
||||
c.Websocket.DataHandler <- err
|
||||
@@ -244,11 +257,12 @@ func (c *Coinbene) WsDataHandler() {
|
||||
}
|
||||
} else if orderBook.Action == "update" {
|
||||
newOB := wsorderbook.WebsocketOrderbookUpdate{
|
||||
Asks: asks,
|
||||
Bids: bids,
|
||||
Asset: asset.PerpetualSwap,
|
||||
Pair: cp,
|
||||
UpdateID: orderBook.Version,
|
||||
Asks: asks,
|
||||
Bids: bids,
|
||||
Asset: asset.PerpetualSwap,
|
||||
Pair: cp,
|
||||
UpdateID: orderBook.Data[0].Version,
|
||||
UpdateTime: orderBook.Data[0].Timestamp,
|
||||
}
|
||||
err = c.Websocket.Orderbook.Update(&newOB)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,7 +2,6 @@ package coinbene
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -56,8 +55,11 @@ func (c *Coinbene) SetDefaults() {
|
||||
c.CurrencyPairs = currency.PairsManager{
|
||||
AssetTypes: asset.Items{
|
||||
asset.Spot,
|
||||
asset.PerpetualSwap,
|
||||
},
|
||||
UseGlobalFormat: true,
|
||||
}
|
||||
|
||||
c.CurrencyPairs.Store(asset.Spot, currency.PairStore{
|
||||
RequestFormat: ¤cy.PairFormat{
|
||||
Uppercase: true,
|
||||
Delimiter: "/",
|
||||
@@ -66,12 +68,22 @@ func (c *Coinbene) SetDefaults() {
|
||||
Uppercase: true,
|
||||
Delimiter: "/",
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
c.CurrencyPairs.Store(asset.PerpetualSwap, currency.PairStore{
|
||||
RequestFormat: ¤cy.PairFormat{
|
||||
Uppercase: true,
|
||||
},
|
||||
ConfigFormat: ¤cy.PairFormat{
|
||||
Uppercase: true,
|
||||
Delimiter: "/",
|
||||
},
|
||||
})
|
||||
|
||||
c.Features = exchange.Features{
|
||||
Supports: exchange.FeaturesSupported{
|
||||
REST: true,
|
||||
Websocket: false, // Purposely disabled until SWAP is supported
|
||||
Websocket: true,
|
||||
RESTCapabilities: protocol.Features{
|
||||
TickerFetching: true,
|
||||
TradeFetching: true,
|
||||
@@ -129,15 +141,6 @@ func (c *Coinbene) Setup(exch *config.ExchangeConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// TO-DO: Remove this once SWAP is supported
|
||||
if exch.Features.Enabled.Websocket {
|
||||
log.Warnf(log.ExchangeSys,
|
||||
"%s websocket only supports SWAP which GoCryptoTrader currently "+
|
||||
"does not. Disabling.\n",
|
||||
c.Name)
|
||||
exch.Features.Enabled.Websocket = false
|
||||
}
|
||||
|
||||
err = c.Websocket.Setup(
|
||||
&wshandler.WebsocketSetup{
|
||||
Enabled: exch.Features.Enabled.Websocket,
|
||||
@@ -150,6 +153,7 @@ func (c *Coinbene) Setup(exch *config.ExchangeConfig) error {
|
||||
Connector: c.WsConnect,
|
||||
Subscriber: c.Subscribe,
|
||||
UnSubscriber: c.Unsubscribe,
|
||||
Features: &c.Features.Supports.WebsocketCapabilities,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -211,14 +215,34 @@ func (c *Coinbene) Run() {
|
||||
|
||||
// FetchTradablePairs returns a list of exchange tradable pairs
|
||||
func (c *Coinbene) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
pairs, err := c.GetAllPairs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if !c.SupportsAsset(a) {
|
||||
return nil, fmt.Errorf("%s does not support asset type %s", c.Name, a)
|
||||
}
|
||||
|
||||
var currencies []string
|
||||
for x := range pairs.Data {
|
||||
currencies = append(currencies, pairs.Data[x].Symbol)
|
||||
switch a {
|
||||
case asset.Spot:
|
||||
pairs, err := c.GetAllPairs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for x := range pairs {
|
||||
currencies = append(currencies, pairs[x].Symbol)
|
||||
}
|
||||
case asset.PerpetualSwap:
|
||||
tickers, err := c.GetSwapTickers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for t := range tickers {
|
||||
idx := strings.Index(t, currency.USDT.String())
|
||||
if idx == 0 {
|
||||
return nil, fmt.Errorf("%s SWAP currency does not contain USDT", c.Name)
|
||||
}
|
||||
currencies = append(currencies,
|
||||
t[0:idx]+c.GetPairFormat(a, false).Delimiter+t[idx:])
|
||||
}
|
||||
}
|
||||
return currencies, nil
|
||||
}
|
||||
@@ -226,38 +250,78 @@ func (c *Coinbene) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
// UpdateTradablePairs updates the exchanges available pairs and stores
|
||||
// them
|
||||
func (c *Coinbene) UpdateTradablePairs(forceUpdate bool) error {
|
||||
pairs, err := c.FetchTradablePairs(asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
assets := c.GetAssetTypes()
|
||||
for x := range assets {
|
||||
pairs, err := c.FetchTradablePairs(assets[x])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.UpdatePairs(currency.NewPairsFromStrings(pairs),
|
||||
assets[x], false, forceUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return c.UpdatePairs(currency.NewPairsFromStrings(pairs),
|
||||
asset.Spot,
|
||||
false,
|
||||
forceUpdate)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (c *Coinbene) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
|
||||
tickerPrice := new(ticker.Price)
|
||||
allPairs := c.GetEnabledPairs(assetType)
|
||||
for x := range allPairs {
|
||||
tempResp, err := c.GetTicker(c.FormatExchangeCurrency(allPairs[x],
|
||||
assetType).String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
resp := new(ticker.Price)
|
||||
if !c.SupportsAsset(assetType) {
|
||||
return nil,
|
||||
fmt.Errorf("%s does not support asset type %s", c.Name, assetType)
|
||||
}
|
||||
|
||||
switch assetType {
|
||||
case asset.Spot:
|
||||
allPairs := c.GetEnabledPairs(assetType)
|
||||
for x := range allPairs {
|
||||
tempResp, err := c.GetTicker(c.FormatExchangeCurrency(allPairs[x],
|
||||
assetType).String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Pair = allPairs[x]
|
||||
resp.Last = tempResp.LatestPrice
|
||||
resp.High = tempResp.DailyHigh
|
||||
resp.Low = tempResp.DailyLow
|
||||
resp.Bid = tempResp.BestBid
|
||||
resp.Ask = tempResp.BestAsk
|
||||
resp.Volume = tempResp.DailyVolume
|
||||
resp.LastUpdated = time.Now()
|
||||
err = ticker.ProcessTicker(c.Name, resp, assetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
tickerPrice.Pair = allPairs[x]
|
||||
tickerPrice.Last = tempResp.TickerData.LatestPrice
|
||||
tickerPrice.High = tempResp.TickerData.DailyHigh
|
||||
tickerPrice.Low = tempResp.TickerData.DailyLow
|
||||
tickerPrice.Bid = tempResp.TickerData.BestBid
|
||||
tickerPrice.Ask = tempResp.TickerData.BestAsk
|
||||
tickerPrice.Volume = tempResp.TickerData.DailyVolume
|
||||
tickerPrice.LastUpdated = time.Now()
|
||||
err = ticker.ProcessTicker(c.Name, tickerPrice, assetType)
|
||||
case asset.PerpetualSwap:
|
||||
tickers, err := c.GetSwapTickers()
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allPairs := c.GetEnabledPairs(assetType)
|
||||
for x := range allPairs {
|
||||
tick, ok := tickers[c.FormatExchangeCurrency(allPairs[x],
|
||||
assetType).String()]
|
||||
if !ok {
|
||||
log.Warnf(log.ExchangeSys,
|
||||
"%s SWAP ticker item was not found", c.Name)
|
||||
continue
|
||||
}
|
||||
resp.Pair = allPairs[x]
|
||||
resp.Last = tick.LastPrice
|
||||
resp.High = tick.High24Hour
|
||||
resp.Low = tick.Low24Hour
|
||||
resp.Bid = tick.BestBidPrice
|
||||
resp.Ask = tick.BestAskPrice
|
||||
resp.Volume = tick.Volume24Hour
|
||||
resp.LastUpdated = tick.Timestamp
|
||||
err = ticker.ProcessTicker(c.Name, resp, assetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return ticker.GetTicker(c.Name, p, assetType)
|
||||
@@ -265,6 +329,11 @@ func (c *Coinbene) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.
|
||||
|
||||
// FetchTicker returns the ticker for a currency pair
|
||||
func (c *Coinbene) FetchTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
|
||||
if !c.SupportsAsset(assetType) {
|
||||
return nil,
|
||||
fmt.Errorf("%s does not support asset type %s", c.Name, assetType)
|
||||
}
|
||||
|
||||
tickerNew, err := ticker.GetTicker(c.Name, p, assetType)
|
||||
if err != nil {
|
||||
return c.UpdateTicker(p, assetType)
|
||||
@@ -274,6 +343,11 @@ func (c *Coinbene) FetchTicker(p currency.Pair, assetType asset.Item) (*ticker.P
|
||||
|
||||
// FetchOrderbook returns orderbook base on the currency pair
|
||||
func (c *Coinbene) FetchOrderbook(currency currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
|
||||
if !c.SupportsAsset(assetType) {
|
||||
return nil,
|
||||
fmt.Errorf("%s does not support asset type %s", c.Name, assetType)
|
||||
}
|
||||
|
||||
ob, err := orderbook.Get(c.Name, currency, assetType)
|
||||
if err != nil {
|
||||
return c.UpdateOrderbook(currency, assetType)
|
||||
@@ -283,47 +357,56 @@ func (c *Coinbene) FetchOrderbook(currency currency.Pair, assetType asset.Item)
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (c *Coinbene) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
|
||||
orderBook := new(orderbook.Base)
|
||||
tempResp, err := c.GetOrderbook(
|
||||
c.FormatExchangeCurrency(p, assetType).String(),
|
||||
100,
|
||||
)
|
||||
resp := new(orderbook.Base)
|
||||
if !c.SupportsAsset(assetType) {
|
||||
return nil,
|
||||
fmt.Errorf("%s does not support asset type %s", c.Name, assetType)
|
||||
}
|
||||
|
||||
var tempResp Orderbook
|
||||
var err error
|
||||
|
||||
switch assetType {
|
||||
case asset.Spot:
|
||||
tempResp, err = c.GetOrderbook(
|
||||
c.FormatExchangeCurrency(p, assetType).String(),
|
||||
100, // TO-DO: Update this once we support configurable orderbook depth
|
||||
)
|
||||
case asset.PerpetualSwap:
|
||||
tempResp, err = c.GetSwapOrderbook(
|
||||
c.FormatExchangeCurrency(p, assetType).String(),
|
||||
100, // TO-DO: Update this once we support configurable orderbook depth
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
return nil, err
|
||||
}
|
||||
orderBook.ExchangeName = c.Name
|
||||
orderBook.Pair = p
|
||||
orderBook.AssetType = assetType
|
||||
var amount, price float64
|
||||
for i := range tempResp.Orderbook.Asks {
|
||||
amount, err = strconv.ParseFloat(tempResp.Orderbook.Asks[i][1], 64)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
resp.ExchangeName = c.Name
|
||||
resp.Pair = p
|
||||
resp.AssetType = assetType
|
||||
for x := range tempResp.Asks {
|
||||
item := orderbook.Item{
|
||||
Price: tempResp.Asks[x].Price,
|
||||
Amount: tempResp.Asks[x].Amount,
|
||||
}
|
||||
price, err = strconv.ParseFloat(tempResp.Orderbook.Asks[i][0], 64)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
if assetType == asset.PerpetualSwap {
|
||||
item.OrderCount = tempResp.Asks[x].Count
|
||||
}
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
|
||||
Price: price,
|
||||
Amount: amount})
|
||||
resp.Asks = append(resp.Asks, item)
|
||||
}
|
||||
for j := range tempResp.Orderbook.Bids {
|
||||
amount, err = strconv.ParseFloat(tempResp.Orderbook.Bids[j][1], 64)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
for x := range tempResp.Bids {
|
||||
item := orderbook.Item{
|
||||
Price: tempResp.Bids[x].Price,
|
||||
Amount: tempResp.Bids[x].Amount,
|
||||
}
|
||||
price, err = strconv.ParseFloat(tempResp.Orderbook.Bids[j][0], 64)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
if assetType == asset.PerpetualSwap {
|
||||
item.OrderCount = tempResp.Bids[x].Count
|
||||
}
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
|
||||
Price: price,
|
||||
Amount: amount})
|
||||
resp.Bids = append(resp.Bids, item)
|
||||
}
|
||||
err = orderBook.Process()
|
||||
err = resp.Process()
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
return nil, err
|
||||
}
|
||||
return orderbook.Get(c.Name, p, assetType)
|
||||
}
|
||||
@@ -332,15 +415,15 @@ func (c *Coinbene) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orde
|
||||
// Coinbene exchange
|
||||
func (c *Coinbene) GetAccountInfo() (exchange.AccountInfo, error) {
|
||||
var info exchange.AccountInfo
|
||||
data, err := c.GetUserBalance()
|
||||
balance, err := c.GetAccountBalances()
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
var account exchange.Account
|
||||
for key := range data.Data {
|
||||
c := currency.NewCode(data.Data[key].Asset)
|
||||
hold := data.Data[key].Reserved
|
||||
available := data.Data[key].Available
|
||||
for key := range balance {
|
||||
c := currency.NewCode(balance[key].Asset)
|
||||
hold := balance[key].Reserved
|
||||
available := balance[key].Available
|
||||
account.Currencies = append(account.Currencies,
|
||||
exchange.AccountCurrencyInfo{CurrencyName: c,
|
||||
TotalValue: hold + available,
|
||||
@@ -374,15 +457,17 @@ func (c *Coinbene) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
|
||||
fmt.Errorf("%s orderside is not supported by this exchange",
|
||||
s.OrderSide)
|
||||
}
|
||||
|
||||
if s.OrderType != order.Limit {
|
||||
return resp, fmt.Errorf("only limit order is supported by this exchange")
|
||||
}
|
||||
tempResp, err := c.PlaceOrder(s.Price,
|
||||
|
||||
tempResp, err := c.PlaceSpotOrder(s.Price,
|
||||
s.Amount,
|
||||
c.FormatExchangeCurrency(s.Pair, asset.Spot).String(),
|
||||
s.OrderSide.String(),
|
||||
s.OrderType.String(),
|
||||
s.ClientID)
|
||||
s.ClientID,
|
||||
0)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
@@ -399,27 +484,27 @@ func (c *Coinbene) ModifyOrder(action *order.Modify) (string, error) {
|
||||
|
||||
// CancelOrder cancels an order by its corresponding ID number
|
||||
func (c *Coinbene) CancelOrder(order *order.Cancel) error {
|
||||
_, err := c.RemoveOrder(order.OrderID)
|
||||
_, err := c.CancelSpotOrder(order.OrderID)
|
||||
return err
|
||||
}
|
||||
|
||||
// CancelAllOrders cancels all orders associated with a currency pair
|
||||
func (c *Coinbene) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
|
||||
var resp order.CancelAllResponse
|
||||
tempMap := make(map[string]string)
|
||||
orders, err := c.FetchOpenOrders(
|
||||
orders, err := c.FetchOpenSpotOrders(
|
||||
c.FormatExchangeCurrency(orderCancellation.CurrencyPair,
|
||||
asset.Spot).String(),
|
||||
)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
for x := range orders.OpenOrders {
|
||||
_, err := c.RemoveOrder(orders.OpenOrders[x].OrderID)
|
||||
tempMap := make(map[string]string)
|
||||
for x := range orders {
|
||||
_, err := c.CancelSpotOrder(orders[x].OrderID)
|
||||
if err != nil {
|
||||
tempMap[orders.OpenOrders[x].OrderID] = "Failed"
|
||||
tempMap[orders[x].OrderID] = "Failed"
|
||||
} else {
|
||||
tempMap[orders.OpenOrders[x].OrderID] = "Success"
|
||||
tempMap[orders[x].OrderID] = "Success"
|
||||
}
|
||||
}
|
||||
resp.Status = tempMap
|
||||
@@ -429,24 +514,24 @@ func (c *Coinbene) CancelAllOrders(orderCancellation *order.Cancel) (order.Cance
|
||||
// GetOrderInfo returns information on a current open order
|
||||
func (c *Coinbene) GetOrderInfo(orderID string) (order.Detail, error) {
|
||||
var resp order.Detail
|
||||
tempResp, err := c.FetchOrderInfo(orderID)
|
||||
tempResp, err := c.FetchSpotOrderInfo(orderID)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
var t time.Time
|
||||
resp.Exchange = c.Name
|
||||
resp.ID = orderID
|
||||
resp.CurrencyPair = currency.NewPairWithDelimiter(tempResp.Order.BaseAsset,
|
||||
resp.CurrencyPair = currency.NewPairWithDelimiter(tempResp.BaseAsset,
|
||||
"/",
|
||||
tempResp.Order.QuoteAsset)
|
||||
t, err = time.Parse(time.RFC3339, tempResp.Order.OrderTime)
|
||||
tempResp.QuoteAsset)
|
||||
t, err = time.Parse(time.RFC3339, tempResp.OrderTime)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Price = tempResp.Order.OrderPrice
|
||||
resp.Price = tempResp.OrderPrice
|
||||
resp.OrderDate = t
|
||||
resp.ExecutedAmount = tempResp.Order.FilledAmount
|
||||
resp.Fee = tempResp.Order.TotalFee
|
||||
resp.ExecutedAmount = tempResp.FilledAmount
|
||||
resp.Fee = tempResp.TotalFee
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -480,47 +565,53 @@ func (c *Coinbene) GetWebsocket() (*wshandler.Websocket, error) {
|
||||
|
||||
// GetActiveOrders retrieves any orders that are active/open
|
||||
func (c *Coinbene) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
|
||||
var resp []order.Detail
|
||||
var tempResp order.Detail
|
||||
var tempData OpenOrderResponse
|
||||
if len(getOrdersRequest.Currencies) == 0 {
|
||||
allPairs, err := c.GetAllPairs()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
return nil, err
|
||||
}
|
||||
for a := range allPairs.Data {
|
||||
getOrdersRequest.Currencies = append(getOrdersRequest.Currencies, currency.NewPairFromString(allPairs.Data[a].Symbol))
|
||||
for a := range allPairs {
|
||||
getOrdersRequest.Currencies = append(getOrdersRequest.Currencies,
|
||||
currency.NewPairFromString(allPairs[a].Symbol))
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
var resp []order.Detail
|
||||
|
||||
for x := range getOrdersRequest.Currencies {
|
||||
tempData, err = c.FetchOpenOrders(
|
||||
var tempData OrdersInfo
|
||||
tempData, err = c.FetchOpenSpotOrders(
|
||||
c.FormatExchangeCurrency(
|
||||
getOrdersRequest.Currencies[x],
|
||||
asset.Spot).String(),
|
||||
)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
return nil, err
|
||||
}
|
||||
var t time.Time
|
||||
for y := range tempData.OpenOrders {
|
||||
|
||||
for y := range tempData {
|
||||
var tempResp order.Detail
|
||||
tempResp.Exchange = c.Name
|
||||
tempResp.CurrencyPair = getOrdersRequest.Currencies[x]
|
||||
tempResp.OrderSide = order.Buy
|
||||
if strings.EqualFold(tempData.OpenOrders[y].OrderType, order.Sell.String()) {
|
||||
if strings.EqualFold(tempData[y].OrderType, order.Sell.String()) {
|
||||
tempResp.OrderSide = order.Sell
|
||||
}
|
||||
t, err = time.Parse(time.RFC3339, tempData.OpenOrders[y].OrderTime)
|
||||
|
||||
var t time.Time
|
||||
t, err = time.Parse(time.RFC3339, tempData[y].OrderTime)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tempResp.OrderDate = t
|
||||
tempResp.Status = order.Status(tempData.OpenOrders[y].OrderStatus)
|
||||
tempResp.Price = tempData.OpenOrders[y].OrderPrice
|
||||
tempResp.Amount = tempData.OpenOrders[y].Amount
|
||||
tempResp.ExecutedAmount = tempData.OpenOrders[y].FilledAmount
|
||||
tempResp.RemainingAmount = tempData.OpenOrders[y].Amount - tempData.OpenOrders[y].FilledAmount
|
||||
tempResp.Fee = tempData.OpenOrders[y].TotalFee
|
||||
tempResp.Status = order.Status(tempData[y].OrderStatus)
|
||||
tempResp.Price = tempData[y].OrderPrice
|
||||
tempResp.Amount = tempData[y].Amount
|
||||
tempResp.ExecutedAmount = tempData[y].FilledAmount
|
||||
tempResp.RemainingAmount = tempData[y].Amount - tempData[y].FilledAmount
|
||||
tempResp.Fee = tempData[y].TotalFee
|
||||
resp = append(resp, tempResp)
|
||||
}
|
||||
}
|
||||
@@ -530,19 +621,21 @@ func (c *Coinbene) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]
|
||||
// GetOrderHistory retrieves account order information
|
||||
// Can Limit response to specific order status
|
||||
func (c *Coinbene) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
|
||||
var resp []order.Detail
|
||||
var tempResp order.Detail
|
||||
var tempData ClosedOrderResponse
|
||||
if len(getOrdersRequest.Currencies) == 0 {
|
||||
allPairs, err := c.GetAllPairs()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
return nil, err
|
||||
}
|
||||
for a := range allPairs.Data {
|
||||
getOrdersRequest.Currencies = append(getOrdersRequest.Currencies, currency.NewPairFromString(allPairs.Data[a].Symbol))
|
||||
for a := range allPairs {
|
||||
getOrdersRequest.Currencies = append(getOrdersRequest.Currencies,
|
||||
currency.NewPairFromString(allPairs[a].Symbol))
|
||||
}
|
||||
}
|
||||
|
||||
var resp []order.Detail
|
||||
var tempData OrdersInfo
|
||||
var err error
|
||||
|
||||
for x := range getOrdersRequest.Currencies {
|
||||
tempData, err = c.FetchClosedOrders(
|
||||
c.FormatExchangeCurrency(
|
||||
@@ -551,27 +644,31 @@ func (c *Coinbene) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]
|
||||
"",
|
||||
)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
return nil, err
|
||||
}
|
||||
var t time.Time
|
||||
for y := range tempData.Data {
|
||||
|
||||
for y := range tempData {
|
||||
var tempResp order.Detail
|
||||
tempResp.Exchange = c.Name
|
||||
tempResp.CurrencyPair = getOrdersRequest.Currencies[x]
|
||||
tempResp.OrderSide = order.Buy
|
||||
if strings.EqualFold(tempData.Data[y].OrderType, order.Sell.String()) {
|
||||
if strings.EqualFold(tempData[y].OrderType, order.Sell.String()) {
|
||||
tempResp.OrderSide = order.Sell
|
||||
}
|
||||
t, err = time.Parse(time.RFC3339, tempData.Data[y].OrderTime)
|
||||
|
||||
var t time.Time
|
||||
t, err = time.Parse(time.RFC3339, tempData[y].OrderTime)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tempResp.OrderDate = t
|
||||
tempResp.Status = order.Status(tempData.Data[y].OrderStatus)
|
||||
tempResp.Price = tempData.Data[y].OrderPrice
|
||||
tempResp.Amount = tempData.Data[y].Amount
|
||||
tempResp.ExecutedAmount = tempData.Data[y].FilledAmount
|
||||
tempResp.RemainingAmount = tempData.Data[y].Amount - tempData.Data[y].FilledAmount
|
||||
tempResp.Fee = tempData.Data[y].TotalFee
|
||||
tempResp.Status = order.Status(tempData[y].OrderStatus)
|
||||
tempResp.Price = tempData[y].OrderPrice
|
||||
tempResp.Amount = tempData[y].Amount
|
||||
tempResp.ExecutedAmount = tempData[y].FilledAmount
|
||||
tempResp.RemainingAmount = tempData[y].Amount - tempData[y].FilledAmount
|
||||
tempResp.Fee = tempData[y].TotalFee
|
||||
resp = append(resp, tempResp)
|
||||
}
|
||||
}
|
||||
@@ -590,9 +687,9 @@ func (c *Coinbene) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error
|
||||
}
|
||||
switch feeBuilder.IsMaker {
|
||||
case true:
|
||||
fee = feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.Data.MakerFeeRate
|
||||
fee = feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.MakerFeeRate
|
||||
case false:
|
||||
fee = feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.Data.TakerFeeRate
|
||||
fee = feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.TakerFeeRate
|
||||
}
|
||||
return fee, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user