Accessing incoming messages with a Python websocket client
Question:
I am trying to receive messages via websocket-client module and be able to use received messages for other purposes (e.g. execute buy/sell orders based on incoming messages).
Here is what I have so far:
import websocket
import time
import json
def on_message(ws, message):
try:
current_price = json.loads(message)
print(current_price["price"]) # data type is dict.. only showing values for the key 'price'
except:
print("Please wait..")
time.sleep(1)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
sub_params = {'type': 'subscribe', 'product_ids': ['BTC-USD'], 'channels': ['ticker']}
ws.send(json.dumps(sub_params))
if __name__ == "__main__":
websocket.enableTrace(False)
ws = websocket.WebSocketApp("wss://ws-feed.pro.coinbase.com/",
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.run_forever()
Running this code will print the current Bitcoin price (current_price
) as they come in through its websocket feed.
What I want to do next is to be able to access that variable current_price
outside of the websocket function, and I am having a difficulty here. Writing anything beyond ws.run_forever()
will be ignored because the websocket event loop will never end.
So I tried running the websocket on a separate thread with ‘threading’ mordule:
import websocket
import json
import threading
current_price = 0
def on_message(ws, message):
global current_price
current_price = message
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
sub_params = {'type': 'subscribe', 'product_ids': ['BTC-USD'], 'channels': ['ticker']}
ws.send(json.dumps(sub_params))
if __name__ == "__main__":
websocket.enableTrace(False)
ws = websocket.WebSocketApp("wss://ws-feed.pro.coinbase.com/",
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws_thread = threading.Thread(target = ws.run_forever)
ws_thread.start()
print(current_price)
and this returns 0
. What can I do to make this work?
Answers:
Not sure if this is the most appropriate answer, but found a way to make this work.
import queue
.
.
.
.
def on_message(ws, message):
current_price = message
q.put(current_price)
.
.
.
ws_thread.start()
while True:
print(q.get())
I’ve worked it out. The key to is is to use functools partials to wrap the callback functions. I’ve built a simple multithreaded demonstrator:
from websocket import WebSocketApp
import time
from threading import Thread
from queue import Queue
from functools import partial
class websocket_client:
def __init__(self):
self.responseQ = Queue() #our message response Q
#add the response Qs on by wrapping the callbacks in functools partials
self.websocket = WebSocketApp("ws://echo.websocket.events",
on_open = partial(self.on_open, responseQ=self.responseQ),
on_message = partial(self.on_message, responseQ=self.responseQ),
on_error = partial(self.on_error, responseQ=self.responseQ),
on_close = partial(self.on_close, responseQ=self.responseQ))
# start run_forever as thread. request keep alive pings every 10 seconds with 5 second timouts
self.controller = Thread(target=self.websocket.run_forever, args=(None, None, 10, 5))
self.controller.start()
def on_open(self, websocket, responseQ):
#print("Opening Connection (run_forever)")
responseQ.put("Connected") #transmit status information
def on_error(self, websocket, error, responseQ):
#print("Connection Error (run_forever):", error)
responseQ.put(error) # or error messages
def on_message(self, websocket, message, responseQ):
#print("Message Received (run_forever):", message)
responseQ.put(message)
def on_close(self, websocket, status, message, responseQ):
#print("Closing Connection (run_forever):", status, message)
responseQ.put({'status': message, 'message': message}) #send close status and message in a dictionary
if __name__ == '__main__':
client = websocket_client() # make the client
while client.responseQ.get() != "Connected": time.sleep(0.1) #block until connected
print("nSERVER GREETING:", client.responseQ.get()) #greeting from server
while True:
message_to_send = time.strftime("%b %d %Y %H:%M:%S")
client.websocket.send(message_to_send)
message_received = client.responseQ.get()
print("Main Thread: SENT --> '%s' RECEIVED --> '%s'" %(message_to_send, message_received))
time.sleep(1)
I am trying to receive messages via websocket-client module and be able to use received messages for other purposes (e.g. execute buy/sell orders based on incoming messages).
Here is what I have so far:
import websocket
import time
import json
def on_message(ws, message):
try:
current_price = json.loads(message)
print(current_price["price"]) # data type is dict.. only showing values for the key 'price'
except:
print("Please wait..")
time.sleep(1)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
sub_params = {'type': 'subscribe', 'product_ids': ['BTC-USD'], 'channels': ['ticker']}
ws.send(json.dumps(sub_params))
if __name__ == "__main__":
websocket.enableTrace(False)
ws = websocket.WebSocketApp("wss://ws-feed.pro.coinbase.com/",
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.run_forever()
Running this code will print the current Bitcoin price (current_price
) as they come in through its websocket feed.
What I want to do next is to be able to access that variable current_price
outside of the websocket function, and I am having a difficulty here. Writing anything beyond ws.run_forever()
will be ignored because the websocket event loop will never end.
So I tried running the websocket on a separate thread with ‘threading’ mordule:
import websocket
import json
import threading
current_price = 0
def on_message(ws, message):
global current_price
current_price = message
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
sub_params = {'type': 'subscribe', 'product_ids': ['BTC-USD'], 'channels': ['ticker']}
ws.send(json.dumps(sub_params))
if __name__ == "__main__":
websocket.enableTrace(False)
ws = websocket.WebSocketApp("wss://ws-feed.pro.coinbase.com/",
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws_thread = threading.Thread(target = ws.run_forever)
ws_thread.start()
print(current_price)
and this returns 0
. What can I do to make this work?
Not sure if this is the most appropriate answer, but found a way to make this work.
import queue
.
.
.
.
def on_message(ws, message):
current_price = message
q.put(current_price)
.
.
.
ws_thread.start()
while True:
print(q.get())
I’ve worked it out. The key to is is to use functools partials to wrap the callback functions. I’ve built a simple multithreaded demonstrator:
from websocket import WebSocketApp
import time
from threading import Thread
from queue import Queue
from functools import partial
class websocket_client:
def __init__(self):
self.responseQ = Queue() #our message response Q
#add the response Qs on by wrapping the callbacks in functools partials
self.websocket = WebSocketApp("ws://echo.websocket.events",
on_open = partial(self.on_open, responseQ=self.responseQ),
on_message = partial(self.on_message, responseQ=self.responseQ),
on_error = partial(self.on_error, responseQ=self.responseQ),
on_close = partial(self.on_close, responseQ=self.responseQ))
# start run_forever as thread. request keep alive pings every 10 seconds with 5 second timouts
self.controller = Thread(target=self.websocket.run_forever, args=(None, None, 10, 5))
self.controller.start()
def on_open(self, websocket, responseQ):
#print("Opening Connection (run_forever)")
responseQ.put("Connected") #transmit status information
def on_error(self, websocket, error, responseQ):
#print("Connection Error (run_forever):", error)
responseQ.put(error) # or error messages
def on_message(self, websocket, message, responseQ):
#print("Message Received (run_forever):", message)
responseQ.put(message)
def on_close(self, websocket, status, message, responseQ):
#print("Closing Connection (run_forever):", status, message)
responseQ.put({'status': message, 'message': message}) #send close status and message in a dictionary
if __name__ == '__main__':
client = websocket_client() # make the client
while client.responseQ.get() != "Connected": time.sleep(0.1) #block until connected
print("nSERVER GREETING:", client.responseQ.get()) #greeting from server
while True:
message_to_send = time.strftime("%b %d %Y %H:%M:%S")
client.websocket.send(message_to_send)
message_received = client.responseQ.get()
print("Main Thread: SENT --> '%s' RECEIVED --> '%s'" %(message_to_send, message_received))
time.sleep(1)