Kraken wsCancelAllOrders added, fix bugs in wsAddOrder, added new API endpoint CancelBatchOrders (#596)

* GetClosedOrder implemented for Kraken and Binance, fixed Binance MARKET order creaton, added rate, fee and cost fileds on SubmitOrder responce

* return Trades on Binance SubmitOrder, new validation methods on Binance and kraken GetClosedOrderInfo

* removed the Binance extra method GetClosedOrder

* func description corrected

* removed price, fee and cost from SimulateOrder response, as we get all necessary info in response to calculate them on client side

* GetClosedOrder implementation moved to GetOrderInfo

* changed GetOrderInfo params

* removed Canceled order.Type used for Kraken

* update QueryOrder in gctscript

* add missed params to QueryOrder validator (gctscript)

* fixed testing issues

* GetClosedOrder implemented for Kraken and Binance, fixed Binance MARKET order creaton, added rate, fee and cost fileds on SubmitOrder responce

* return Trades on Binance SubmitOrder, new validation methods on Binance and kraken GetClosedOrderInfo

* removed the Binance extra method GetClosedOrder

* func description corrected

* removed price, fee and cost from SimulateOrder response, as we get all necessary info in response to calculate them on client side

* GetClosedOrder implementation moved to GetOrderInfo

* changed GetOrderInfo params

* removed Canceled order.Type used for Kraken

* update QueryOrder in gctscript

* add missed params to QueryOrder validator (gctscript)

* fixed testing issues

* pull previous changes

* linter issues fix

* updated query_order exmple in gctscript, fixed params check

* removed orderPair unnecessary conversion

* added wsCancelAllOrders, fixed bugs

* fixed Kraken wsAddOrder method

* cleanup

* CancelBatchOrders implementation

* changed CancelBatchOrders signature

* fixed tests and wrappers

* btcmarkets_test fix

* cleanup

* cleanup

* changed CancelBatchOrders signature

* fmt

* Update configtest.json

* Update configtest.json

* rollback configtest

* refactored Kraken wsHandleData to allow tests

* removed unnecessary error test in TestWsAddOrderJSON

* dependencies updates

Co-authored-by: Vazha Bezhanishvili <vazha.bezhanishvili@elegro.eu>
This commit is contained in:
Vazha
2020-11-24 01:35:31 +02:00
committed by GitHub
parent 5478442d65
commit 3ee99f0b87
51 changed files with 2945 additions and 1808 deletions

View File

@@ -268,6 +268,11 @@ func (a *Alphapoint) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (a *Alphapoint) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders for a given account
func (a *Alphapoint) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
if err := orderCancellation.Validate(); err != nil {

View File

@@ -101,3 +101,8 @@ func New(input string) (Item, error) {
input,
supported)
}
// UseDefault returns default asset type
func UseDefault() Item {
return Spot
}

View File

@@ -664,6 +664,11 @@ func (b *Binance) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *Binance) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *Binance) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -650,6 +650,11 @@ func (b *Bitfinex) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *Bitfinex) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *Bitfinex) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
var err error

View File

@@ -369,6 +369,11 @@ func (b *Bitflyer) CancelOrder(_ *order.Cancel) error {
return common.ErrNotYetImplemented
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *Bitflyer) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *Bitflyer) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
// TODO, implement BitFlyer API

View File

@@ -449,6 +449,11 @@ func (b *Bithumb) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *Bithumb) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *Bithumb) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
if err := orderCancellation.Validate(); err != nil {

View File

@@ -582,6 +582,11 @@ func (b *Bitmex) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *Bitmex) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *Bitmex) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -495,6 +495,11 @@ func (b *Bitstamp) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *Bitstamp) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *Bitstamp) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
success, err := b.CancelAllExistingOrders()

View File

@@ -479,6 +479,11 @@ func (b *Bittrex) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *Bittrex) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *Bittrex) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -671,7 +671,7 @@ func (b *BTCMarkets) GetBatchTrades(ids []string) (BatchTradeResponse, error) {
}
// CancelBatchOrders cancels given ids
func (b *BTCMarkets) CancelBatchOrders(ids []string) (BatchCancelResponse, error) {
func (b *BTCMarkets) CancelBatch(ids []string) (BatchCancelResponse, error) {
var resp BatchCancelResponse
marketIDs := strings.Join(ids, ",")
return resp, b.SendAuthenticatedRequest(http.MethodDelete,

View File

@@ -439,13 +439,13 @@ func TestGetBatchTrades(t *testing.T) {
}
}
func TestCancelBatchOrders(t *testing.T) {
func TestCancelBatch(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
}
temp := []string{"4477045999", "4477381751", "4477381751"}
_, err := b.CancelBatchOrders(temp)
_, err := b.CancelBatch(temp)
if err != nil {
t.Error(err)
}

View File

@@ -527,6 +527,11 @@ func (b *BTCMarkets) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *BTCMarkets) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *BTCMarkets) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
var resp order.CancelAllResponse
@@ -541,7 +546,7 @@ func (b *BTCMarkets) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse,
}
splitOrders := common.SplitStringSliceByLimit(orderIDs, 20)
for z := range splitOrders {
tempResp, err := b.CancelBatchOrders(splitOrders[z])
tempResp, err := b.CancelBatch(splitOrders[z])
if err != nil {
return resp, err
}

View File

@@ -520,6 +520,11 @@ func (b *BTSE) CancelOrder(o *order.Cancel) error {
return nil
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (b *BTSE) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
// If product ID is sent, all orders of that specified market will be cancelled
// If not specified, all orders of all markets will be cancelled

View File

@@ -546,6 +546,11 @@ func (c *CoinbasePro) CancelOrder(o *order.Cancel) error {
return c.CancelExistingOrder(o.ID)
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (c *CoinbasePro) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (c *CoinbasePro) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
// CancellAllExisting orders returns a list of successful cancellations, we're only interested in failures

View File

@@ -598,6 +598,11 @@ func (c *Coinbene) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (c *Coinbene) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (c *Coinbene) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
if err := orderCancellation.Validate(); err != nil {

View File

@@ -676,6 +676,11 @@ func (c *COINUT) CancelOrder(o *order.Cancel) error {
return nil
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (c *COINUT) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (c *COINUT) CancelAllOrders(details *order.Cancel) (order.CancelAllResponse, error) {
if err := details.Validate(); err != nil {

View File

@@ -468,6 +468,11 @@ func (e *EXMO) CancelOrder(o *order.Cancel) error {
return e.CancelExistingOrder(orderIDInt)
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (e *EXMO) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (e *EXMO) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -601,6 +601,11 @@ func (f *FTX) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (f *FTX) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (f *FTX) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
if err := orderCancellation.Validate(); err != nil {

View File

@@ -530,6 +530,11 @@ func (g *Gateio) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (g *Gateio) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (g *Gateio) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -454,6 +454,11 @@ func (g *Gemini) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (g *Gemini) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (g *Gemini) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -581,6 +581,11 @@ func (h *HitBTC) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (h *HitBTC) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (h *HitBTC) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -667,6 +667,11 @@ func (h *HUOBI) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (h *HUOBI) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (h *HUOBI) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
if err := orderCancellation.Validate(); err != nil {

View File

@@ -53,6 +53,7 @@ type IBotExchange interface {
SubmitOrder(s *order.Submit) (order.SubmitResponse, error)
ModifyOrder(action *order.Modify) (string, error)
CancelOrder(o *order.Cancel) error
CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error)
CancelAllOrders(orders *order.Cancel) (order.CancelAllResponse, error)
GetOrderInfo(orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error)
GetDepositAddress(cryptocurrency currency.Code, accountID string) (string, error)

View File

@@ -419,6 +419,11 @@ func (i *ItBit) CancelOrder(o *order.Cancel) error {
return i.CancelExistingOrder(o.WalletAddress, o.ID)
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (i *ItBit) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (i *ItBit) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
if err := orderCancellation.Validate(); err != nil {

View File

@@ -55,6 +55,9 @@ const (
// Rate limit consts
krakenRateInterval = time.Second
krakenRequestRate = 1
// Status consts
StatusOpen = "open"
)
var (

View File

@@ -543,6 +543,34 @@ func TestCancelExchangeOrder(t *testing.T) {
}
}
// TestCancelExchangeOrder wrapper test
func TestCancelBatchExchangeOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
pair := currency.Pair{
Delimiter: "/",
Base: currency.BTC,
Quote: currency.USD,
}
var ordersCancellation []order.Cancel
ordersCancellation = append(ordersCancellation, order.Cancel{
Pair: pair,
ID: "OGEX6P-B5Q74-IGZ72R,OGEX6P-B5Q74-IGZ722",
AssetType: asset.Spot,
})
_, err := k.CancelBatchOrders(ordersCancellation)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not cancel orders: %v", err)
}
}
// TestCancelAllExchangeOrders wrapper test
func TestCancelAllExchangeOrders(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
@@ -778,6 +806,14 @@ func TestWsCancelOrder(t *testing.T) {
}
}
func TestWsCancelAllOrders(t *testing.T) {
setupWsTests(t)
_, err := k.wsCancelAllOrders()
if err != nil {
t.Error(err)
}
}
func TestWsPong(t *testing.T) {
pressXToJSON := []byte(`{
"event": "pong",
@@ -1376,16 +1412,6 @@ func TestWsAddOrderJSON(t *testing.T) {
if err != nil {
t.Error(err)
}
pressXToJSON = []byte(`{
"errorMessage": "EOrder:Order minimum not met",
"event": "addOrderStatus",
"status": "error"
}`)
err = k.wsHandleData(pressXToJSON)
if err == nil {
t.Error("Expected error")
}
}
func TestWsCancelOrderJSON(t *testing.T) {

View File

@@ -579,12 +579,13 @@ type WsOpenOrderDescription struct {
type WsAddOrderRequest struct {
Event string `json:"event"`
Token string `json:"token"`
RequestID int64 `json:"reqid,omitempty"` // Optional, client originated ID reflected in response message.
OrderType string `json:"ordertype"`
OrderSide string `json:"type"`
Pair string `json:"pair"`
Price float64 `json:"price,omitempty"` // optional
Price2 float64 `json:"price2,omitempty"` // optional
Volume float64 `json:"volume,omitempty"`
Price float64 `json:"price,string,omitempty"` // optional
Price2 float64 `json:"price2,string,omitempty"` // optional
Volume float64 `json:"volume,string,omitempty"`
Leverage float64 `json:"leverage,omitempty"` // optional
OFlags string `json:"oflags,omitempty"` // optional
StartTime string `json:"starttm,omitempty"` // optional
@@ -598,10 +599,11 @@ type WsAddOrderRequest struct {
// WsAddOrderResponse response data for ws order
type WsAddOrderResponse struct {
Description string `json:"descr"`
Event string `json:"event"`
RequestID int64 `json:"reqid"`
Status string `json:"status"`
TransactionID string `json:"txid"`
Description string `json:"descr"`
ErrorMessage string `json:"errorMessage"`
}
@@ -609,12 +611,15 @@ type WsAddOrderResponse struct {
type WsCancelOrderRequest struct {
Event string `json:"event"`
Token string `json:"token"`
TransactionIDs []string `json:"txid"`
TransactionIDs []string `json:"txid,omitempty"`
RequestID int64 `json:"reqid,omitempty"` // Optional, client originated ID reflected in response message.
}
// WsCancelOrderResponse response data for ws cancel order
// WsCancelOrderResponse response data for ws cancel order and ws cancel all orders
type WsCancelOrderResponse struct {
Event string `json:"event"`
Status string `json:"status"`
ErrorMessage string `json:"errorMessage"`
RequestID int64 `json:"reqid"`
Count int64 `json:"count"`
}

View File

@@ -7,6 +7,7 @@ import (
"net/http"
"strconv"
"strings"
"sync"
"time"
"github.com/gorilla/websocket"
@@ -29,26 +30,28 @@ const (
krakenWSURL = "wss://ws.kraken.com"
krakenAuthWSURL = "wss://ws-auth.kraken.com"
krakenWSSandboxURL = "wss://sandbox.kraken.com"
krakenWSSupportedVersion = "1.0.0"
krakenWSSupportedVersion = "1.4.0"
// WS endpoints
krakenWsHeartbeat = "heartbeat"
krakenWsSystemStatus = "systemStatus"
krakenWsSubscribe = "subscribe"
krakenWsSubscriptionStatus = "subscriptionStatus"
krakenWsUnsubscribe = "unsubscribe"
krakenWsTicker = "ticker"
krakenWsOHLC = "ohlc"
krakenWsTrade = "trade"
krakenWsSpread = "spread"
krakenWsOrderbook = "book"
krakenWsOwnTrades = "ownTrades"
krakenWsOpenOrders = "openOrders"
krakenWsAddOrder = "addOrder"
krakenWsCancelOrder = "cancelOrder"
krakenWsAddOrderStatus = "addOrderStatus"
krakenWsCancelOrderStatus = "cancelOrderStatus"
krakenWsRateLimit = 50
krakenWsPingDelay = time.Second * 27
krakenWsHeartbeat = "heartbeat"
krakenWsSystemStatus = "systemStatus"
krakenWsSubscribe = "subscribe"
krakenWsSubscriptionStatus = "subscriptionStatus"
krakenWsUnsubscribe = "unsubscribe"
krakenWsTicker = "ticker"
krakenWsOHLC = "ohlc"
krakenWsTrade = "trade"
krakenWsSpread = "spread"
krakenWsOrderbook = "book"
krakenWsOwnTrades = "ownTrades"
krakenWsOpenOrders = "openOrders"
krakenWsAddOrder = "addOrder"
krakenWsCancelOrder = "cancelOrder"
krakenWsCancelAll = "cancelAll"
krakenWsAddOrderStatus = "addOrderStatus"
krakenWsCancelOrderStatus = "cancelOrderStatus"
krakenWsCancelAllOrderStatus = "cancelAllStatus"
krakenWsRateLimit = 50
krakenWsPingDelay = time.Second * 27
)
// orderbookMutex Ensures if two entries arrive at once, only one can be
@@ -66,6 +69,14 @@ var defaultSubscribedChannels = []string{krakenWsTicker,
krakenWsSpread}
var authenticatedChannels = []string{krakenWsOwnTrades, krakenWsOpenOrders}
var cancelOrdersStatusMutex sync.Mutex
var cancelOrdersStatus = make(map[int64]*struct {
Total int // total count of orders in wsCancelOrders request
Successful int // numbers of Successfully canceled orders in wsCancelOrders request
Unsuccessful int // numbers of Unsuccessfully canceled orders in wsCancelOrders request
Error string // if at least one of requested order return fail, store error here
})
// WsConnect initiates a websocket connection
func (k *Kraken) WsConnect() error {
if !k.Websocket.IsEnabled() || !k.IsEnabled() {
@@ -160,6 +171,26 @@ func (k *Kraken) wsReadData(comms chan stream.Response) {
}
}
// awaitForCancelOrderResponses used to wait until all responses will received for appropriate CancelOrder request
// success param = was the response from Kraken successful or not
func isAwaitingCancelOrderResponses(requestID int64, success bool) bool {
cancelOrdersStatusMutex.Lock()
if stat, ok := cancelOrdersStatus[requestID]; ok {
if success {
cancelOrdersStatus[requestID].Successful++
} else {
cancelOrdersStatus[requestID].Unsuccessful++
}
if stat.Successful+stat.Unsuccessful != stat.Total {
cancelOrdersStatusMutex.Unlock()
return true
}
}
cancelOrdersStatusMutex.Unlock()
return false
}
func (k *Kraken) wsHandleData(respRaw []byte) error {
if strings.HasPrefix(string(respRaw), "[") {
var dataResponse WebsocketDataResponse
@@ -190,8 +221,65 @@ func (k *Kraken) wsHandleData(respRaw []byte) error {
}
if event, ok := eventResponse["event"]; ok {
switch event {
case stream.Pong, krakenWsHeartbeat, krakenWsCancelOrderStatus:
case stream.Pong, krakenWsHeartbeat:
return nil
case krakenWsCancelOrderStatus:
var status WsCancelOrderResponse
err := json.Unmarshal(respRaw, &status)
if err != nil {
return fmt.Errorf("%s - err %s unable to parse WsCancelOrderResponse: %s",
k.Name,
err,
respRaw)
}
success := true
if status.Status == "error" {
success = false
cancelOrdersStatusMutex.Lock()
if _, ok := cancelOrdersStatus[status.RequestID]; ok {
if cancelOrdersStatus[status.RequestID].Error == "" { // save the first error, if any
cancelOrdersStatus[status.RequestID].Error = status.ErrorMessage
}
}
cancelOrdersStatusMutex.Unlock()
}
if isAwaitingCancelOrderResponses(status.RequestID, success) {
return nil
}
// all responses handled, return results stored in cancelOrdersStatus
if status.RequestID > 0 && !k.Websocket.Match.IncomingWithData(status.RequestID, respRaw) {
return fmt.Errorf("can't send ws incoming data to Matched channel with RequestID: %d",
status.RequestID)
}
case krakenWsCancelAllOrderStatus:
var status WsCancelOrderResponse
err := json.Unmarshal(respRaw, &status)
if err != nil {
return fmt.Errorf("%s - err %s unable to parse WsCancelOrderResponse: %s",
k.Name,
err,
respRaw)
}
var isChannelExist bool
if status.RequestID > 0 {
isChannelExist = k.Websocket.Match.IncomingWithData(status.RequestID, respRaw)
}
if status.Status == "error" {
return fmt.Errorf("%v Websocket status for RequestID %d: '%v'",
k.Name,
status.RequestID,
status.ErrorMessage)
}
if !isChannelExist && status.RequestID > 0 {
return fmt.Errorf("can't send ws incoming data to Matched channel with RequestID: %d",
status.RequestID)
}
case krakenWsSystemStatus:
var systemStatus wsSystemStatus
err := json.Unmarshal(respRaw, &systemStatus)
@@ -222,16 +310,29 @@ func (k *Kraken) wsHandleData(respRaw []byte) error {
err,
respRaw)
}
if status.ErrorMessage != "" {
return fmt.Errorf("%s - err %s",
var isChannelExist bool
if status.RequestID > 0 {
isChannelExist = k.Websocket.Match.IncomingWithData(status.RequestID, respRaw)
}
if status.Status == "error" {
return fmt.Errorf("%v Websocket status for RequestID %d: '%v'",
k.Name,
status.RequestID,
status.ErrorMessage)
}
k.Websocket.DataHandler <- &order.Detail{
Exchange: k.Name,
ID: status.TransactionID,
Status: order.New,
}
if !isChannelExist && status.RequestID > 0 {
return fmt.Errorf("can't send ws incoming data to Matched channel with RequestID: %d",
status.RequestID)
}
case krakenWsSubscriptionStatus:
var sub wsSubscription
err := json.Unmarshal(respRaw, &sub)
@@ -378,7 +479,7 @@ func (k *Kraken) wsProcessOwnTrades(ownOrders interface{}) error {
Side: oSide,
Timestamp: convert.TimeFromUnixTimestampDecimal(val.Time),
}
k.Websocket.DataHandler <- &order.Modify{
k.Websocket.DataHandler <- &order.Detail{
Exchange: k.Name,
ID: val.OrderTransactionID,
Trades: []order.TradeHistory{trade},
@@ -1004,9 +1105,10 @@ channels:
return nil
}
// wsAddOrder creates an order, returned order ID if success
func (k *Kraken) wsAddOrder(request *WsAddOrderRequest) (string, error) {
id := k.Websocket.AuthConn.GenerateMessageID(false)
request.UserReferenceID = strconv.FormatInt(id, 10)
request.RequestID = id
request.Event = krakenWsAddOrder
request.Token = authToken
jsonResp, err := k.Websocket.AuthConn.SendMessageReturnResponse(id, request)
@@ -1024,11 +1126,66 @@ func (k *Kraken) wsAddOrder(request *WsAddOrderRequest) (string, error) {
return resp.TransactionID, nil
}
// wsCancelOrders cancels one or more open orders passed in orderIDs param
func (k *Kraken) wsCancelOrders(orderIDs []string) error {
id := k.Websocket.AuthConn.GenerateMessageID(false)
request := WsCancelOrderRequest{
Event: krakenWsCancelOrder,
Token: authToken,
TransactionIDs: orderIDs,
RequestID: id,
}
return k.Websocket.AuthConn.SendJSONMessage(request)
cancelOrdersStatus[id] = &struct {
Total int
Successful int
Unsuccessful int
Error string
}{
Total: len(orderIDs),
}
defer delete(cancelOrdersStatus, id)
_, err := k.Websocket.AuthConn.SendMessageReturnResponse(id, request)
if err != nil {
return err
}
successful := cancelOrdersStatus[id].Successful
if cancelOrdersStatus[id].Error != "" || len(orderIDs) != successful { // strange Kraken logic ...
var reason string
if cancelOrdersStatus[id].Error != "" {
reason = fmt.Sprintf(" Reason: %s", cancelOrdersStatus[id].Error)
}
return fmt.Errorf("%s cancelled %d out of %d orders.%s",
k.Name, successful, len(orderIDs), reason)
}
return nil
}
// wsCancelAllOrders cancels all opened orders
// Returns number (count param) of affected orders or 0 if no open orders found
func (k *Kraken) wsCancelAllOrders() (*WsCancelOrderResponse, error) {
id := k.Websocket.AuthConn.GenerateMessageID(false)
request := WsCancelOrderRequest{
Event: krakenWsCancelAll,
Token: authToken,
RequestID: id,
}
jsonResp, err := k.Websocket.AuthConn.SendMessageReturnResponse(id, request)
if err != nil {
return &WsCancelOrderResponse{}, err
}
var resp WsCancelOrderResponse
err = json.Unmarshal(jsonResp, &resp)
if err != nil {
return &WsCancelOrderResponse{}, err
}
if resp.ErrorMessage != "" {
return &WsCancelOrderResponse{}, fmt.Errorf(k.Name + " - " + resp.ErrorMessage)
}
return &resp, nil
}

View File

@@ -69,7 +69,7 @@ func (k *Kraken) SetDefaults() {
Delimiter: currency.DashDelimiter,
Separator: ",",
}
err := k.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
err := k.SetGlobalPairsManager(requestFmt, configFmt, asset.UseDefault())
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
@@ -229,7 +229,7 @@ func (k *Kraken) Run() {
}
forceUpdate := false
format, err := k.GetPairFormat(asset.Spot, false)
format, err := k.GetPairFormat(asset.UseDefault(), false)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
@@ -237,7 +237,7 @@ func (k *Kraken) Run() {
err)
return
}
enabled, err := k.GetEnabledPairs(asset.Spot)
enabled, err := k.GetEnabledPairs(asset.UseDefault())
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
@@ -246,7 +246,7 @@ func (k *Kraken) Run() {
return
}
avail, err := k.GetAvailablePairs(asset.Spot)
avail, err := k.GetAvailablePairs(asset.UseDefault())
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
@@ -271,7 +271,7 @@ func (k *Kraken) Run() {
log.Warn(log.ExchangeSys, "Available pairs for Kraken reset due to config upgrade, please enable the ones you would like again")
forceUpdate = true
err = k.UpdatePairs(p, asset.Spot, true, true)
err = k.UpdatePairs(p, asset.UseDefault(), true, true)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update currencies. Err: %s\n",
@@ -343,7 +343,7 @@ func (k *Kraken) FetchTradablePairs(asset asset.Item) ([]string, error) {
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (k *Kraken) UpdateTradablePairs(forceUpdate bool) error {
pairs, err := k.FetchTradablePairs(asset.Spot)
pairs, err := k.FetchTradablePairs(asset.UseDefault())
if err != nil {
return err
}
@@ -352,7 +352,7 @@ func (k *Kraken) UpdateTradablePairs(forceUpdate bool) error {
if err != nil {
return err
}
return k.UpdatePairs(p, asset.Spot, false, forceUpdate)
return k.UpdatePairs(p, asset.UseDefault(), false, forceUpdate)
}
// UpdateTicker updates and returns the ticker for a currency pair
@@ -569,9 +569,10 @@ func (k *Kraken) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
var resp string
s.Pair.Delimiter = "/" // required pair format: ISO 4217-A3
resp, err = k.wsAddOrder(&WsAddOrderRequest{
OrderType: s.Type.String(),
OrderSide: s.Side.String(),
OrderType: strings.ToLower(s.Type.String()),
OrderSide: strings.ToLower(s.Side.String()),
Pair: s.Pair.String(),
Price: s.Price,
Volume: s.Amount,
@@ -628,12 +629,40 @@ func (k *Kraken) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (k *Kraken) CancelBatchOrders(orders []order.Cancel) (order.CancelBatchResponse, error) {
var ordersList []string
for i := range orders {
if err := orders[i].Validate(orders[i].StandardCancel()); err != nil {
return order.CancelBatchResponse{}, err
}
ordersList = append(ordersList, orders[i].ID)
}
if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
err := k.wsCancelOrders(ordersList)
return order.CancelBatchResponse{}, err
}
return order.CancelBatchResponse{}, common.ErrFunctionNotSupported
}
// CancelAllOrders cancels all orders associated with a currency pair
func (k *Kraken) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{
Status: make(map[string]string),
}
if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
resp, err := k.wsCancelAllOrders()
if err != nil {
return cancelAllOrdersResponse, err
}
cancelAllOrdersResponse.Count = resp.Count
return cancelAllOrdersResponse, err
}
var emptyOrderOptions OrderInfoOptions
openOrders, err := k.GetOpenOrders(emptyOrderOptions)
if err != nil {
@@ -668,8 +697,8 @@ func (k *Kraken) GetOrderInfo(orderID string, pair currency.Pair, assetType asse
return orderDetail, fmt.Errorf("order %s not found in response", orderID)
}
if assetType == "" {
assetType = asset.Spot
if !assetType.IsValid() {
assetType = asset.UseDefault()
}
avail, err := k.GetAvailablePairs(assetType)
@@ -707,6 +736,12 @@ func (k *Kraken) GetOrderInfo(orderID string, pair currency.Pair, assetType asse
if err != nil {
return orderDetail, err
}
price := orderInfo.Price
if orderInfo.Status == StatusOpen {
price = orderInfo.Description.Price
}
orderDetail = order.Detail{
Exchange: k.Name,
ID: orderID,
@@ -716,7 +751,7 @@ func (k *Kraken) GetOrderInfo(orderID string, pair currency.Pair, assetType asse
Date: convert.TimeFromUnixTimestampDecimal(orderInfo.OpenTime),
CloseTime: convert.TimeFromUnixTimestampDecimal(orderInfo.CloseTime),
Status: status,
Price: orderInfo.Price,
Price: price,
Amount: orderInfo.Volume,
ExecutedAmount: orderInfo.VolumeExecuted,
RemainingAmount: orderInfo.Volume - orderInfo.VolumeExecuted,
@@ -810,12 +845,17 @@ func (k *Kraken) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e
return nil, err
}
avail, err := k.GetAvailablePairs(asset.Spot)
assetType := req.AssetType
if !req.AssetType.IsValid() {
assetType = asset.UseDefault()
}
avail, err := k.GetAvailablePairs(assetType)
if err != nil {
return nil, err
}
fmt, err := k.GetPairFormat(asset.Spot, true)
format, err := k.GetPairFormat(assetType, true)
if err != nil {
return nil, err
}
@@ -824,7 +864,7 @@ func (k *Kraken) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e
for i := range resp.Open {
p, err := currency.NewPairFromFormattedPairs(resp.Open[i].Description.Pair,
avail,
fmt)
format)
if err != nil {
return nil, err
}
@@ -865,12 +905,17 @@ func (k *Kraken) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]or
req.End = strconv.FormatInt(getOrdersRequest.EndTicks.Unix(), 10)
}
avail, err := k.GetAvailablePairs(asset.Spot)
assetType := getOrdersRequest.AssetType
if !getOrdersRequest.AssetType.IsValid() {
assetType = asset.UseDefault()
}
avail, err := k.GetAvailablePairs(assetType)
if err != nil {
return nil, err
}
fmt, err := k.GetPairFormat(asset.Spot, true)
format, err := k.GetPairFormat(assetType, true)
if err != nil {
return nil, err
}
@@ -884,7 +929,7 @@ func (k *Kraken) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]or
for i := range resp.Closed {
p, err := currency.NewPairFromFormattedPairs(resp.Closed[i].Description.Pair,
avail,
fmt)
format)
if err != nil {
return nil, err
}

View File

@@ -442,6 +442,11 @@ func (l *LakeBTC) CancelOrder(o *order.Cancel) error {
return l.CancelExistingOrder(orderIDInt)
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (l *LakeBTC) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (l *LakeBTC) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
var cancelAllOrdersResponse order.CancelAllResponse

View File

@@ -447,6 +447,11 @@ func (l *Lbank) CancelOrder(o *order.Cancel) error {
return err
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (l *Lbank) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (l *Lbank) CancelAllOrders(o *order.Cancel) (order.CancelAllResponse, error) {
if err := o.Validate(); err != nil {

View File

@@ -414,6 +414,11 @@ func (l *LocalBitcoins) CancelOrder(o *order.Cancel) error {
return l.DeleteAd(o.ID)
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (l *LocalBitcoins) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (l *LocalBitcoins) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -509,3 +509,8 @@ func (o *OKCoin) GetRecentTrades(p currency.Pair, assetType asset.Item) ([]trade
sort.Sort(trade.ByDate(resp))
return resp, nil
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (o *OKCoin) CancelBatchOrders(orders []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}

View File

@@ -789,3 +789,8 @@ func (o *OKEX) GetRecentTrades(p currency.Pair, assetType asset.Item) ([]trade.D
sort.Sort(trade.ByDate(resp))
return resp, nil
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (o *OKEX) CancelBatchOrders(ord []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}

View File

@@ -174,6 +174,13 @@ type Cancel struct {
// cancel all orders on an exchange
type CancelAllResponse struct {
Status map[string]string
Count int64
}
// CancelBatchResponse returns the status of orders
// that have been requested for cancellation
type CancelBatchResponse struct {
Status map[string]string
}
// TradeHistory holds exchange history data

View File

@@ -554,6 +554,11 @@ func (p *Poloniex) CancelOrder(o *order.Cancel) error {
return p.CancelExistingOrder(orderIDInt)
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (p *Poloniex) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (p *Poloniex) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -424,6 +424,11 @@ func (y *Yobit) CancelOrder(o *order.Cancel) error {
return y.CancelExistingOrder(orderIDInt)
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (y *Yobit) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (y *Yobit) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{

View File

@@ -531,6 +531,11 @@ func (z *ZB) CancelOrder(o *order.Cancel) error {
return z.CancelExistingOrder(orderIDInt, fpair.String())
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (z *ZB) CancelBatchOrders(o []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}
// CancelAllOrders cancels all orders associated with a currency pair
func (z *ZB) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
cancelAllOrdersResponse := order.CancelAllResponse{