Python:Asyncio / Aiohttp Websocket客户端请求出现问题

时间:2018-03-06 19:59:58

标签: python websocket python-asyncio aiohttp

一直试图从Bitfinex websocket客户端服务中提取websocket信息。下面是代码。当我搜索30个以下加密对时(即#34; p"或" PAIRS"有30个元素),该脚本工作正常但如果我试图更高,脚本永远不会到达& #34; save_data"协同例程。任何想法为什么会发生这种情况。

我修改了脚本:" https://mmquant.net/replicating-orderbooks-from-websocket-stream-with-python-and-asyncio/",赞美Mmquant使代码可用,并提供了一个很棒的脚本描述。

import aiohttp
import asyncio
import ujson
from tabulate import tabulate
from copy import deepcopy
import pandas as pd
from openpyxl import load_workbook
import datetime
from datetime import datetime
import numpy as np
from collections import OrderedDict
from time import sleep

"""
Load the workbook to dump the API data as well as instruct it to not generate a new sheet.
The excel work book must:
1. Be of the type ".xlsx", only this because the load_workbook function was set to call a specific sheet with .xlsx format. This can be changed.
2. Must have the worksheets, "apidata" and "Test". This can also be adjusted below.
3. The excel workbooks name is "bitfinexws.xlsx". This can be changed below.
4. The excel spreadsheet is in the same folder as this script.
"""
book = load_workbook('bitfinexwsasync.xlsx') #.xlsx Excel spreadsheet that will be used for the placement and extracting of data.
apdat = book['Sheet1'] #Assign a variable to the sheet where the trade ratios will be put. This is case sensitive.

#The next 3 lines are critical to allow overwriting of data and not creating a new worksheet when using panda dataframes.
writer = pd.ExcelWriter('bitfinexwsasync.xlsx', engine='openpyxl')
writer.book = book
writer.sheets = dict((ws.title, ws) for ws in book.worksheets)

#Get a list of all the ratios and add the standard trade url: "https://api.bitfinex.com/v1/book/" before the ratios.
burl = 'https://api.bitfinex.com/v1/book/' #This is the standard url for retrieving trade ratios, the pair symbol must be added after this.
sym = pd.read_json('https://api.bitfinex.com/v1/symbols',orient='values') #This is a list of all the symbols on the Bitfinex website.
p=[]
p=[0]*len(sym)

for i in range(0,len(sym)):
       p[i]=sym.loc[i,0]

p=tuple(p)
m=len(p) #Max number of trade ratios to extract for this script. Script cannot run the full set of 105 trade ratios, it will time-out.
p=p[0:m]
d=[]
e=[]
j=[]

"""
NOTE:
The script cannot run for the full 105 pairs, it timesout and becomes unresponsive.
By testig the stability it was found that calling 21 pairs per script at a refresh rate of 5seconds did not allow for any time-out problems.

"""

print('________________________________________________________________________________________________________')
print('')
print('Bitfinex Websocket Trading Orderbook Extraction - Asynchronous.')
print('There are a total of ', len(sym), ' trade ratios in this exchange.')
print('Only ',m,' trading pairs will be extracted by this script, namely:',p) 
print('Process initiated at',datetime.now().strftime('%Y-%m-%d %H:%M:%S'),'.') #Tells me the date and time that the data extraction was intiated.
print('________________________________________________________________________________________________________')
print('')

# Pairs which generate orderbook for.
PAIRS = p

# If there is n pairs we need to subscribe to n websocket channels.
# This the subscription message template.
# For details about settings refer to https://bitfinex.readme.io/v2/reference#ws-public-order-books.
SUB_MESG = {
        'event': 'subscribe',
        'channel': 'book',
        'freq': 'F0', #Adjust for real time
        'len': '25',
        'prec': 'P0'
        # 'pair': <pair>
    }


def build_book(res, pair):
    """ Updates orderbook.
    :param res: Orderbook update message.
    :param pair: Updated pair.

    """
    global orderbooks

    # Filter out subscription status messages.
    if res.data[0] == '[':

        # String to json
        data = ujson.loads(res.data)[1]

        # Build orderbook
        # Observe the structure of orderbook. The prices are keys for corresponding count and amount.
        # Structuring data in this way significantly simplifies orderbook updates.
        if len(data) > 10:
            bids = {
                       str(level[0]): [str(level[1]), str(level[2])]
                       for level in data if level[2] > 0
            }

            asks = {
                       str(level[0]): [str(level[1]), str(level[2])[1:]]
                       for level in data if level[2] < 0
            }

            orderbooks[pair]['bids'] = bids
            orderbooks[pair]['asks'] = asks

        # Update orderbook and filter out heartbeat messages.
        elif data[0] != 'h':

            # Example update message structure [1765.2, 0, 1] where we have [price, count, amount].
            # Update algorithm pseudocode from Bitfinex documentation:
            # 1. - When count > 0 then you have to add or update the price level.
            #   1.1- If amount > 0 then add/update bids.
            #   1.2- If amount < 0 then add/update asks.
            # 2. - When count = 0 then you have to delete the price level.
            #   2.1- If amount = 1 then remove from bids
            #   2.2- If amount = -1 then remove from asks

            data = [str(data[0]), str(data[1]), str(data[2])]
            if int(data[1]) > 0:  # 1.

                if float(data[2]) > 0:  # 1.1
                    orderbooks[pair]['bids'].update({data[0]: [data[1], data[2]]})

                elif float(data[2]) < 0:  # 1.2
                    orderbooks[pair]['asks'].update({data[0]: [data[1], str(data[2])[1:]]})

            elif data[1] == '0':  # 2.

                if data[2] == '1':  # 2.1
                    if orderbooks[pair]['bids'].get(data[0]):
                        del orderbooks[pair]['bids'][data[0]]

                elif data[2] == '-1':  # 2.2
                    if orderbooks[pair]['asks'].get(data[0]):
                        del orderbooks[pair]['asks'][data[0]]

async def save_data():
    """ Save the data to the excel spreadsheet specified """
    #NOTE, Adjusted this for every 5 seconds, ie "await asyncio.sleep(10)" to "await asyncio.sleep(5)"
    global orderbooks
    while 1:
        d=[]
        e=[]
        j=[]
        await asyncio.sleep(5)
        for pair in PAIRS:
            bids2 = [[v[1], v[0], k] for k, v in orderbooks[pair]['bids'].items()]
            asks2 = [[k, v[0], v[1]] for k, v in orderbooks[pair]['asks'].items()]
            bids2.sort(key=lambda x: float(x[2]), reverse=True)
            asks2.sort(key=lambda x: float(x[0]))
            table2 = [[*bid, *ask] for (bid, ask) in zip(bids2, asks2)]
            d.extend(table2)
            e.extend([0]*len(table2))
            e[len(e)-len(table2)]=pair
        j.extend([0]*len(d))
        j[0]=datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        s = pd.DataFrame(d, columns=['bid:amount', 'bid:count', 'bid:price', 'ask:price', 'ask:count', 'ask:amount'])
        r = pd.DataFrame(e, columns=['Trade pair'])
        u = pd.DataFrame(j, columns=['Last updated'])
        z = pd.concat([s, r, u], axis=1, join_axes=[s.index])
        z.to_excel(writer, 'Sheet1', startrow=0, startcol=0, index=False)
        writer.save()
        print('Update completed at',datetime.now().strftime('%Y-%m-%d %H:%M:%S'),'.')


async def get_book(pair, session):
    """ Subscribes for orderbook updates and fetches updates. """
    #print('enter get_book, pair: {}'.format(pair))
    pair_dict = deepcopy(SUB_MESG) #Allows for changes to a made within a variable.
    pair_dict.update({'pair': pair}) #Updates the dictionary SUB_MESG with the new pair to be evaluated. Will be added to the end of the dictionary.
    async with session.ws_connect('wss://api.bitfinex.com/ws/2') as ws:
        asyncio.ensure_future(ws.send_json(pair_dict)) #This was added and replaced "ws.send_json(pair_dict)" as Ubuntu python required a link to asyncio for this function.
        while 1: #Loops infinitely.
               res = await ws.receive()
               print(pair_dict['pair'], res.data)  # debug
               build_book(res, pair)

async def main():
    """ Driver coroutine. """
    async with aiohttp.ClientSession() as session:
        coros = [get_book(pair, session) for pair in PAIRS]
        # Append coroutine for printing orderbook snapshots every 10s.
        coros.append(save_data())

        await asyncio.wait(coros)

orderbooks = {
    pair: {}
    for pair in PAIRS
}

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

0 个答案:

没有答案