I want to stream real-time data of the order book of BTC/USD using a WebSocket connection to the FTX Exchange. After the first snapshot of the order book, the WebSocket returns updates that I apply to my local reconstructed order book. To ensure that my order book is synchronized, I have to check a crc32 integer after every update. If this number matches the checksum in the message, I can be confident that my order book is well synchronized. However, sometimes the checksum is not right, and I need to reset the connection, i.e. unsubscribe to the channel and subscribe right after.
At the end of the on_messagefunction, I check if the checksum is successful. If it is not, I close the connection but I would like to clear the memory (i.e. the global objects asks, bids, checksum) and reconnect right after. How can I do it?
Should I just add a while True loop at the end? Like this one?
while True:
ws.run_forever()
I don't like this while Truesolution because it's impossible for me to stop it, I have to quit the terminal.
My code is the following
import websocket,json
import zlib
from decimal import Decimal
import binascii
from itertools import chain, zip_longest
from typing import Iterable, Sequence
asks = {}
bids = {}
checksum = {'checksum':0}
def format_e(dec):
return ('{:.' + str(len(dec.as_tuple().digits) - 1) + 'e}').format(dec)
def check_sum(
asks: Iterable[Sequence[float]], bids: Iterable[Sequence[float]]
) -> int:
asks=[[level[0],level[1]]for level in asks.items()]
bids=[[level[0],level[1]]for level in bids.items()]
order_book_hash_iterator = zip_longest(bids, asks, fillvalue=tuple())
check_string = ":".join(
(
str(token)
for ask_level, bid_level in order_book_hash_iterator
for token in chain(ask_level, bid_level)
)
)
return binascii.crc32(check_string.encode("ascii"))
def on_open(ws):
print('Opened connection')
asks.clear()
bids.clear()
subscribe_message = {'op': 'subscribe', 'channel': 'orderbook','market':'BTC/USD'}
ws.send(json.dumps(subscribe_message))
def on_message(ws,message):
js=json.loads(message)
if js['type'] == 'partial':
print('Get Snapshot')
for level in js['data']['asks']:
asks[level[0]]=level[1]
for level in js['data']['bids']:
bids[level[0]]=level[1]
checksum['checksum']=js['data']['checksum']
if js['type'] == 'update':
for level in js['data']['asks']:
if level[1]==0:
asks.pop(level[0])
else:
asks[level[0]]=level[1]
for level in js['data']['bids']:
if level[1]==0:
bids.pop(level[0])
else:
bids[level[0]]=level[1]
if check_sum(asks,bids) != js['data']['checksum']:
print('Error')
ws.close()
socket = "wss://ftx.com/ws/"
ws = websocket.WebSocketApp(socket,on_open=on_open)
ws.on_message = lambda ws,msg: on_message(ws,msg)
ws.run_forever()
How about this:
# everything up to and including `on_open` the same
class ChecksumException(Exception):
pass
def on_message(ws, message):
print(message)
js = json.loads(message)
if js['type'] == 'partial':
print('Get Snapshot')
for level in js['data']['asks']:
asks[level[0]] = level[1]
for level in js['data']['bids']:
bids[level[0]] = level[1]
checksum['checksum'] = js['data']['checksum']
if js['type'] == 'update':
for level in js['data']['asks']:
if level[1] == 0:
asks.pop(level[0])
else:
asks[level[0]] = level[1]
for level in js['data']['bids']:
if level[1] == 0:
bids.pop(level[0])
else:
bids[level[0]] = level[1]
if js['type'] == 'subscribed':
return
# so, checking this for *any* type of message, except 'subscribed'
if check_sum(asks, bids) != js['data']['checksum']:
raise ChecksumException
def main():
socket = "wss://ftx.com/ws/"
while True:
ws = None
try:
ws = websocket.WebSocketApp(socket, on_open=on_open)
ws.on_message = lambda w=ws, msg=None: on_message(w, msg)
print('Connecting...')
ws.run_forever()
print('Keyboard interrupt, stopping')
break
except ChecksumException:
ws.close()
print('Checksum error, closed')
# no break here, so the loop continues and will reconnect
if __name__ == '__main__':
main()
Related
I am creating a Telegram Bot using pyTelegramBotAPI that sends real-time updates of ongoing cricket matches. I want to break the loop whenever the user enters the "/stop" command. I've looked up various sources and also tried several methods to achieve the same but all in vain. The loop continues to iterate. The closest I've reached is by exiting the program by raising an error. Also, while inside the loop, the getUpdates method always returns an empty list. I've also written an issue for the same on GitHub.
def loop(match_url):
prev_info = ""
flag = 1
#continuously fetch data
while flag:
response = requests.get(match_url)
info = response.json()['score']
#display only when the score updates
if str(info) != prev_info:
prev_info = str(info)
send_msg(info)
else:
pass
send_msg(info)
#this handler needs to be fixed
#bot.message_handler(commands=['stop', 'end'])
def stop(message):
#code to break the loop
flag = 0
return
Since this was not working, I willingly used this wrong method:
while flag:
response = requests.get(match_url)
info = response.json()['score']
if str(info) != prev_info:
prev_info = str(info)
send_msg(info)
else:
pass
send_msg(info)
#bot.message_handler(commands=['stop', 'end'])
def stop(message):
bot.polling.abort = True #an arbitrary function that raises error and exits the program
Here's the whole code. I've also added my GitHub link of this code:
import requests, json, telebot
token = <TOKEN>
bot = telebot.TeleBot(token)
#parsing data from cricapi.com
def live_matches():
#here I'm using the KEY obtained from cricapi.com
curr_matches_url = "https://cricapi.com/api/cricket?apikey=<KEY>"
curr_matches = requests.get(curr_matches_url)
match_data = curr_matches.json()['data']
global unique_id_arr, score_arr
unique_id_arr, score_arr = [], []
match_details = ""
for i in match_data:
unique_id_arr.append(i["unique_id"])
for i in range(len(match_data)):
score_arr.append(match_data[i]["title"])
score_arr[i] += "\n"
match_details += str(i+1) + ". "
match_details += score_arr[i]
send_msg(match_details)
def send_msg(msg):
url2 = 'https://api.telegram.org/bot'+token+'/sendMessage'
data = {'chat_id': chat_id, 'text': msg}
requests.post(url2, data).json()
#bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
bot.reply_to(message, "Howdy, how are you doing?")
global chat_id
chat_id = message.chat.id
msg = bot.reply_to(message, "Welcome to test project\nEnter the match number whose updates you want to receive")
live_matches()
bot.register_next_step_handler(msg, fetch_score)
def fetch_score(message):
chat_id = message.chat.id
match_no = message.text
#checking if the number entered is present in the displayed list
if not match_no.isdigit():
msg = bot.reply_to(message, 'Error1!\nSelect a no. from the above list only')
return bot.register_next_step_handler(msg, fetch_score)
elif 1 <= int(match_no) <= len(score_arr):
unique_id = unique_id_arr[int(match_no)-1]
global match_url
#get the data of the desired match
match_url = "https://cricapi.com/api/cricketScore?unique_id="+unique_id+"&apikey=<KEY>"
loop(match_url)
else:
msg = bot.reply_to(message, "Error2!\nSelect a no. from the above list only")
return bot.register_next_step_handler(msg, fetch_score)
def loop(match_url):
prev_info = ""
flag = 1
#continuously fetch data
while flag:
response = requests.get(match_url)
info = response.json()['score']
#display only when the score updates
if str(info) != prev_info:
prev_info = str(info)
send_msg(info)
else:
pass
send_msg(info)
#this handler needs to be fixed
#bot.message_handler(commands=['stop', 'end'])
def stop(message):
#an arbitrary function that raises error and then exits
bot.polling.abort = True
bot.polling()
"""
#currently not using
def receive_msg():
url1 = 'https://api.telegram.org/bot'+token+'/getUpdates'
response = requests.get(url1)
text = response.json()['result']
if len(text) > 0:
user_msg = text[-1]['message']['text']
return user_msg
return text
"""
You are using telebot(pyTelegramBotAPI) package in the wrong way:
Why did you create your own function send_msg where there is already send_message method in telebot exists?
You are redeclaring your "stop" handler in the loop, which is wrong!
My suggestion to you is to learn how to use the pyTelegramBotAPI properly!
Here is a demonstration code, that solves your problem:
import telebot
from time import sleep
bot = telebot.TeleBot(BOT_TOKEN)
flag = 1
#bot.message_handler(commands=['loop'])
def loop(msg):
while flag:
bot.send_message(msg.chat.id, "ping")
sleep(1)
#bot.message_handler(commands=['stop', 'end'])
def stop(msg):
global flag
flag = 0
bot.send_message(msg.chat.id, "stopped")
bot.polling(none_stop=True)
Explanation:
Declared flag as a global variable and set it to 1
"loop" handler for starting the loop that sends you "ping" message every second
"stop" handler that changes flag to 0, which terminates your running loop
I have coded a drawing & guessing game simular to skribble.io and I'm using a TCP Socketserver for providing the game information to all connected players. On the client side is a thread running with a while true loop which is constently requesting for the needed information and on the server side is running a while true loop which is receiving the request and giving the right response for that. For serialization of the data I use pickle. When I'm testing my game on my local machine with a running server and some clients everything is working fine but If I'm testing it on my local network or over the internet it's starting to get very laggy on the client side and everything takes a huge amount of time to display the right server information for example a timer which is running in my game isn't updating every second and also the drawing is laggy.
Client Side:
def receive_data():
global connected_clients, client, start, grid, in_game, lobby_id
while True:
try:
if not in_game:
data = network.receive()
if isinstance(data, list): # get connected clients
connected_clients = data
for c in connected_clients: # get current client
if c.uid == client.uid:
client = c
elif data == 'start':
start = True
in_game = True
else:
if not client.drawing:
res = network.get('grid')['grid'] # get grid
grid.update(res)
# get round information
topbar.word = network.get('word')['word'] # get word
client.drawing = network.get('drawing')['drawing']
topbar.time = network.get('time')['time'] # get round time
topbar.round = network.get('round')['round']
response = network.get('clients') # get all connected clients
connected_clients = response['clients']
for c in connected_clients: # get current client
if c.uid == client.uid:
client = c
messages = network.get('chat')['chat'] # get chat
chat.update(messages, client)
except:
pass
Network method get():
def get(self, data): # returns the response for the request
try:
self.client.send(pickle.dumps(data))
buff_size = 4096
data = bytearray()
while True:
chunk = self.client.recv(buff_size)
data += chunk
if len(chunk) < buff_size:
break
return pickle.loads(data)
except Exception as e:
print(e)
Server Side:
def handle_player(self, connection, player):
data = None
send_msg = {}
while True:
try:
try:
data = self.receive(connection)
except:
pass
if not self.in_game: # lobby logic
if data == 'ready':
player.ready = True
self.broadcast(self.players_to_clients())
if self.all_players_ready():
self.broadcast('start')
self.game = Game(self.players)
for p in self.players:
p.set_game(self.game)
self.in_game = True
elif self.in_game: # in game logic
if player.game:
if isinstance(data, list):
player.game.grid.completely_update(data)
elif isinstance(data, dict):
player.game.make_player_guess(player, data['guess'])
if data == 'word':
send_msg['word'] = player.game.round.word
elif data == 'drawing':
player.drawing = player.game.round.player_drawing == player
if player.drawing:
send_msg['drawing'] = True
else:
send_msg['drawing'] = False
elif data == 'clients':
send_msg['clients'] = self.players_to_clients()
elif data == 'time':
send_msg['time'] = player.game.round.draw_time
elif data == 'grid':
send_msg['grid'] = player.game.grid.get_grid()
elif data == 'chat':
send_msg['chat'] = player.game.chat.messages
elif data == 'round':
send_msg['round'] = player.game.round_count
connection.send(pickle.dumps(send_msg))
else:
pass
except socket.error:
print(f'{player.name} disconnected.')
player.game.player_disconnect(player)
if player in self.players:
self.players.remove(player)
if connection in self.connections:
self.connections.remove(connection)
self.broadcast(self.players_to_clients())
break
I got already an idea for a possible solution which is to switch from TCP to UDP and TCP but i don't know how to implement both in a way that I can send all necessary information.
PS: Sorry for any english mistakes and thanks for any help.
I am creating a simple chat room server using python and when I send data using I'm not receiving it. The code worked until I sepperated it using functions and classes. I did this so it would be simpler to add a UI
here's my server side code:
import socket
import select
from tkinter import *
import threading
HEADER_LENGTH = 10
IP = socket.gethostbyname('0.0.0.0')
PORT = 1234
class ServerNoGui():
def __init__(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((IP, PORT))
self.server_socket.listen()
self.sockets_list = [self.server_socket]
self.clients = {}
print("server started")
print("Starting thread sub-proccesses...")
acceptLoop = threading.Thread(target=self.acceptUsers)
acceptLoop.start()
recieveLoop = threading.Thread(target=self.manageDataSending)
recieveLoop.start()
print("sub-proccesses started!")
def recieve_message(self, client_socket):
message_header = client_socket.recv(HEADER_LENGTH)
if not len(message_header):
return False
message_length = int(float(message_header.decode('utf-8').strip()))
return {'header': message_header, 'data': client_socket.recv(message_length)}
def acceptUsers(self):
read_sockets, _x_, exception_sockets = select.select(self.sockets_list, [], self.sockets_list)
for notified_socket in read_sockets:
if notified_socket == self.server_socket:
client_socket, client_address = self.server_socket.accept()
print("accepted")
user = self.recieve_message(client_socket)
#print(user)
print("Recieved")
if(not user):
continue
self.sockets_list.append(client_socket)
print("added to list")
self.clients[client_socket] = user
print("created user")
print(f"Accepted connection from {client_address[0]}{client_address[1]} username: {user['data'].decode('utf-8')}")
def manageDataSending(self):
while True:
read_sockets, _x_, exception_sockets = select.select(self.sockets_list, [], self.sockets_list)
print("point")
for notified_socket in read_sockets:
if notified_socket == self.server_socket:
print("point 0")
self.acceptUsers()
else:
print("point 1")
message = self.recieve_message(notified_socket)
if(message is False):
print(f"Closed connection from {self.clients[notified_socket]['data'].decode('utf-8')}")
self.sockets_list.remove(notified_socket)
del self.clients[notified_socket]
continue
else:
user = self.clients[notified_socket]
type_, data = message['data'].decode("utf-8").split("$")
if(type_ == "message"):
print(f"Recieved Message from {user['data'].decode('utf-8')} : {message['data'].decode('utf-8')} of type {type_}")
for client_socket in self.clients:
if client_socket != notified_socket:
client_socket.send(user['header'] + user['data'] + message['header'] + message['data'])
for notified_socket in exception_sockets:
sockets_list.remove(notified_socket)
del clients[notified_socket]
print(f"Closed connection from {clients[notified_socket]['data'].decode('utf-8')}")
class serverGUI():
def __init__():
window = Tk()
window.title(f"Chatt.py HOSTING SERVER (IP : {IP} \\\\ HOST : {HOST})")
def createWidgets(self):
return False
def log(self, data):
return False
def loop(self):
window.mainloop()
serverBackend = ServerNoGui()
and here's the client
import socket
import select
import errno
import sys
HEADER_LENGTH = 10
IP = socket.gethostbyname("0.0.0.0")#'192.168.0.40'
PORT = 1234
my_username = input("Username: ")
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((IP, PORT))
client_socket.setblocking(False)
username = my_username.encode("utf-8");
username_header = f"{len(username):<{HEADER_LENGTH}}".encode("utf-8")
client_socket.send(username_header + username)
while True:
messageInput = input(f"Me({my_username}) > ")
message = f"message${messageInput}"
if(message):
message = message.encode("utf-8")
message_header = f"{len(message):<{HEADER_LENGTH}}".encode("utf-8")
client_socket.send(message_header + message)
print(f"sent {message_header} : {message}")
try:
while True:
username_header = client_socket.recv(HEADER_LENGTH)
if(not len(username_header)):
print("connection closed by server")
sys.exit()
username_lenght = int(username_header.decode("utf-8").strip())
username = client_socket.recv(username_lenght).decode("utf-8")
message_header = client_socket.recv(HEADER_LENGTH)
message_length = int(message_header.decode("utf-8").strip())
messageRaw = client_socket.recv(message_length).decode("utf-8")
type_, message = messageRaw.split("$")
if(type_ == message):
print(f"{username} >> {message}")
except IOError as e:
if(e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK):
print("READ ERR",str(e))
sys.exit()
continue
except Exception as e:
print("Error".str(e))
sys.exit()
I decided to try with two clients I get the following output
Server:
server started
Starting thread sub-proccesses...
sub-proccesses started!
point
point 0
accepted
Recieved
added to list
created user
Accepted connection from 127.0.0.160338 username: test1
point
point 0
accepted
Recieved
added to list
created user
Accepted connection from 127.0.0.160340 username: test2
point
point 1
Recieved Message from test2 : message$hello of type message
point
point 1
Recieved Message from test1 : message$ of type message
point
point 1
Recieved Message from test1 : message$hello of type message
client1:
Username: test1
Me(test1) >
sent b'8 ' : b'message$'
Me(test1) > hello
sent b'13 ' : b'message$hello'
client2:
Username: test2
Me(test2) > hello
sent b'13 ' : b'message$hello'
as you can see the messages are sent and recieved by the server but not displayed (I'm not stupid I hit enter a few times).
Your server code is confusing and probably not doing what you thought. First you create a thread to accept connections giving it acceptUsers as its thread function (call this thread A). However, that function will only run once in that thread, then it will exit after it has visited all of the read_sockets (because there's no while True loop).
Second, your other thread (B) is running in manageDataSending -- also executing select, but when a client connects, it's calling acceptUsers. That means on the first connection to your server, there's a "race" between the two threads. It's questionable what will happen next because both are now destined to execute the acceptUsers code at more or less the same time, so the exact order of operations in the two threads is now indeterminate.
It's possible that thread A could run first, handle the server_socket (accept the connection and do the receive) and quit, before thread B enters the select call in acceptUsers. That would leave thread B waiting in the select (in acceptUsers) until the next time a client connects or sends data.
On the other hand, it's possible that thread A and thread B both get past the select in acceptUsers and both execute the accept call. Then the accept will succeed in one, and block in the other, waiting for a subsequent incoming connection. In either case, your thread B eventually ends up blocked in a place in your code that you didn't expect it to be.
Bottom line: there's no obvious reason for you to have the two separate threads, both doing a select call. It will be much simpler and easier to understand what is going on if create only one thread, and have it execute your socket select in one place, have that place handle the socket notifications for both incoming connections and incoming client data.
One other thing to note: you don't really need to handle the exception_sockets separately. If one of your clients goes away, its entry in the read_sockets list will be signaled as "ready-to-read" and when you attempt to read it, you will get an end-of-file indication (i.e. zero-length buffer returned).
I have a list write command that need to infinite loop as thread by turn. Each call thread need to wait recv method to match OK or ERROR conditional. Then, call next command again with thread. The command in below. Current, the output buffer in unorder. It can't use join. Because, write command to fast than income buffer. The only ways is to pause after call write method and wait recv method a signal conditional match OK or ERROR. Then, iterate next command again and so on. How to achieve this?
AT+CGMI
AT+CGSN
class Device():
__command = 'none'
def __init__(self, port, baudrate):
self.__port = port
self.__baudrate = baudrate
def open(self):
try:
self.handle = serial.Serial(self.__port, self.__baudrate)
return True
except SerialException as e:
error = re.findall(r"'(.*?)'", str(e))
self.__error = {'port': error[0], 'description': error[1]}
return None
def __state(self):
if self.open() is None:
if self.__error['description'] == 'Access is denied.':
return True
elif self.__error['description'] == 'Port is already open.':
return True
else:
return False
else:
return True
def recv(self):
while True:
if self.__state():
buffer = self.handle.readline()
if buffer == b'AT+CGSN\r\r\n':
data = []
while True:
buffers = self.handle.readline()
data.append(buffers.decode('UTF-8').strip())
if buffers == b'OK\r\n':
print(data[0])
self.__command = 'done'
break
elif buffers == b'ERROR\r\n':
break
elif buffer == b'AT+CGMI\r\r\n':
data = []
while True:
buffers = self.handle.readline()
data.append(buffers.decode('UTF-8').strip())
if buffers == b'OK\r\n':
print(data[0])
self.__command = 'done'
break
elif buffers == b'ERROR\r\n':
break
else:
print(self.__error['port'] + ' ' + self.__error['description'])
time.sleep(1)
def write(self, command):
if self.__state():
self.__command = 'waiting'
self.handle.write(command.encode('UTF-8') + b'\r')
while self.__command == 'waiting':
if self.__command == 'done':
break
device = Device('COM12', 9600)
device.open()
recv = threading.Thread(target=device.recv)
recv.start()
commands = ['AT+CGSN', 'AT+CGMI']
while True:
for command in commands:
device.write(command)
I'm just trying to make a 'Twitch Plays' game. I used a tutorial for making Twitch Plays games. I need to re-run this code every five minutes as it ignores the messages. I'm making Threes game on Scratch. I checked for all the possible bugs and it works very well. This game is based on moving tiles that contain numbers on them. Any suggestions regarding the IRC twitch ignoring all messages will be highly appreciated.
The code is shown here below.
import socket
import pyautogui
import time
SERVER = "irc.twitch.tv"
PORT = 6667
PASS = "oauth:fz5kp09su0lz46vnos78sklvpjnu6l"
BOT = "TwitchBot"
CHANNEL = "olejika2016"
OWNER = "olejika2016"
message = ""
irc = socket.socket()
irc.connect((SERVER,PORT))
irc.send(( "PASS "+PASS+"\n"+
"NICK "+BOT+"\n"+
"JOIN #"+CHANNEL+"\n").encode())
def twitch():
def joinchat():
loading = True
while loading:
readbuffer_join = irc.recv(1024)
readbuffer_join = readbuffer_join.decode()
for line in readbuffer_join.split("\n")[0:-1]:
# print(line)
loading = loadingComplete(line)
def loadingComplete(line):
if ("End of /NAMES list" in line):
print("Bot has joined "+CHANNEL+"'s channel")
sendMessage(irc, "Chat Room Joined")
return False
else:
return True
def sendMessage(irc, message):
messageTemp = "PRIVMSG #" + CHANNEL + " :" + message
irc.send((messageTemp+"\n").encode())
def getUser(line):
separate = line.split(":", 2)
user = separate[1].split("!",1)[0]
return user
def getMessage(line):
global message
try:
message = (line.split(":",2))[2]
except:
message = ""
return message
joinchat()
while True:
try:
readbuffer = irc.recv(1024).decode()
except:
readbuffer = ""
for line in readbuffer.split("\r\n"):
if line == "":
continue
else:
# print("2;"+line)
user = getUser(line)
message = getMessage(line)
print(user+":"+message)
def writeUser():
time.sleep(0.1)
pyautogui.typewrite(user)
pyautogui.press('enter')
if message.lower() == "w":
pyautogui.press('up')
writeUser()
if message.lower() == "s":
pyautogui.press('down')
writeUser()
if message.lower() == "a":
pyautogui.press('left')
writeUser()
if message.lower() == "d":
pyautogui.press('right')
writeUser()
else:
pass
user = ''
message = ''
twitch()
for line in readbuffer.split("\r\n"):
if line == "":
continue
elif "PING" is line and Console(line):
msgg = "PONG tml.twitch.tv\r\n".encode()
irc.send(msgg)
print(msgg)
continue
else:
user = getUser(line)
message = getMessage(line)
print(user+":"+message)