ccxt / ccxt

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading API with support for more than 100 bitcoin/altcoin exchanges
https://docs.ccxt.com
MIT License
32.44k stars 7.46k forks source link

enhancement: "success"parameter #1297

Closed wannesdemaeght closed 6 years ago

wannesdemaeght commented 6 years ago

ATTENTION!!!

MUST READ THIS BEFORE SUBMITTING ISSUES:

https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-submit-an-issue

Looking at these statements from the manual, depicting the response to a new order and a withdrawal:

A successful call to a unified method for placing market or limit orders returns the following structure:

{ 'id': 'string', // order id 'info': { ... }, // decoded original JSON response from the exchange as is }

The withdraw method returns a dictionary containing the withdrawal id, which is usually the txid of the onchain transaction itself, or an internal withdrawal request id registered within the exchange. The returned value looks as follows:

{ 'info' { ... }, // unparsed reply from the exchange, as is 'id': '12345567890', // string withdrawal id, if any }

I was wondering if it might be possible to pass a simple "success"parameter, along with the id. Some exchanges do this already (Bittrex and Kucoin for instance), and that makes for an easy check. However, not all exchanges do this, but maybe that is something that could be implemented from ccxt?

For example: a withdrawal from kucoin gives a "id: undefined" as response even when ok, which is hard to program in as "it probably went through".

My proposal would add, alongside the id (which may or may not be available), a boolean parameter "success", stating true or false.

Now for Kucoin, I can check the .info to get the original response, but, at least for me, having a success parameter on every exchange would be preferable.

kroitor commented 6 years ago

Hi!

Thx for your suggestion! )

I have a few questions to you, however:

Standing by for your reply )

wannesdemaeght commented 6 years ago

What behaviour would you expect in case of failure?

success: false, with perhaps a short reason why. This would be for things that wouldn't result in an error.

errr...

come to think of it, I can see where you're going with this one.

Whenever the promise is resolved, we can asume that everything went ok, so there's no need to state it again with a success flag.

All the stuff I can think of (balance too low, order not going through, ...) would come up in the catch block.

wannesdemaeght commented 6 years ago

Still stuck in the callback era I guess :-)

kroitor commented 6 years ago

@wannesdemaeght so, from here you should just catch them like described in the manual:

Otherwise, if there was no exception, it's a success, isn't it? ) You're thinking in the right direction. Would you mind if I close this now? Or do you still have questions?

wannesdemaeght commented 6 years ago

No man, you rock. Thanks again for the fast reply, and making me smarter. Great support!

wannesdemaeght commented 6 years ago

Not that a scuccess parameter would have made a difference, but almost the entire afternoon kucoin responded to to fetchTickers with this EXACT response: (nothing ever changed, not even the timestamp)

{ symbol: 'LTC/BTC', timestamp: 1516109871000, datetime: '2018-01-16T13:37:51.000Z', high: 0.0174355, low: 0.01630001, bid: 0.01676137, ask: 0.01687575, vwap: undefined, open: undefined, close: undefined, first: undefined, last: 0.01676137, change: undefined, percentage: undefined, average: undefined, baseVolume: 23755.297628, quoteVolume: 401.3065585, info: { coinType: 'LTC', trading: true, symbol: 'LTC-BTC', lastDealPrice: 0.01676137, buy: 0.01676137, sell: 0.01687575, change: -0.0001612, coinTypePair: 'BTC', sort: 10, feeRate: 0.001, volValue: 401.3065585, high: 0.0174355, datetime: 1516109871000, vol: 23755.297628, low: 0.01630001, changeRate: -0.0095 } }

This lasted a good 4 hours (even tested with another library, same response, so definitely on their end) Let's say a lot of orders were posted by my bot based on that "ask"price...

I now programmed it to also check the timestamp, just to be sure.

So a resolve is not ALWAYS correct.

kroitor commented 6 years ago

@wannesdemaeght the same symptoms are common to other exchanges as well, now you have a much better understanding how all of this is functioning )

wannesdemaeght commented 6 years ago

one caveat though, maybe you have an idea...

I poll the tickers every 2500 ms. I run a test on the timetamp every 5000ms, the test being that the current timestamp must be greater than the one recorded 5000 ms ago.

This still results in a lot of falses from that test. Kucoin responds evry 2500 ms (or my catch block would fire), but it seems they only refresh their answer every 10 000 ms.

Any experience with this?

kroitor commented 6 years ago

Any experience with this?

Yeah, exchanges will in general cache all possible info by all means. So you can in fact get a 10-sec snapshot from their cache, not the actual price. I know this can be frustrating, but it's just how it is. If you want the most up-to-date info, you will have to calculate your stats from trades and orderbooks. Those endpoints are as close to "realtime" as it can get over REST.

wannesdemaeght commented 6 years ago

Buh, even more confusing: I logged their response timedate and timestamp every 2 seconds:

2018-01-16T20:22:32.000Z 1516134152000 2018-01-16T20:22:34.000Z 1516134154000 2018-01-16T20:22:26.000Z 1516134146000 2018-01-16T20:22:38.000Z 1516134158000 2018-01-16T20:22:36.000Z 1516134156000 2018-01-16T20:22:32.000Z 1516134152000 2018-01-16T20:22:38.000Z 1516134158000 2018-01-16T20:22:50.000Z 1516134170000 2018-01-16T20:22:46.000Z 1516134166000 2018-01-16T20:22:50.000Z 1516134170000 2018-01-16T20:22:50.000Z 1516134170000

They even go backwards? WTH?

kroitor commented 6 years ago

@wannesdemaeght depends on how you do that. Not seeing your code it's hard to tell )

wannesdemaeght commented 6 years ago
setInterval(function(){
    Kucoin.fetchTickers().then((result) => {console.log(result["LTC/BTC"].datetime, result["LTC/BTC"].timestamp)}).catch((err) => {console.log(err)})
    }, 2000)
kroitor commented 6 years ago

@wannesdemaeght well, first, you're not doing it properly, and sometimes, because of the nature of HTTP (it does not guarantee the order of separate requests) – you will get the responses out of order due to temporary quirks and lags in the networking itself. So when you receive a reply for a previous request after the next one is already received, you observe "weird ordering".

To do that properly:

'use strict';

const ccxt = require ('ccxt')

const exchange = new ccxt.kucoin ({
    enableRateLimit: true,
})

const symbol = 'LTC/BTC'

;(async () => {
    while (true) {
        console.log ('----------------------------------------------------')
        const sent = Date.now ()
        console.log ('Sent: ', exchange.iso8601 (sent))
        const tickers = await exchange.fetchTickers ()
        const received = Date.now ()
        console.log ('Received: ', exchange.iso8601 (received))
        console.log (symbol, 'ticker datetime:', tickers[symbol].datetime)
    } 
}) ()

However, when I launched that code against kucoin, I got this result:

----------------------------------------------------
Sent:  2018-01-16T20:41:47.141Z
Received:  2018-01-16T20:41:48.306Z
LTC/BTC ticker datetime: 2018-01-16T20:41:44.000Z
----------------------------------------------------
Sent:  2018-01-16T20:41:48.306Z
Received:  2018-01-16T20:41:50.166Z
LTC/BTC ticker datetime: 2018-01-16T20:41:44.000Z
----------------------------------------------------
Sent:  2018-01-16T20:41:50.167Z
Received:  2018-01-16T20:41:52.186Z
LTC/BTC ticker datetime: 2018-01-16T20:41:46.000Z
----------------------------------------------------
Sent:  2018-01-16T20:41:52.187Z
Received:  2018-01-16T20:41:54.147Z
LTC/BTC ticker datetime: 2018-01-16T20:41:52.000Z
----------------------------------------------------
Sent:  2018-01-16T20:41:54.147Z
Received:  2018-01-16T20:41:56.198Z
LTC/BTC ticker datetime: 2018-01-16T20:41:52.000Z
----------------------------------------------------
Sent:  2018-01-16T20:41:56.198Z
Received:  2018-01-16T20:41:58.067Z
LTC/BTC ticker datetime: 2018-01-16T20:41:46.000Z
----------------------------------------------------
Sent:  2018-01-16T20:41:58.068Z
Received:  2018-01-16T20:42:00.174Z
LTC/BTC ticker datetime: 2018-01-16T20:41:44.000Z
----------------------------------------------------
Sent:  2018-01-16T20:42:00.174Z
Received:  2018-01-16T20:42:02.245Z
LTC/BTC ticker datetime: 2018-01-16T20:41:56.000Z
----------------------------------------------------
Sent:  2018-01-16T20:42:02.245Z
Received:  2018-01-16T20:42:04.266Z
LTC/BTC ticker datetime: 2018-01-16T20:41:58.000Z
----------------------------------------------------
Sent:  2018-01-16T20:42:04.266Z
Received:  2018-01-16T20:42:06.266Z
LTC/BTC ticker datetime: 2018-01-16T20:41:52.000Z
----------------------------------------------------

So, apparently, even when you wait for an answer before sending the next request – it will still mess up the tickers datetime. Multiple repeating datetimes are ok if there were no trades – the reported date is the date of the last trade, obviously. But they can't go backwards, really.

wannesdemaeght commented 6 years ago

so basically, their API is shit :-)

kroitor commented 6 years ago

@wannesdemaeght i'd say not ideal )) You don't want to see real shit-APIs, trust me )

wannesdemaeght commented 6 years ago

Question is, if the timestamp in their response can not be trusted, can the ticker? And if the answer to that one is also a "no", it kinda qualifies as a shit-API :-)

kroitor commented 6 years ago

@wannesdemaeght

And if the answer to that one is also a "no", it kinda qualifies as a shit-API :-)

Well, that depends on how you look at it. Most traders will use order books and trade history as their primary sources of volume and price info. A ticker is usually a reduced (recalculated) piece of statistics, so it isn't and shouldn't be trusted too much. It is mostly for an "overview", and then you have to dig into orderbooks and trade history anyway. A successful trade just from the ticker alone is hardly sustainable. Yet still your use case may be different, so it's up to you.

wannesdemaeght commented 6 years ago

hmmm, food for thought. Right now I just go on the tickers, and a certain amount of "bid extra".

If the arbitrage would be succesful, including the bid extra, and the trading fees, I trade. Maybe the orderbooks would be a better way to go, but you have to fetch those one by one, which costs API-calls. If you want to monitor multiple pairs, you get banned rather quickly (Binance)

kroitor commented 6 years ago

Yeah, it's always a tradeoff.

wannesdemaeght commented 6 years ago

anyway, thanks for your time man. If there are any subscription-based websockets or whatever, count me in :-)