20

I am trying to connect to wss://api.poloniex.com and subscribe to ticker. I can't find any working example in python. I have tried to use autobahn/twisted and websocket-client 0.32.0.

The purpose of this is to get real time ticker data and store it in a mysql database.

So far I have tried to use examples provided in library documentation. They work for localhost or the test server but if I change to wss://api.poloniex.com I get a bunch of errors.

here is my attempt using websocket-client 0.32.0:

from websocket import create_connection
ws = create_connection("wss://api.poloniex.com")
ws.send("ticker")
result = ws.recv()
print "Received '%s'" % result
ws.close()

and this is using autobahn/twisted:

from autobahn.twisted.websocket import WebSocketClientProtocol
from autobahn.twisted.websocket import WebSocketClientFactory


class MyClientProtocol(WebSocketClientProtocol):

    def onConnect(self, response):
        print("Server connected: {0}".format(response.peer))

    def onOpen(self):
        print("WebSocket connection open.")

        def hello():
            self.sendMessage(u"ticker".encode('utf8'))
            self.sendMessage(b"\x00\x01\x03\x04", isBinary=True)
            self.factory.reactor.callLater(1, hello)

        # start sending messages every second ..
        hello()

    def onMessage(self, payload, isBinary):
        if isBinary:
            print("Binary message received: {0} bytes".format(len(payload)))
        else:
            print("Text message received: {0}".format(payload.decode('utf8')))

    def onClose(self, wasClean, code, reason):
        print("WebSocket connection closed: {0}".format(reason))


if __name__ == '__main__':

    import sys

    from twisted.python import log
    from twisted.internet import reactor

    log.startLogging(sys.stdout)

    factory = WebSocketClientFactory("wss://api.poloniex.com", debug=False)
    factory.protocol = MyClientProtocol

    reactor.connectTCP("wss://api.poloniex.com", 9000, factory)
    reactor.run()

A complete but simple example showing how to connect and subscribe to to a websocket push api using any python library would be greatly appreciated.

Ion Scerbatiuc
  • 1,151
  • 6
  • 10
Matei Razvan
  • 205
  • 1
  • 2
  • 4

4 Answers4

28

This uses the undocumented websocket endpoint because Poloniex has pulled support for the original WAMP socket endpoint.

import websocket
import thread
import time
import json

def on_message(ws, message):
    print(message)

def on_error(ws, error):
    print(error)

def on_close(ws):
    print("### closed ###")

def on_open(ws):
    print("ONOPEN")
    def run(*args):
        ws.send(json.dumps({'command':'subscribe','channel':1001}))
        ws.send(json.dumps({'command':'subscribe','channel':1002}))
        ws.send(json.dumps({'command':'subscribe','channel':1003}))
        ws.send(json.dumps({'command':'subscribe','channel':'BTC_XMR'}))
        while True:
            time.sleep(1)
        ws.close()
        print("thread terminating...")
    thread.start_new_thread(run, ())


if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://api2.poloniex.com/",
                              on_message = on_message,
                              on_error = on_error,
                              on_close = on_close)
    ws.on_open = on_open
    ws.run_forever()

The channels are:

1001 = trollbox (you will get nothing but a heartbeat)
1002 = ticker
1003 = base coin 24h volume stats
1010 = heartbeat
'MARKET_PAIR' = market order books
Ricky Han
  • 1,309
  • 1
  • 15
  • 25
  • I'm sure the previous answer by @Ion Scerbatiuc works perfectly well, but I'm using Python 2.7.x, so this answer was a better option for me. The answer by Ion is based on Python 3.5 and above. – MikeyE Jul 18 '17 at 09:42
  • Correction: After re-reading the comments on Ion's answer, it looks like his solution is based on Python 3.3. – MikeyE Jul 18 '17 at 10:15
  • Glad I could be of help – Ricky Han Jul 18 '17 at 19:01
  • Hi Ricky, first of all great solution, I finally managed to get live push data. However, I still have some issues: I don't manage to understand what's the first number of the output of 1002 (ticker). In the documentation it says that it should be a string with the currency pair, however I get an integer. Is it an id for the currency pair? How can I know the map id -> pair? Do you maybe also have a documentation explaining what corresponds to what? How did you know that 1002 was ticker, 1003 volume stats, etc? Unfortunately I find the poloniex documentation a bit outdated and not very useful. – riccio777 Aug 02 '17 at 19:37
  • Hi Ricky thanks for replying, but I didn't really understand what you just said. Can you be a bit more specific? – riccio777 Aug 03 '17 at 16:14
  • The following is an example of what I get with your script: [1002,null,[121,"2759.99999999","2759.99999999","2758.00000000","0.02184376","12268375.01419869","4495.18724321",0,"2767.80020000","2680.10000000"]] However, I don't really know what 121 represents (also null, actually). – riccio777 Aug 03 '17 at 16:26
  • I'm having the same results as @riccio777 . Not sure how to interpret this, or if this is correct behavior – scubabuddha Aug 06 '17 at 22:50
  • 6
    @riccio777 I just started using this code for orderbook (Currency Pair) data. The message output is something like this: [148,394056638,[["o",0,"0.07615527","0.34317849"]]] 148: appears to be the ID of the Currency Pair. 394056638: Sequence number (I presume of the last sequence value prior to the full orderbook as presented) "o" + 0: orderBookRemove "o" + 1: orderBookModify "0.07615527": Price "0.34317849": Quantity – Gabe Spradlin Aug 23 '17 at 19:04
  • 4
    in addition to what @GabeSpradlin mentioned you have: `["t","9394200",1,"5545.00000000","0.00009541",1508060546]` which is a Trade entry (t) and is defined as `[trade, tradeId, 0/1 (sell/buy), price, amount, timestamp]` – Nasir Oct 15 '17 at 09:47
  • @Nasir Which channel did you subscribe to for the trade data? – mchangun Oct 29 '17 at 08:09
  • Just to any of the pairs if you subscribe to, it will give you order book but also trades which start with "t" – Nasir Oct 29 '17 at 13:29
  • 5
    A slight correction to @GabeSpradlin 's appreciated specification: I beleive they have done away with orderBookRemove in this iteration (evidence: "o"+1 messages specify a volume and it is not always 0). So for "o" messages 0 is asks and 1 is bids being modified (evidence: deducing from empirical data, also 0/1 match the indexes of the asks/bids in the order book snapshot provided from these channels). – ess Nov 21 '17 at 01:14
  • 2
    And another addition: `["i",{"currencyPair":"BTC_XMR","orderBook":[{"0.02427000":"18.91841222","0.02431500":"0.16475174", ...}, {...}]}]`. This seems to be an `inital` message which is retrieved at the very start. All other datas are just updates. As you can see it shows which pair is send and the whole orderbook for the pair (array with 2 objects: bid/ask) – Fuzzyma Dec 31 '17 at 16:37
  • @ess You are correct about "o" + 0 being asks and "o" + 1 being bids. I wrote the original. Figured it out later and forgot to come back and correct it. – Gabe Spradlin Mar 19 '18 at 21:24
  • Another 2 notes regarding Poloniex, 1) I saw the API (not WebSocket) for Poloniex change multiple times btwn Aug and Dec 2017. These are undocumented and, AFAIK, unannounced changes. 2) Latency on the WebSocket climbed dramatically back in Dec. Nothing on the Orderbook WS gives you a server time directly but you can get a time from the trade updates. The latency on those trade updates climbed from approx 0.5 sec in Aug to 3 sec in Dec. What caused me to look at the latency data after my initial assessment at 0.5 sec was that many more trades were failing. (I was using Immediate or Cancel.) – Gabe Spradlin Mar 19 '18 at 21:27
  • Upvoted! one question if you dont mind answering, this seems to work really well for say a single exchange like Poloniex, how would you handle say 10 exchanges? clustering? multiprocessing? – PirateApp Sep 04 '18 at 15:31
13

What you are trying to accomplish can be done by using WAMP, specifically by using the WAMP modules of the autobahn library (that you are already trying to use).

After following their docs, I managed to set-up a simple example using autobahn and asyncio. The following example subscribes to the 'ticker' feed and prints the received values:

from autobahn.asyncio.wamp import ApplicationSession
from autobahn.asyncio.wamp import ApplicationRunner
from asyncio import coroutine


class PoloniexComponent(ApplicationSession):
    def onConnect(self):
        self.join(self.config.realm)

    @coroutine
    def onJoin(self, details):
        def onTicker(*args):
            print("Ticker event received:", args)

        try:
            yield from self.subscribe(onTicker, 'ticker')
        except Exception as e:
            print("Could not subscribe to topic:", e)


def main():
    runner = ApplicationRunner("wss://api.poloniex.com:443", "realm1")
    runner.run(PoloniexComponent)


if __name__ == "__main__":
    main()

You can find more details about WAMP programming with autobahn here: http://autobahn.ws/python/wamp/programming.html

Ion Scerbatiuc
  • 1,151
  • 6
  • 10
  • yield from self.subscribe(onTicker, 'ticker') SyntaxError: invalid syntax – A. STEFANI Mar 24 '16 at 11:15
  • 4
    What Python version are you on? This example is for Python 3.3+, as it uses `asyncio` and the `yield from` construct to delegate the execution to a different coroutine. You can find more details here: https://docs.python.org/3/library/asyncio-task.html – Ion Scerbatiuc Mar 24 '16 at 15:51
  • Since the runner creates and handles the instance of PoloniexComponent we don't get much access to it. Do you know a way to add a callback to it so another object can receive updates? I'd prefer a method that doesn't use some kind of global notification dispatch. – null0pointer Oct 11 '16 at 03:00
  • I figured it out. You can pass an object via the `extra` dictionary of the ApplicationRunner and then access it by `self.config.extra` within the PoloniexComponent. – null0pointer Oct 11 '16 at 23:23
  • Unfortunately `asyncio` doesn't get everytime continuous trollbox messages. Sometimes I get continuous message numbers, but sometimes there are like 10 or 20 or 30 messages missing before the next message with the next number appears. Is there a reason for this behavior? – sunwarr10r Feb 24 '17 at 11:27
  • 6
    This method is outdated. I don't think Poloniex is currently supporting this endpoint. See my answer below. – Ricky Han Jul 17 '17 at 05:53
  • @IonScerbatiuc Hi! I tried to reproduce your code in Python 3.6 and Twisted 19. I can connect to Poloniex can subscribe to ticker channel, but Polo returns nothing! What I doing wrong? – Alex Feb 01 '18 at 23:04
9

Meanwhile poloniex changed the ticker API, so now it returns a symbol-id instead of a name, so maybe someone will find this useful:

7: BTC_BCN
8: BTC_BELA
10: BTC_BLK
12: BTC_BTCD
13: BTC_BTM
14: BTC_BTS
15: BTC_BURST
20: BTC_CLAM
24: BTC_DASH
25: BTC_DGB
27: BTC_DOGE
28: BTC_EMC2
31: BTC_FLDC
32: BTC_FLO
38: BTC_GAME
40: BTC_GRC
43: BTC_HUC
50: BTC_LTC
51: BTC_MAID
58: BTC_OMNI
61: BTC_NAV
63: BTC_NEOS
64: BTC_NMC
69: BTC_NXT
73: BTC_PINK
74: BTC_POT
75: BTC_PPC
83: BTC_RIC
89: BTC_STR
92: BTC_SYS
97: BTC_VIA
98: BTC_XVC
99: BTC_VRC
100: BTC_VTC
104: BTC_XBC
108: BTC_XCP
112: BTC_XEM
114: BTC_XMR
116: BTC_XPM
117: BTC_XRP
121: USDT_BTC
122: USDT_DASH
123: USDT_LTC
124: USDT_NXT
125: USDT_STR
126: USDT_XMR
127: USDT_XRP
129: XMR_BCN
130: XMR_BLK
131: XMR_BTCD
132: XMR_DASH
137: XMR_LTC
138: XMR_MAID
140: XMR_NXT
148: BTC_ETH
149: USDT_ETH
150: BTC_SC
151: BTC_BCY
153: BTC_EXP
155: BTC_FCT
158: BTC_RADS
160: BTC_AMP
162: BTC_DCR
163: BTC_LSK
166: ETH_LSK
167: BTC_LBC
168: BTC_STEEM
169: ETH_STEEM
170: BTC_SBD
171: BTC_ETC
172: ETH_ETC
173: USDT_ETC
174: BTC_REP
175: USDT_REP
176: ETH_REP
177: BTC_ARDR
178: BTC_ZEC
179: ETH_ZEC
180: USDT_ZEC
181: XMR_ZEC
182: BTC_STRAT
183: BTC_NXC
184: BTC_PASC
185: BTC_GNT
186: ETH_GNT
187: BTC_GNO
188: ETH_GNO
189: BTC_BCH
190: ETH_BCH
191: USDT_BCH
192: BTC_ZRX
193: ETH_ZRX
194: BTC_CVC
195: ETH_CVC
196: BTC_OMG
197: ETH_OMG
198: BTC_GAS
199: ETH_GAS
200: BTC_STORJ
leberknecht
  • 1,526
  • 15
  • 27
4

Currently, Twisted doesn't properly use the Windows trust store. So the verification of the TLS certificate will fail. To work around this until either Twisted or Autobahn includes a workaround, you can:

  • install certifi module (pip install certifi)
  • set SSL_CERT_FILE, like export SSL_CERT_FILE="$(python -m certifi)"
meejah
  • 316
  • 1
  • 5
  • Thanks meejah. That helped under Linux too. – Andz Apr 22 '17 at 14:48
  • Twisted *should* be using the global trust store on Linux; perhaps you're on a minimal Linux install that doesn't include them for some reason? – meejah Jun 16 '17 at 03:56