Return JSON Array Websockets data feed - python

I am trying to get the message that is being returned by and apparently it is more of a
<function on_message at 0x00000138D6488F28>
How can I get it to return json message instead of the one above?
Below is my code.
from websocket import WebSocketApp
from json import dumps, loads
from pprint import pprint
URL = "wss://ws-feed.gdax.com"
def on_message(_, message):
pprint(loads(message))
print
def on_open(socket):
params = {
"type": "subscribe",
"channels": [{"name": "ticker", "product_ids": ["BTC-EUR"]}]
}
socket.send(dumps(params))
def main():
ws = WebSocketApp(URL, on_open=on_open, on_message=on_message)
while True:
print(ws.on_message)
time.sleep(1)
if __name__ == '__main__':
main()

def on_message(_, message): # this will be called everytime a message is recieved
pprint(loads(message))
print
def main():
ws = WebSocketApp(URL, on_open=on_open, on_message=on_message)
ws.run_forever() # this will run the ws main_loop that is builtin and listen for the connection and messages
ps thats a cool ws service ... I always enjoy finding new public ws services

Well, it prints function, because you literally are printing out the function, not calling it. But that function is not meant to be called by you, but it will be called by the WebSocketApp when you actually have a message and someone has connected to your socket.
If you, however, really want to call it, you can probably just do it by changing print(ws.on_message) to ws.on_message(None, 'your message').

Related

Tkinter GUI is not showing, But Response is received for the functions

Im trying to create GUI using tkinter, objective is to call a function from Button(Tkinter Window), without websocket function , i'm able to get Tkinter window and able to execute the function via button, trying the same with Websocket client function, functions are executed at first and Tkinter window is not showing UP.
from tkinter import *
from threading import *
import websocket
import threading
window=Tk()
window.title("Scalp")
window.geometry('400x400')
window.config(bg='lavender')
def Login():
import requests
import json
global tokens
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
url = 'https://api.stocknote.com/login'
myobj = { 'userId': 'DDXX12', 'password': 'Durant', 'yob': '1999'}
res = requests.post(url, data = json.dumps(myobj), headers = headers)
tokens = res.json().get("sessionToken")
print (res.text);
print(tokens)
def on_message(ws, msg):
print ("Message Arrived:" + msg)
return
def on_error(ws, error):
print (error)
return
def on_close(ws):
print ("Connection Closed")
return
def on_open(ws):
print ("Sending json")
data='{"request":{"streaming_type":"quote", "data":{"symbols":[{"symbol":"45402_NFO"}]}, "request_type":"subscribe", "response_format":"json"}}'
ws.send(data)
ws.send("\n")
return
def connection():
Login()
headers = {'x-session-token': tokens }
websocket.enableTrace(True)
ws = websocket.WebSocketApp("wss://stream.stocknote.com", on_open = on_open, on_message = on_message, on_error = on_error, on_close = on_close, header = headers)
ws.run_forever()
return
a=Button(window,text='Login',width=12,bg='azure',command=Login(),fg='black',font('bold',14),activebackground='dark sea green',activeforeground='khaki3')
a.place(x=100,y=50)
b=Button(window,text='BankNifty',width=12,bg='azure',command=connection(),fg='black',font('bold',14),activebackground='dark sea green',activeforeground='khaki3')
b.place(x=100,y=90)
window.mainloop
Tkinter window is not appearing , but functions are executed directly.
There are several problems in your code.
First, you aren't calling mainloop. You need to change window.mainloop to be window.mainloop().
The second problem is that you are immediately calling connection and Login at startup because you are specifying them wrong.
When creating a button, you must pass a callable. In your case you're immediately calling the function and then passing the result to the command option. You need to change command=Login() to command=Login, and change command=connection() to command=connection.
Finally, when connection is called, it calls ws.run_forever(). That is a blocking call - it doesn't return. Because it doesn't return, the tkinter event loop (what gets started with mainloop()) never has a chance to run. Because it doesn't run, it can't process even basic events like requests to display the window.

Binance stream trade via websocket problem

I am getting no response from the Binance api when executing the following code, is there anything that I could have missed here? do I need to create an account in order to get stream data through the api?
import json
import websocket
socket='wss://stream.binance.com:9443'
def on_open(ws):
print("opened")
subscribe_message = {
"method": "SUBSCRIBE",
"params":
[
"btcusdt#trade",
"btcusdt#depth"
],
"id": 1
}
ws.send(json.dumps(subscribe_message))
def on_message(ws, message):
print("received a message")
print(json.loads(message))
def on_close(ws):
print("closed connection")
ws = websocket.WebSocketApp(socket, on_open=on_open, on_message=on_message, on_close=on_close)
ws.run_forever()
In order to make your code work you just need to add /ws at the end of the websocket url:
socket = 'wss://stream.binance.com:9443/ws'
What I can see is, that you are not using the path after the hostname and port!
See here what is missing:
https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#general-wss-information
Raw streams are accessed at /ws/streamName>
Combined streams are accessed at /stream?streams=streamName1/streamName2/streamName3
If you want to subscribe to streams via websocket.send() then you have to create a combined stream first and then send the payload to subscribe the streams.
But you dont have to reinvent the wheel. There are different ready implementations for python.
I recommend you this lib I have written: https://github.com/oliver-zehentleitner/unicorn-binance-websocket-api
This will save you a lot of work :)
Best regards,
Oliver

Threads can only be started once in Django Channels

I created a simple Django Channels consumer that should connects to an external source, retrieve data and send it to the client. So, the user opens the page > the consumer connects to the external service and gets the data > the data is sent to the websocket.
Here is my code:
import json
from channels.generic.websocket import WebsocketConsumer, AsyncConsumer, AsyncJsonWebsocketConsumer
from binance.client import Client
import json
from binance.websockets import BinanceSocketManager
import time
import asyncio
client = Client('', '')
trades = client.get_recent_trades(symbol='BNBBTC')
bm = BinanceSocketManager(client)
class EchoConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
await self.accept()
await self.send_json('test')
bm.start_trade_socket('BNBBTC', self.process_message)
bm.start()
def process_message(self, message):
JSON1 = json.dumps(message)
JSON2 = json.loads(JSON1)
#define variables
Rate = JSON2['p']
Quantity = JSON2['q']
Symbol = JSON2['s']
Order = JSON2['m']
asyncio.create_task(self.send_json(Rate))
print(Rate)
This code works when i open one page; if i try to open a new window with a new account, though, it will throw the following error:
File "C:\Users\User\Desktop\Heroku\github\master\myapp\consumers.py", line 54, in connect
bm.start()
File "C:\Users\User\lib\threading.py", line 843, in start
raise RuntimeError("threads can only be started once")
threads can only be started once
I'm new to Channels, so this is a noob question, but how can i fix this problem? What i wanted to do was: user opens the page and gets the data, another user opens the page and gets the data; is there no way to do that? Or am i simply misunderstanding how Django Channels and websockets works?
Do you really need a secondary thread ?
class EchoConsumer(AsyncJsonWebsocketConsumer):
symbol = ''
async def connect(self):
self.symbol = 'BNBBTC'
# or, more probably, retrieve the value for "symbol" from query_string
# so the client can specify which symbol he's interested into:
# socket = new WebSocket("ws://.../?symbol=BNBBTC");
await self.accept()
def process_message(self, message):
# PSEUDO-CODE BELOW !
if self.symbol == message['symbol']:
await self.send({
'type': 'websocket.send',
'text': json.dumps(message),
})
For extra flexibility, you might also accept al list of symbols from the client, instead:
//HTML
socket = new WebSocket("ws://.../?symbols=XXX,YYY,ZZZ");
then (in the consumer):
class EchoConsumer(AsyncJsonWebsocketConsumer):
symbols = []
async def connect(self):
# here we need to parse "?symbols=XXX,YYY,ZZZ" ...
# the code below has been stolen from another project of mine and should be suitably adapted
params = urllib.parse.parse_qs(self.scope.get('query_string', b'').decode('utf-8'))
try:
self.symbols = json.loads(params.get('symbols', ['[]'])[0])
except:
self.symbols = []
def process_message(self, message):
if message['symbol'] in self.symbols:
...
I'm no Django developer, but if I understand correctly, the function connect is being called more than once-- and bm.start references the same thread most likely made in bm.start_trade_socket (or somewhere else in connect). In conclusion, when bm.start is called, a thread is started, and when it is done again, you get that error.
Here start() Start the thread’s activity.
It should be called at most once per thread object. You have made a global object of BinanceSocketManager as "bm".
It will always raise a RuntimeError if called more than once on the same thread object.
Please refer the below mentioned code, it may help you
from channels.generic.websocket import WebsocketConsumer, AsyncConsumer, AsyncJsonWebsocketConsumer
from binance.client import Client
import json
from binance.websockets import BinanceSocketManager
import time
import asyncio
class EchoConsumer(AsyncJsonWebsocketConsumer):
client = Client('', '')
trades = client.get_recent_trades(symbol='BNBBTC')
bm = BinanceSocketManager(client)
async def connect(self):
await self.accept()
await self.send_json('test')
self.bm.start_trade_socket('BNBBTC', self.process_message)
self.bm.start()
def process_message(self, message):
JSON1 = json.dumps(message)
JSON2 = json.loads(JSON1)
#define variables
Rate = JSON2['p']
Quantity = JSON2['q']
Symbol = JSON2['s']
Order = JSON2['m']
asyncio.create_task(self.send_json(Rate))
print(Rate)

Websocket to useable data in Python - get price from GDAX websocket feed

I am trying to get the last price data to use which is easy enough using polling on /ticker endpoint i.e.
rawticker = requests.get('https://api.gdax.com/products/BTC-EUR/ticker')
json_data = json.loads(rawticker.text)
price = json_data['price']
but the GDAX API discourages polling. How could I get the same information using websocket. How could I get the following code to run just once and then extract the price information.
from websocket import WebSocketApp
from json import dumps, loads
from pprint import pprint
URL = "wss://ws-feed.gdax.com"
def on_message(_, message):
"""Callback executed when a message comes.
Positional argument:
message -- The message itself (string)
"""
pprint(loads(message))
print
def on_open(socket):
"""Callback executed at socket opening.
Keyword argument:
socket -- The websocket itself
"""
params = {
"type": "subscribe",
"channels": [{"name": "ticker", "product_ids": ["BTC-EUR"]}]
}
socket.send(dumps(params))
def main():
"""Main function."""
ws = WebSocketApp(URL, on_open=on_open, on_message=on_message)
ws.run_forever()
if __name__ == '__main__':
main()
Thanks for any help.
Pulling is discouraged when you want to have real-time updates. In that case, it is recommended to use Web Sockets. In your case, however, running the code once and exiting, it is fine to use the pull endpoint.
To answer your question anyway. on_message's first argument is the WebSocketApp you can simply add this line to close it after receiving the first message.
def on_message(ws, message):
"""Callback executed when a message comes.
Positional argument:
message -- The message itself (string)
"""
pprint(loads(message))
ws.close()
Tip
Requests library has built-in .json() that you can use directly on the .get() return
import requests
rawticker = requests.get('https://api.gdax.com/products/BTC-EUR/ticker').json()
price = rawticker['price']
print(price)

Python Logging Module Output To Screen Lag

In my python script, I am using the logging module with INFO going to a file and the screen:
fh_info = logging.FileHandler(info)
fh_info.setLevel(logging.INFO)
fh_info.setFormatter(formatter)
std_out_info = logging.StreamHandler()
std_out_info.setLevel(logging.INFO)
logger.addHandler(fh_info)
logger.addHandler(std_out_info)
But the issue I am having is that the messages got to the screen after the function is completed. For instance, in:
def getToken(self):
msg = ("Initializing token....")
logger.info(msg)
sys.stdout.flush()
try:
jsonreq = ( {"auth": {"KEY:apiKeyCredentials": {
"username": self.username,
"apiKey": self.apikey}}})
auth_headers = {'content-type': 'application/json'}
r = requests.post(self.url, data=json.dumps(jsonreq), headers=auth_headers)
self.jsonresp = json.loads(r.text)
self.token = str(self.jsonresp['access']['token']['id'])
msg = ("done!")
logger.info(msg)
except:
msg = ("Bad name or apikey!")
logger.error(msg)
sys.exit()
The message "Initializing token...." will go to the screen after the operations in the function are completed. The makes for a long pause while authentication is going on with no output...then after authentication completes I see "Initializing token....done".
How can I make the logger.info(msg) stay in sync with the flow of the script and output to the screen in it's timely manner?
You are not initialising the StreamHandler with sys.stdout, so it will use the documented default - sys.stderr. That's why your flushing of sys.stdout isn't having any effect - there's nothing the the code you posted which is causing writes to sys.stdout!

Categories