小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

【給量化行情插上翅膀】天翼云電腦上實踐純Python通過LMDB加速股票行情讀寫速度

 長江黃鶴 2023-05-30 發(fā)布于湖北

前言

**量化交易,行情先行!**對于量化交易,行情數(shù)據(jù)很重要,可以說很關(guān)鍵。策略再優(yōu)秀,行情有問題,跑出的結(jié)果那就千差萬別了。如果誤觸發(fā),虧了錢連說理的地方都找不到。

**行情要準,但也要快!**獲取行情數(shù)據(jù),因為網(wǎng)絡(luò)時延、本地IO開銷、電腦速度等原因,會大大增加開銷。就算你1000兆光纖入戶,但這個只是下行網(wǎng)速,不代表響應(yīng)時延就能比交易所附近的10M專線快小。本地IO開銷,M.2 NVMe固態(tài)盤目前是個人配置的最優(yōu)方案。電腦速度那就因人而異了,但獲取網(wǎng)絡(luò)上的行情數(shù)據(jù),CPU再好,對提速幫助不是很大。

那么,如何實現(xiàn)對行情數(shù)據(jù)的極速、穩(wěn)定的訪問呢?

筆者之前一直使用Mysql,后來也試過MongoDB,還曾甩開數(shù)據(jù)庫,直接用csv文件讀寫,但速度都還不夠快!直到后來學(xué)習(xí)到可以將數(shù)據(jù)緩存到內(nèi)存里,那速度可真的快得不要不要滴!redis內(nèi)存數(shù)據(jù)庫的訪問速度比mysql快了不止一個等量級,速度勝過各種數(shù)據(jù)庫(沒有了IO的開銷,直接在內(nèi)存里讀寫)。但是本地電腦受網(wǎng)速的影響,甚至偶爾停電的影響,總歸不是最佳解決方案。

直到我發(fā)現(xiàn)了天翼云電腦,4核8G,80G硬盤,一年才558。關(guān)鍵是網(wǎng)絡(luò)穩(wěn)定,24小時在線,更不用擔(dān)心停電的問題。但這個配置再裝redis就比較坑了,那么有沒有一種既可以享受內(nèi)存數(shù)據(jù)庫的快速,又不用太消耗資源的辦法呢?

答案是:有!

廢話不多說,上方案。


一、LMDB 是什么?

LMDB 全稱為 Lightning Memory-Mapped Database,就是非??斓膬?nèi)存映射型數(shù)據(jù)庫,是一個key-value數(shù)據(jù)庫(鍵值型數(shù)據(jù)庫,同redis)。

redis之所以快首先就是得益于其內(nèi)存讀取,免去了磁盤的IO消耗。

LMDB效率高的一個關(guān)鍵原因是它是基于內(nèi)存映射的,這意味著它返回指向鍵和值的內(nèi)存地址的指針,而不需要像大多數(shù)其他數(shù)據(jù)庫那樣復(fù)制內(nèi)存中的任何內(nèi)容。

二、LMDB 怎么用?

1. 準備工作

讀寫行情,離不開pandas包,這個怎么安裝自己百度,下面才是我們的主角。使用如下語句安裝lmdb包,通過一個包加載一個文件就可以將本地數(shù)據(jù)轉(zhuǎn)移到內(nèi)存中讀寫,免去了redis安裝的繁瑣。

pip install lmdb

2. 基本常識

LMDB屬于key-value數(shù)據(jù)庫,而不是關(guān)系型數(shù)據(jù)庫( 比如 MySQL ),LMDB提供 key-value 存儲,其中每個鍵值對都可以存一只股票行情。LMDB的主要作用是提供數(shù)據(jù)管理,可以將各種各樣的原始數(shù)據(jù)轉(zhuǎn)換為統(tǒng)一的key-value存儲。

2.1 LMDB 的基本函數(shù)

env = lmdb.open() # 創(chuàng)建 lmdb 環(huán)境
txn = env.begin() # 建立事務(wù)
txn.put(key, value) # 進行插入和修改
txn.delete(key) # 進行刪除
txn.get(key) # 通過查詢value
txn.cursor() # 整個db進行遍歷
txn.commit() # 提交更改
env.close()  # 關(guān)閉環(huán)境

2.2 重點解釋

env = lmdb.open(lmdb_path, map_size=1099511627776)
lmdb_path 指定存放生成的lmdb數(shù)據(jù)庫的文件夾路徑,如果沒有該文件夾則自動創(chuàng)建。map_size 指定創(chuàng)建的新數(shù)據(jù)庫所需磁盤空間的最小值,1099511627776B=1T??梢栽谶@里進行 存儲單位換算。
創(chuàng)建環(huán)境會在指定路徑下創(chuàng)建 data.mdb 和 lock.mdb 兩個文件,一是個數(shù)據(jù)文件,一個是鎖文件。

先創(chuàng)建一個事務(wù)(transaction) 對象 txn,所有的操作都必須經(jīng)過這個事務(wù)對象。因為我們要對數(shù)據(jù)庫進行寫入操作,所以將 write 參數(shù)置為 True,默認其為 False。

使用 .put(key, value) 對數(shù)據(jù)庫進行插入和修改操作,傳入的參數(shù)為鍵值對。

值得注意的是,需要在鍵值字符串后加 .encode() 改變其編碼格式,將 str 轉(zhuǎn)換為 bytes 格式,否則會報該錯誤:TypeError: Won’t implicitly convert Unicode to bytes; use .encode()。在后面使用 .decode() 對其進行解碼得到原數(shù)據(jù)。

使用 .delete(key) 刪除指定鍵值對。

對LMDB的讀寫操作在事務(wù)中執(zhí)行,需要使用 commit 方法提交待處理的事務(wù),如果不提交存在數(shù)據(jù)保存不成功的情況。

每次 commit() 之后都要用 env.begin() 更新 txn(得到最新的lmdb數(shù)據(jù)庫)。

3. 獲取行情并寫入lmdb(代碼展示)

# -*- coding: utf-8 -*-
import lmdb # 安裝:pip install lmdb
import pickle

def lmdb_put(key,value):
    # 添加數(shù)據(jù)和鍵值 
    txn.put(key = key.encode(), value = value)

def lmdb_get(key):
    # get函數(shù)通過鍵值查詢數(shù)據(jù) 
    res = txn.get(key.encode())
    return res

if __name__ == '__main__':
    # 下載Ashare.py、KTstock放到和這個文件相同目錄下
    from Ashare import get_price
    # 獲取股票代碼
    from KTstock import get_stocklist_dfcfw # KTstock詳見文章http://t./59WUP
    res = get_stocklist_dfcfw()
    if res['success']:
        df = res['df_data']
        # 剔除交易量為0的股票
        df = df[df['volume']>0]
        # # 只篩選特定代碼的數(shù)據(jù)
        # lists = ['000001','300750','600259']
        # if len(lists)>0:
            # df = df[df['code'].isin(lists)]
        code_list = df['code'].tolist()
        print(code_list)
        
    import time
    # 計時開始
    time1 = time.time()

    if 1:
        # 建立lmdb環(huán)境
        env = lmdb.open('./stock', map_size=int(1e9)) 
        # 參數(shù)write設(shè)置為True才可以寫入
        txn = env.begin(write=True)
        # 遍歷并lmdb數(shù)據(jù)庫
        count = 0
        for key, value in txn.cursor():
            count+=1
            print(count,'lmdb現(xiàn)有鍵',key)
            if 1:
                # 讀取鍵值
                df_bytes_from_lmdb = txn.get(key)
                if not df_bytes_from_lmdb is None:
                    df = pickle.loads(df_bytes_from_lmdb)
                    print('提取lmdb現(xiàn)有鍵值到df',df.tail(5))
            if 1:
                # 刪除鍵值
                txn.delete(key)
                print('lmdb現(xiàn)有鍵',key,'已刪除')
        txn.commit()
        time2 = time.time()
        print('Python遍歷lmdb耗時:',time2-time1,'秒')    

    if 1:
        # 建立lmdb環(huán)境
        env = lmdb.open('./stock', map_size=int(1e9)) 
        # 參數(shù)write設(shè)置為True才可以寫入
        txn = env.begin(write=True) 
        # 提取行情并寫入lmdb
        for code in code_list:
            if code[0] == '6' or code[0] == '9':  #上證股票
                code = 'sh'+code
            if code[0] == '0' or code[0] == '3' or code[0] == '2':  #深證股票
                code = 'sz'+code
            if code[0] == '4' or code[0] == '8':  #北證股票
                code = 'bj'+code
            print('通過Ashare獲取股票('+code+')行情...') 
            
            try:
                df_data=get_price(code,frequency='1d',count=1000)      #支持'1d'日, '1w'周, '1M'月 
            except Exception as e:
                print('獲取股票('+code+')行情失?。?)
            else:
                df_bytes = pickle.dumps(df)
                lmdb_put(code, df_bytes)

        time3 = time.time()
        print('Python寫入lmdb耗時:',time3-time2,'秒')

    # 關(guān)閉lmdb環(huán)境
    env.close()
        
    # 計時結(jié)束    
    time_end = time.time()
    print('Python讀取lmdb總耗時:',time_end-time1,'秒')


這里為方便演示使用單文件的Ashare,但經(jīng)常獲取數(shù)據(jù)會失敗,所有增加了try語句保護運行完整。獲取行情數(shù)據(jù)更多方法請參考:
【數(shù)據(jù)知多少】一文學(xué)懂通過Tushare、AKshare、baostock、Ashare、Pytdx獲取股票行情數(shù)據(jù)(含代碼)https://blog.csdn.net/popboy29/article/details/125815775


總結(jié)

筆者使用自制采集方法,并寫入和便利lmdb,結(jié)果如下:
Python采集所有股票(D)并寫入到lmdb耗時: 480.66940474510193 秒
Python遍歷讀取lmdb共耗時: 4.877945423126221 秒

注:寫入因為包含采集時間所以長一些,但讀取非???,不進行任何計算,不到5秒就可以遍歷5000多支股票。

至此,使用LMDB存取股票行情已展示完畢,更多玩法各位自行探索。有好消息或疑問請評論區(qū)留言,我看到會回復(fù)大家。

附:常見錯誤及解決辦法

  1. 錯誤提示:lmdb.MapFullError: mdb_put: MDB_MAP_FULL: Environment mapsize limit reached
    解決方法: lmdb.open(“./stock”, map_size=int(1e9)

  2. 錯誤提示:TypeError: Won’t implicitly convert Unicode to bytes; use .encode()
    解決方法: TypeError:不會隱式地將Unicode轉(zhuǎn)換為字節(jié),對字符串部分,進行.encode()

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多