ccxt icon indicating copy to clipboard operation
ccxt copied to clipboard

[Kucoin] Unable to recreate watchOrderbook after initial sync problem

Open krychla1 opened this issue 2 years ago • 7 comments

Hi Fellows!

After initial error on watchOrderbook "kucoin failed to synchronize WebSocket feed with the snapshot for symbol COV/USDT in 3 attempts": (kucoin.js)

    retryFetchOrderBookSnapshot (client, message, subscription) {
        const symbol = this.safeString (subscription, 'symbol');
        const messageHash = this.safeString (subscription, 'messageHash');
        // console.log ('fetchOrderBookSnapshot', nonce, previousSequence, nonce >= previousSequence);
        const options = this.safeValue (this.options, 'fetchOrderBookSnapshot', {});
        const maxAttempts = this.safeInteger (options, 'maxAttempts', 3);
        let numAttempts = this.safeInteger (subscription, 'numAttempts', 0);
        // retry to syncrhonize if we haven't reached maxAttempts yet
        if (numAttempts < maxAttempts) {
            // safety guard
            if (messageHash in client.subscriptions) {
                numAttempts = this.sum (numAttempts, 1);
                subscription['numAttempts'] = numAttempts;
                client.subscriptions[messageHash] = subscription;
                this.spawn (this.fetchOrderBookSnapshot, client, message, subscription);
            }
        } else {
            if (messageHash in client.subscriptions) {
                subscription['fetchingOrderBookSnapshot'] = false;
                subscription['numAttempts'] = 0;
                client.subscriptions[messageHash] = subscription;
            }
            const e = new InvalidNonce (this.id + ' failed to synchronize WebSocket feed with the snapshot for symbol ' + symbol + ' in ' + maxAttempts.toString () + ' attempts');
            client.reject (e, messageHash);
        }
    }

there is no possibility to create watchOrderbook for the pair again, because the subscription exists, however is not handled. On new watchOrderbook, the if condition of following code is skipped to "return future", which never resolves (Exchange.js line 115-134)

connected.then (() => {
            if (!client.subscriptions[subscribeHash]) {
                client.subscriptions[subscribeHash] = subscription || true;
                const options = this.safeValue (this.options, 'ws');
                const cost = this.safeValue (options, 'cost', 1);
                if (message) {
                    if (this.enableRateLimit && client.throttle) {
                        // add cost here |
                        //               |
                        //               V
                        client.throttle (cost).then (() => {
                            client.send (message);
                        }).catch ((e) => { throw e });
                    } else {
                        client.send (message);
                    }
                }
            }
        })
        return future;

I face memory leaks on kucoin and I suspect dead subscriptions like these might be the cause. Please investigate. Thanks.

  • CCXT.PRO:1.2.6

krychla1 avatar Aug 02 '22 11:08 krychla1

Hi @krychla1, thanks for letting us know, we will investigate it

carlosmiei avatar Aug 02 '22 15:08 carlosmiei

Here's a little snippet for you (you might execute it repeatedly until failed sync occurs)

const exchangeName = 'kucoin';
const exchangeKroitor = new ccxtpro[exchangeName](params);
let orderbooksKroitor = {}

async function main() {
    await exchangeKroitor.loadMarkets();
    watchOrderBook(exchangeKroitor, 'COV/BTC')
    watchOrderBook(exchangeKroitor, 'COV/USDT')  

}

async function watchOrderBook(exchange, symbol) {
    if (!exchange.markets[symbol]) {
        return;
    }
    while (true) {
        try {
            const orderbook = await exchange.watchOrderBook(symbol)
            orderbooksKroitor[symbol] = orderbook
        } catch (e) {
            console.log(e.message)
        }
    }
}

krychla1 avatar Aug 02 '22 15:08 krychla1

This is kind of broader snippet, I've got around 10% of failed sync out of the symbol list every time:


const exchangeName = 'kucoin';
const exchangeKroitor = new ccxtpro[exchangeName](params);
let orderbooksKroitor = {}

async function main() {
 const symbols = ["BAX/ETH", "BAX/USDT", "SAND/BTC", "SAND/USDT", "COV/ETH", "COV/BTC", "COV/USDT", "SYLO/USDT", "BIFI/BTC", "ORBS/BTC", "YLD/BTC", "YLD/USDT", "DMT/ETH", "DMT/BTC", "INXT/BTC", "INXT/USDT", "REV/BTC", "REV/USDT", "AKRO/BTC", "JASMY/ETH", "JASMY/USDT", "SPI/BTC", "SPI/USDT", "BFC/BTC", "VEMP/USDT", "GAME/BTC", "GAME/USDT", "DAWN/BTC", "STPT/BTC", "MDT/BTC", "MDT/USDT", "SUKU/BTC", "SUKU/USDT", "SHR/BTC", "SHR/USDT", "UBT/ETH", "UBT/BTC", "DVI/BTC", "DVI/USDT", "PUNDIX/ETH", "PUNDIX/BTC", "PUNDIX/USDT", "XDB/BTC", "XDB/USDT", "GET/BTC", "GET/USDT", "SPC/BTC", "MYST/BTC", "MYST/USDT", "POLC/ETH", "POLC/USDT", "PYR/BTC", "PYR/USDT", "ANKR/ETH", "ANKR/BTC", "PMA/BTC", "PMA/USDT", "VEE/BTC", "TRAC/ETH", "TRAC/BTC", "TRAC/USDT", "VRA/ETH", "VRA/BTC", "VRA/USDT", "SRN/ETH", "SRN/BTC", "SXP/BTC", "RSV/USDT", "CND/BTC", "MFT/BTC", "MFT/USDT", "LRC/BTC", "UPP/BTC", "UQC/BTC", "UQC/USDT", "POWR/ETH", "POWR/BTC", "SOLVE/ETH", "SOLVE/BTC", "SOLVE/USDT", "MTL/BTC", "1INCH/ETH", "1INCH/BTC", "1INCH/USDT", "SUSHI/ETH", "SUSHI/BTC", "SUSHI/USDT", "REVV/BTC", "REVV/USDT", "CEL/ETH", "CEL/BTC", "CEL/USDT", "UTK/BTC", "USDP/BTC", "CVC/ETH", "CVC/BTC", "MET/ETH", "MET/BTC", "RSR/BTC", "RSR/USDT", "AMP/ETH", "AMP/BTC", "AMP/USDT", "RLC/BTC", "QNT/ETH", "QNT/BTC", "DRGN/BTC", "BOSON/BTC", "BOSON/USDT", "UMA/ETH", "UMA/BTC", "UMA/USDT", "UNI/ETH", "UNI/BTC", "UNI/USDT", "REN/ETH", "REN/BTC", "REN/USDT", "GNO/ETH", "GNO/BTC", "BAL/ETH", "BAL/BTC", "BAL/USDT", "DNT/BTC", "NMR/ETH", "NMR/BTC", "NMR/USDT", "NKN/BTC", "NKN/USDT", "GLM/ETH", "GLM/BTC", "DUSK/USDT", "RENBTC/ETH", "RENBTC/BTC", "RENBTC/USDT", "ANT/ETH", "ANT/BTC", "CRV/ETH", "CRV/BTC", "CRV/USDT", "RAMP/ETH", "RAMP/BTC", "RAMP/USDT", "COMP/ETH", "COMP/BTC", "COMP/USDT", "BAND/ETH", "BAND/BTC", "BAND/USDT", "SNT/ETH", "SNT/BTC", "TUSD/ETH", "TUSD/BTC", "TUSD/USDT", "OXT/BTC", "OXT/USDT", "USDC/ETH", "USDC/BTC", "USDC/USDT", "OGN/ETH", "OGN/BTC", "DEP/BTC", "DEP/USDT", "AAVE/ETH", "AAVE/BTC", "AAVE/USDT", "BNT/ETH", "BNT/BTC", "MATIC/ETH", "MATIC/BTC", "MATIC/USDT", "ENJ/ETH", "ENJ/BTC", "ENJ/USDT", "SNX/ETH", "SNX/BTC", "SNX/USDT", "MKR/ETH", "MKR/BTC", "MKR/USDT", "STORJ/BTC", "BAT/ETH", "BAT/BTC", "BAT/USDT", "MANA/ETH", "MANA/BTC", "LINK/ETH", "LINK/BTC", "LINK/USDT", "TYC/BTC", "TYC/USDT", "FOR/BTC", "ABYSS/BTC", "FLIXX/USDT", "INV/ETH", "CIV/USDT", "VID/BTC", "AGRS/BTC", "AGRS/USDT", "ENG/ETH", "ENG/BTC", "EDR/BTC", "STRK/BTC", "STRK/USDT", "UCT/BTC", "UCT/USDT", "ROOK/ETH", "ROOK/BTC", "ROOK/USDT", "CRTS/USDT", "BOA/BTC", "BOA/USDT", "PROM/BTC", "PROM/USDT", "SPWN/ETH", "SPWN/USDT", "DRC/ETH", "SKM/BTC", "SKM/USDT", "RFOX/BTC", "RFOX/USDT"]
    await exchangeKroitor.loadMarkets();
    const symbolsPromises = symbols.map(symbol => watchOrderBook(exchangeKroitor, symbol))
    await Promise.all([symbolsPromises.concat(tick())])

}

async function watchOrderBook(exchange, symbol) {
    if (!exchange.markets[symbol]) {
        return;
    }
    while (true) {
        try {
            const orderbook = await exchange.watchOrderBook(symbol)
            orderbooksKroitor[symbol] = orderbook
        } catch (e) {
            console.log(e.message)
        }
    }
}

async function tick() {
    try {
        const delay = 30000
        while (true) {
            const symbols = Object.keys(orderbooksKroitor)
            console.log(new Date(), exchangeKroitor.id, 'orderbooks', symbols.length, symbols)            
            await exchangeKroitor.sleep(delay)
        }
    } catch (e) {
        console.log(e);
    }
}

krychla1 avatar Aug 02 '22 15:08 krychla1

@krychla1 Thank you very much; as temporary mitigation, can you try to increase maxAttempts and delay to see if it helps?

    const exchange = new ccxtpro.kucoin ({
        'apiKey': '',
        'secret': ''
    })
exchange.options['fetchOrderBookSnapshot']['maxAttemps'] = NEW_VALUE // currently 3
exchange.options['fetchOrderBookSnapshot']['delay'] = NEW_VALUE // currently 1000

carlosmiei avatar Aug 02 '22 16:08 carlosmiei

@carlosmiei Thanks for your suggestions. Delay has no impact (even with extremes like 10 000), however maxAttempts (you've got a typo there) does help. It is strange as the nonces returned from fetchOrderbook are quite random, jumping from low to high to low.

//kucoin.js line 400 
 if (nonce < previousSequence) {
                console.log(nonce, '<', previousSequence, 'retryFetch')
                this.retryFetchOrderBookSnapshot (client, message, subscription);
            } else {
                console.log(nonce, '>', previousSequence, 'ok')
...
2889983 < 2890062 retryFetch
2889912 < 2890062 retryFetch
2889873 < 2890062 retryFetch
2890027 < 2890062 retryFetch
2890013 < 2890062 retryFetch
2890066 > 2890062 ok

Often it takes more than 5 tries to match previousSequence.

Nice workaround, however this issue needs to be addressed - once a subscription gets lost, it can't be resubscribed. And to reinitialize the whole kucoin client is not an option either, as with many subscriptions there are always a few problematic ones. Thank you.

krychla1 avatar Aug 02 '22 20:08 krychla1

Hi as the ccxt pro will be free very soon, Im working on a project which need socket conndction and ig is wasteful to buy it only fof few weeks. I will be thankful if @krychla1 send me a download able link or package to my email: [email protected]

myehrajat avatar Aug 08 '22 22:08 myehrajat

@myehrajat Hi. Nothing is wasteful, you would support a very professional project that will save your time and make money if you're clever enough. Don't try to find ways to cheat it, be thankful you can enjoy it for lifetime paying just for a few months.

krychla1 avatar Aug 09 '22 08:08 krychla1