歡迎你們訂閱《教你用 Python 進階量化交易》專欄!爲了可以提供給你們更輕鬆的學習過程,筆者在專欄內容以外已陸續推出一些手記來輔助同窗們學習本專欄內容,目前已推出以下擴展篇:app
在第一篇《管理機率==理性交易》中筆者結合一個簡單的市場模型介紹了爲何在沒有機率優點的前提下參與交易會虧錢,其實股票交易和玩一個遊戲、作一個項目理念是相通的,須要章法、須要制定策略,不然就和拋硬幣賭博同樣同樣的,用量化交易能夠幫助咱們管理好幾率,更理性的去下單。函數
在第二篇《線性迴歸擬合股價沉浮》中筆者在專欄《股票交易策略開發:走勢線性迴歸選股策略》小節的基礎上對線性迴歸方法的策略應用作進一步的擴展介紹。因爲線性迴歸做用於股票收盤價的整個週期,先後兩段徹底相反的週期會彼此做用,最終影響擬合的角度值,因而筆者設定窗口期用移動窗口的方式擬合股價的走勢,尋找角度曲線的拐點以預示新一輪的反轉走勢,給你們提供一個衍生的策略思路。工具
在第三篇《最大回撤評價策略風險》中筆者在專欄
《股票交易數據可視化:買賣區間下策略收益繪製》的基礎上對策略的最大回撤指標作必定的擴展介紹。投資是有風險的,那麼如何去衡量這個風險呢?最大回撤率就是一種直觀的將風險切實量化的指標,它描述了買入股票後,在策略出現最糟糕的狀況下會損失多少錢,這也直接關係到了風險策略停止損因子的設定。學習
在第四篇《尋找最優化策略參數》中筆者在專欄
《股票交易策略開發:趨勢突破擇時策略》的基礎上對尋找最優化策略參數的方法作一些擴展介紹。對於尋找最優化參數的方法能夠選擇枚舉法或者蒙特卡洛法。枚舉法適用於解決效率要求不高,樣本規模小的問題。蒙特卡洛法獲得的結果並不必定是最優的,可是在大規模樣品的場合下能夠更快地找到近似最優結果。測試
在第五篇《標記A股市場漲跌週期》中筆者在專欄
《股票交易數據的自動下載》的基礎上對matplotlib繪圖工具的使用方法作一些擴展介紹,在A股歷史走勢圖中標記出市場漲跌週期。優化
在第六篇《Tushare Pro接口介紹》中筆者在《股票交易數據的自動下載》的基礎上擴展介紹使用Tushare Pro版本獲取財經和股票交易數據的方法。ui
本次場外篇筆者在《用裝飾器註冊股票池》的基礎上對裝飾器的進行擴展介紹,經過裝飾器方式實現timeit測試函數執行時間功能。spa
當咱們對一個函數或若干語句的執行時間要求較高時,但願可以測試獲得該代碼的開銷時間是多少。好比遍歷計算每一個交易日股票數據的價格波動,以下所示:code
df_sh['diff_price'] = df_sh.apply(lambda row: (row['high']-row['low']), axis =1) """ date open close ... volume code diff_price 0 2018-01-02 3314.03 3348.33 ... 202278860.0 sh 35.02 1 2018-01-03 3347.74 3369.11 ... 213836149.0 sh 34.63 2 2018-01-04 3371.00 3385.71 ... 206955288.0 sh 27.53 3 2018-01-05 3386.46 3391.75 ... 213060681.0 sh 21.82 4 2018-01-08 3391.55 3409.48 ... 236165106.0 sh 28.17 """
最早想到的是採用Python內置time模塊來測試代碼運行時間,使用時導入模塊,以下所示:token
import time
其中time.perf_counter()和time.process_time() 能夠實現咱們的需求。
time.perf_counter():返回計時器的精準時間(系統的運行時間),包含整個系統的睡眠時間。因爲返回值的基準點是未定義的,因此,只有連續調用的結果之間的差纔是有效的。
time.process_time() :返回當前進程執行 CPU 的時間總和,不包含睡眠時間。因爲返回值的基準點是未定義的,因此,只有連續調用的結果之間的差纔是有效的。
此處使用time.perf_counter()方法實現,以下所示:
start = time.perf_counter() #運行的程序 df_sh['diff_price'] = df_sh.apply(lambda row: (row['high']-row['low']), axis =1) #運行的程序 elapsed = (time.perf_counter() - start) print("Time used:",elapsed) # Time used: 0.00546605699999958
另外可使用Python更強大的計時庫timeit測試執行時間,使用時導入timeit,以下所示:
from timeit import timeit
用timeit()測試一個函數的執行時間可按timeit(函數名_字符串,運行環境_字符串,number=運行次數)這個方式來實現,以下所示:
elapsed = timeit('func()', 'from __main__ import func', number=1) print("Time used:",elapsed) #Time used: 0.005263746999999985
因爲電腦永遠都有其餘程序也在佔用着資源,所以咱們的程序不可能最高效地執行該函數。因此通常會進行屢次試驗,取最少的執行時間爲真正的執行時間。此時可使用repeat方法,使用時須要導入repeat ,以下所示:
from timeit import repeat
使用repeat方法和timeit用法類似,多了一個repeat參數,表示重複測試的次數(默認值爲3),返回值爲一個時間的列表,以下所示:
t_elapsed = repeat('func()', 'from __main__ import func', number=1, repeat=5) print("Time used:",t_elapsed) print("Time of min used:",min(t_elapsed)) #Time used: [0.005254602999999136, 0.005191876999999678, 0.00529747400000069, 0.005051099999999309, 0.005246075000000516] #Time of min used: 0.005051099999999309
回到本篇的主題內容——裝飾器,裝飾器顧名思義就是「裝飾」其餘函數,爲其餘函數添加附加功能,原則上不能修改被裝飾的函數的代碼,也不能修改被裝飾的函數的調用方式,這也是裝飾器「厲害」的地方。
以上不管使用time,仍是timeit,都須要對被測函數添加實現的代碼,那麼嘗試使用裝飾器來解決這個痛點,以下所示:
def timeit(func): def wrapper(*args, **kwargs): start = time.perf_counter() func(*args, **kwargs) elapsed = (time.perf_counter() - start) print("Time used: %s ", elapsed) return wrapper @timeit def func_diff(): df_sh['diff_price'] = df_sh.apply(lambda row: (row['high']-row['low']), axis =1) # timeit(func_diff)() func_diff()#Time used: %s 0.005055395000000074
關於裝飾器的基礎原理介紹,你們能夠參閱專欄中的講解,此處再次提煉下其關鍵點。執行func_diff()等價於執行timeit(func_diff)(),把函數做爲一個變量傳遞給timeit函數, 此處timeit爲二層嵌套的高階函數。電動叉車
假如要像timeit模塊那樣有repeat、number參數擴展測試的需求,能夠再進一步升級下裝飾器,以下所示:
# use decorator method with parameter def timer_para(number = 3, repeat = 3): def decorator(func): def wrapper(*args, **kwargs): for i in range(repeat): start = time.perf_counter() for _ in range(number): func(*args, **kwargs) elapsed = (time.perf_counter() - start) print("Time of %s used: %s ", i, elapsed) return wrapper return decorator @timer_para(number = 2, repeat = 2) def func_diff_par(): df_sh['diff_price'] = df_sh.apply(lambda row: (row['high']-row['low']), axis =1) # timer_para(number = 2, repeat = 2)(func_diff_par)() func_diff_par() #Time of %s used: %s 0 0.01577713199999997 #Time of %s used: %s 1 0.015201944999999939
執行func_diff_par ()等價於執行timer_para(number = 2)(func_diff_par)(),此處timeit爲三層嵌套的高階函數,第1層是timer_para(number = 2),返回第2層decorator函數,第2層函數decorator接受func_diff_par函數做爲參數進行調用,返回第三層函數wrapper。如此一來只須要使用@xxxx就能夠一勞永逸地對全部函數測試執行時間了!!!