python非同步加協程擷取比特幣市場資訊

來源:互聯網
上載者:User

標籤:mon   5.7   1.0   rar   mat   隊列   ali   tick   end   

目標

  選取幾個比特幣交易量大的幾個交易平台,查看對應的API,擷取該市場下貨幣對的ticker和depth資訊。我們從網站上選取4個交易平台:bitfinex、okex、binance、gdax。對應的交易對是BTC/USD,BTC/USDT,BTC/USDT,BTC/USD。

 

一、ccxt庫

  開始想著直接請求市場的API,然後再解析擷取下來的資料,但到github上發現一個比較好得python庫,裡面封裝好了擷取比特幣市場的相關函數,這樣一來就省掉分析API的時間了。因此我只要傳入市場以及對應的貨幣對,利用庫裡面的函數 fetch_ticker 和 fetch_order_book 就可以擷取到市場的ticker和depth資訊(具體的使用方法可以查看ccxt手冊)。接下來以市場okex為例,利用ccxt庫擷取okex的ticker和depth資訊。

# 引入庫import ccxt# 執行個體化市場exchange = ccxt.okex()# 交易對symbol = ‘BTC/USDT‘# 擷取ticker資訊ticker = exchange.fetch_ticker(symbol)# 擷取depth資訊depth = exchange.fetch_order_book(symbol)print(‘ticker:%s, depth:%s‘ % (ticker, depth))

   運行後會得到結果如,從此可以看出已經擷取到了ticker和depth資訊。

 

 

 二、擷取四個市場的資訊(for迴圈)

   接下來我們擷取四個市場的資訊,深度裡面有asks和bids,資料量稍微有點兒多,這裡depth資訊我只去前面五個,對於ticker我也只提取裡面的info資訊(具體代表什麼含義就要參考一下對應市場的API啦)。將其簡單的封裝後,最開始我想的是for迴圈。想到啥就開始吧:

# 引入庫import ccxtimport timenow = lambda: time.time()start = now()def getData(exchange, symbol):    data = {}  # 用於儲存ticker和depth資訊    # 擷取ticker資訊    tickerInfo = exchange.fetch_ticker(symbol)    # 擷取depth資訊    depth = {}    # 擷取深度資訊    exchange_depth = exchange.fetch_order_book(symbol)    # 擷取asks,bids 最低5個,最高5個資訊    asks = exchange_depth.get(‘asks‘)[:5]    bids = exchange_depth.get(‘bids‘)[:5]    depth[‘asks‘] = asks    depth[‘bids‘] = bids    data[‘ticker‘] = tickerInfo    data[‘depth‘] = depth    return datadef main():    # 執行個體化市場    exchanges = [ccxt.binance(), ccxt.bitfinex2(), ccxt.okex(), ccxt.gdax()]    # 交易對    symbols = [‘BTC/USDT‘, ‘BTC/USD‘, ‘BTC/USDT‘, ‘BTC/USD‘]    for i in range(len(exchanges)):        exchange = exchanges[i]        symbol = symbols[i]        data = getData(exchange, symbol)        print(‘exchange: %s data is %s‘ % (exchange.id, data))if __name__ == ‘__main__‘:    main()    print(‘Run Time: %s‘ % (now() - start))

   運行後會發現雖然每個市場的資訊都擷取到了,執行完差不多花掉5.7秒,因為這是同步的,也就是按順序執行的,要是要想每隔一定時間同時擷取四個市場的資訊,很顯然這種結果不符合我們的要求。

 

 

三、非同步加協程(coroutine)

  前面講的迴圈雖然可以輸出結果,但耗時間長度而且達不到想要的效果,接下來採用非同步加協程(參考知乎上的一篇文章),要用到非同步首先得引入asyncio庫,這個庫是3.4以後才有的,它提供了一種機制,使得你可以用協程(coroutines)、IO複用(multiplexing I/O)在單線程環境中編寫並行存取模型。這裡python文檔有個小例子。

import asyncioasync def compute(x, y):    print("Compute %s + %s ..." % (x, y))    await asyncio.sleep(1.0)    return x + yasync def print_sum(x, y):    result = await compute(x, y)    print("%s + %s = %s" % (x, y, result))loop = asyncio.get_event_loop()loop.run_until_complete(print_sum(1, 2))loop.close()

  

 

  當事件迴圈開始運行時,它會在Task中尋找coroutine來執行調度,因為事件迴圈註冊了print_sum(),因此print_sum()被調用,執行result = await compute(x, y)這條語句(等同於result = yield from compute(x, y)),因為compute()自身就是一個coroutine,因此print_sum()這個協程就會暫時被掛起,compute()被加入到事件迴圈中,程式流執行compute()中的print語句,列印”Compute %s + %s …”,然後執行了await asyncio.sleep(1.0),因為asyncio.sleep()也是一個coroutine,接著compute()就會被掛起,等待計時器讀秒,在這1秒的過程中,事件迴圈會在隊列中查詢可以被調度的coroutine,而因為此前print_sum()compute()都被掛起了,因此事件迴圈會停下來等待協程的調度,當計時器讀秒結束後,程式流便會返回到compute()中執行return語句,結果會返回到print_sum()中的result中,最後列印result,事件隊列中沒有可以調度的任務了,此時loop.close()把事件隊列關閉,程式結束。

  接下來我們採用非同步和協程(ps:ccxt庫也有對應的非同步),運行後發現時間只用了1.9秒,比之前快了好多倍。  

Run Time: 1.9661316871643066 

相關代碼:

# 引入庫import ccxt.async as ccxtimport asyncioimport timenow = lambda: time.time()start = now()async def getData(exchange, symbol):    data = {}  # 用於儲存ticker和depth資訊    # 擷取ticker資訊    tickerInfo = await exchange.fetch_ticker(symbol)    # 擷取depth資訊    depth = {}    # 擷取深度資訊    exchange_depth = await exchange.fetch_order_book(symbol)    # 擷取asks,bids 最低5個,最高5個資訊    asks = exchange_depth.get(‘asks‘)[:5]    bids = exchange_depth.get(‘bids‘)[:5]    depth[‘asks‘] = asks    depth[‘bids‘] = bids    data[‘ticker‘] = tickerInfo    data[‘depth‘] = depth    return datadef main():    # 執行個體化市場    exchanges = [ccxt.binance(), ccxt.bitfinex2(), ccxt.okex(), ccxt.gdax()]    # 交易對    symbols = [‘BTC/USDT‘, ‘BTC/USD‘, ‘BTC/USDT‘, ‘BTC/USD‘]    tasks = []    for i in range(len(exchanges)):        task = getData(exchanges[i], symbols[i])        tasks.append(asyncio.ensure_future(task))    loop = asyncio.get_event_loop()    loop.run_until_complete(asyncio.wait(tasks))if __name__ == ‘__main__‘:    main()    print(‘Run Time: %s‘ % (now() - start))

  

 三、定時爬取並用mongodb儲存資料

  在前面的基礎上,添加一個定時任務,實現每隔一段時間爬取一次資料,並將資料儲存到mongodb資料庫。只需再前面的代碼上稍微改改就可以啦,代碼和運行結果如下:

 

import asyncioimport ccxt.async as ccxtimport timeimport pymongo# 擷取ticker和depth資訊async def get_exchange_tickerDepth(exchange, symbol):  # 其中exchange為執行個體化後的市場    # print(‘start get_ticker‘)    while True:        print(‘%s is run %s‘ % (exchange.id, time.ctime()))        # 擷取ticher資訊        tickerInfo = await exchange.fetch_ticker(symbol)        ticker = tickerInfo.get(‘info‘)        if type(ticker) == type({}):            ticker[‘timestamp‘] = tickerInfo.get(‘timestamp‘)            ticker[‘high‘] = tickerInfo.get(‘high‘)            ticker[‘low‘] = tickerInfo.get(‘low‘)            ticker[‘last‘] = tickerInfo.get(‘last‘)        else:            ticker = tickerInfo        # print(ticker)        # 擷取深度資訊        depth = {}        exchange_depth = await exchange.fetch_order_book(symbol)        # 擷取asks,bids 最低5個,最高5個資訊        asks = exchange_depth.get(‘asks‘)[:5]        bids = exchange_depth.get(‘bids‘)[:5]        depth[‘asks‘] = asks        depth[‘bids‘] = bids        # print(‘depth:{}‘.format(depth))        data = {            ‘exchange‘: exchange.id,            ‘countries‘: exchange.countries,            ‘symbol‘: symbol,            ‘ticker‘: ticker,            ‘depth‘: depth        }        # 儲存資料        save_exchangeDate(exchange.id, data)        print(‘********* %s is finished, time %s *********‘ % (exchange.id, time.ctime()))        # 等待時間        await asyncio.sleep(2)# 存庫def save_exchangeDate(exchangeName, data):    # 連結MongoDB    connect = pymongo.MongoClient(host=‘localhost‘, port=27017)    # 建立資料庫    exchangeData = connect[‘exchangeDataAsyncio‘]    # 建立表    exchangeInformation = exchangeData[exchangeName]    # print(table_name)    # 資料去重後儲存    count = exchangeInformation.count()    if not count > 0:        exchangeInformation.insert_one(data)    else:        for item in exchangeInformation.find().skip(count - 1):            lastdata = item        if lastdata[‘ticker‘][‘timestamp‘] != data[‘ticker‘][‘timestamp‘]:            exchangeInformation.insert_one(data)def main():    exchanges = [ccxt.binance(), ccxt.bitfinex2(), ccxt.okex(),                  ccxt.gdax()]    symbols = [‘BTC/USDT‘, ‘BTC/USD‘, ‘BTC/USDT‘, ‘BTC/USD‘]    tasks = []    for i in range(len(exchanges)):        task = get_exchange_tickerDepth(exchanges[i], symbols[i])        tasks.append(asyncio.ensure_future(task))    loop = asyncio.get_event_loop()    try:        # print(asyncio.Task.all_tasks(loop))        loop.run_forever()    except Exception as e:        print(e)        loop.stop()        loop.run_forever()    finally:        loop.close()if __name__ == ‘__main__‘:    main()

 

 五、小結

  使用協程可以實現高效的並發任務。Python在3.4中引入了協程的概念,可是這個還是以產生器對象為基礎,3.5則確定了協程的文法。這裡只簡單的使用了asyncio。當然實現協程的不僅僅是asyncio,tornado和gevent都實現了類似的功能。這裡我有一個問題,就是運行一段時間後,裡面的市場可能有請求逾時等情況導致協程停止運行,我要怎樣才能擷取到錯誤然後重啟對應的協程。如果有大神知道的話請指點指點。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

python非同步加協程擷取比特幣市場資訊

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.