在上一次https://www.cnblogs.com/webor2006/p/15164775.html學習了使用JQData來查詢股票相關數據, 這次則開始一點點構建咱們的量化交易系統了。
量化交易平台功能模塊了解:
對於一個量化交易平台,它主要包含如下功能模塊:
而整個模塊基本都是基於后端來開發的,只有圖表可視化是用一個可視化庫來打造,並未涉及到前端的功能,核心主要是來學習業務邏輯:
獲取股票數據:
新建readme:
在咱們的工程中新建一個Readme文件用來進行功能的描述:
#DeltaTrader ## 功能模塊 ### 行情記錄 (data) #### stock.py - 獲取所有A股股票列表 - 獲取單個股票行情數據 - 導出股票行情數據 - 轉換股票行情周期 - 獲得單個股票財務指標 - 獲取單個股票估值指標 ### 策略開發 ### 自動交易
三大塊,其中目前主要關注獲取行情記錄數據這塊,所以咱們的包名是data:
而其它模塊會隨着學習的不斷深入再慢慢填充,其實對於這些數據的獲取在上一次咱們已經都試驗過了,只是沒有將其封裝成一個通用的API,所以接下來則會基於咱們上次實現的代碼進行一個抽取,來實現這里所列出的具體API。
獲取所有A股股票列表:
這個在之前咱們實現的代碼為:
咱們直接基於它來改一下:
獲取單個股票行情數據:
先看一下咱們之前實現的:
定義一下:
導出股票相關數據:
原來咱們在獲取股票財務指標時用過:
咱們來封裝一下:
其中這里新建一個price目錄,專門用來存儲股票價格相關的文件:
如果要導出其它類別的數據,新建相關目錄,在調用此函數時傳相關的type名稱既可。
轉換股票行情周期:
先來看一下之前咱們使用的:
下面基於它來封裝一下:
獲取單個股票財務指標:
之前的代碼:
封裝一下:
獲取單個股票估值指標:
封裝為:
調用stock:
接下來咱們則來調用一下咱們重新封裝的stock。
1、新建一個測試模塊:
為了規范,這里將測試相關的代碼都放到另一個模塊中:
2、獲取股票行情數據並導入csv:
控制台輸出:
看一下表格文件有木有生成?
但是!!!你會發現表格中表頭部分少了一個日期:
這個時候是需要咱們來處理一下的,如果不處理在未來的cvs的讀取數據時是會有問題的,這個問題是啥呢?下面在stock中先增加一個cvs的讀取函數,讀出來自然就明白了:
咱們來調用讀取一下:
接下來就來修復它,需要在導出函數那塊進行處理了,如何處理呢,此時則需要對數據的索引進行重命名了,如下:
再重新導出,再獲取:
計算交易指標:
接下來就來學習跟咱們炒股操作息息相關的一些東東啦~~
使用shift函數計算漲跌幅:
每日漲跌幅:
先來計算一下每日的漲跌幅,看是否准確,先來明確一下漲跌幅的計算公式:“(當期收盤價-前期收盤價)/ 前期收盤價” ,
其中的當期可以是當天、當分鍾、當秒鍾、當周、當月,具體實現如下:
其中shift(1)表示上一行的數據,而shift(2)表示上兩行的數據,shift(-1)表示下一行的數據。
下面來調用驗證一下是否准確,這里還是以平安銀行的日K數據為例:
運行看一下:
/Users/xiongwei/opt/anaconda3/bin/python3.8 /Users/xiongwei/Documents/workspace/python/QuantitativeTrading/studycode/example/stock.py auth success open close high low volume money 2021-09-01 17.48 17.88 17.92 17.01 231689409.0 4.046284e+09 2021-09-02 18.00 18.40 18.78 17.80 242260354.0 4.454545e+09 2021-09-03 18.50 18.04 18.50 17.70 139481871.0 2.523273e+09 2021-09-06 17.93 18.45 18.60 17.78 151522556.0 2.780281e+09 2021-09-07 18.60 19.24 19.56 18.35 162234416.0 3.067366e+09 open close high low volume money close_pct 2021-09-01 17.48 17.88 17.92 17.01 231689409.0 4.046284e+09 NaN 2021-09-02 18.00 18.40 18.78 17.80 242260354.0 4.454545e+09 0.029083 2021-09-03 18.50 18.04 18.50 17.70 139481871.0 2.523273e+09 -0.019565 2021-09-06 17.93 18.45 18.60 17.78 151522556.0 2.780281e+09 0.022727 2021-09-07 18.60 19.24 19.56 18.35 162234416.0 3.067366e+09 0.042818 Process finished with exit code 0
接下來就要來驗證咱們計算漲幅的正確性了,咱們這里來挑兩天的驗證一下既可,比如我們挑這兩天的:
回到交易平台中咱們來看一下:
完成正確。
每周漲跌幅:
為了進一步驗證准確性,這里再來看一下周K的漲跌幅,那首先咱們需要將日K的數據轉換為周K的數據對吧,該功能我們已經封裝好了,直接調用既可:
好,獲取了三周的數據,回到同花順軟件里確認一下准確性:
為啥咱們打印的日期是顯示的2021-09-05呢?
其實我們打印的是一周的最后一天:
而股票軟件里是算到工作日的最后一天,關於這個細節就不過多較真的,總之漲幅結果對相同的,再來看一天的:
模擬股票交易:
買入、賣出信號:
對於股票操作最頻繁的就是買入和賣出操作對吧,所以接下來會以一個簡單的策略來對股票生成買賣信息,注意:由於這階段還在打基礎,還沒有學習各種選股策略的應用這塊,所以這里的策略是很呆板的,比如可能就是定就是周一賣,周四買之類的,重點是能讓我們程序按預期來生成買賣信號。
1、新建Strategy模塊:
由於未來會有很多的一些策略,所以這里新建一個包名:
2、創建周期選股策略:
好,接下來則來創建一個非常簡單按周期選股的策略:周四買入、周一賣出【有過炒股經歷的應該也能感受到這個規則,也有點用吧,因為基本上到了周四就開始陰了,而周一作為一周的開始往往勢頭比較旺~~】
""" 用來創建交易策略、生成交易信號 """ import data.stock as st import numpy as np def week_peroid_strategy(code, time_freq, start_date, end_date): data = st.get_single_price(code, time_freq, start_date, end_date) # 新建周期字段,周一是從0開始 data['weekday'] = data['date'].weekday # 周四買入,下面代碼的意思是如果是周四,則是1表示買,0表示不買 data['buy_signal'] = np.where((data['weekday'] == 3), 1, 0) # 周一賣出,下面代碼的意思是如果是周一,則是-1表示賣,0表示不賣 data['sell_signal'] = np.where((data['weekday'] == 0), -1, 0) return data if __name__ == '__main__': data = week_peroid_strategy(code='000001.XSHE', time_freq='daily', start_date='2021-08-25', end_date='2021-09-08') print(data)
下面運行看一下,報錯了:
原因是咱們的索引列木有給它重命名,沒有date這一列,當時我們重命名只是在導出函數中加了:
好,那修改一下程序:
為了打印看得更加清楚,這里將字段過濾一下:
3、信號整合:
接下來想一個場景,就是有可能周四你買入,周五又有可能買入對吧,那連續兩天買入,在實際做策略時會經常碰到這種重復的信號的,所以這里針對這樣的場景模擬一下:
那針對這樣的數據,咱們得想辦法只讓第一天買入為1,其它的買入都變為0,其實也很簡單,如下:
此時又用到了shift()函數了對吧,不過這里還是會有些沒覆蓋的場景,這里將獲取數據的時間改一下就能看到問題之所在了:
好,此時咱們把整合的那句代碼又打開注釋,再運行,你會發現:
這里需要改一下條件了,如下:
當然對於賣出信號也需要整合一下,因為也有可能出現重復賣出的情況,如下:
4、最終生成買賣信號:
目前buy_signal和sell_singal,在同一天只可能有一種signal,要么是買,要么是賣對么?所以這里再加一個字段,用來做買賣的直觀判斷,不然我看兩個字段有點暈,如下:
計算持倉收益:
了解:
先來看一下股票軟件中持倉收益一般包含的數據內容:
其中咱們需要了解的有如下幾個計算公式:
1、總盈虧= (市價 - 成本價) * 股數;
也就是表格中這一列的值:
2、 浮動盈虧比 = (市價 - 成本價) / 成本價;
也就是表格中的這一列的值,我們平常最關心的收益率:
比如7.377,通常也叫掙了7.3個點,其實還有一個更加簡便的計算方法,比如盈利的情況下,直接拿市價/成本價,然后肉眼就可以看到收益率了,比如表格中的新城股份:
咱們來除一下:
然后用它再減去1,此時就可以看到百分收益率就是7.377啦,如果對於虧損的情況也類似,用價格高的除以價格低的就成。
3、成本價 = 買入金額 / 持有股數
因為有可能在實際會分倉進行買入,所以成本價計算就是如上公式了。
4、股數 = 累計買入股數
這個就比較簡單了,通常一手是100股。
以上數據不用太過關心,因為股票軟件里持倉記錄那塊都能一眼看到,對於我們來說只要知道如何查詢出這些數據就可以了, 其中重點是關注如何計算持倉收益率,這個是評價一個策略是否掙錢最直接的一個指標。
實現:
1、收益率計算:
接下來咱們基於上面的買賣信號數據來計算一下每次的收益率,先分析一下咱們的數據:
發現在買和賣之間的天數肯定是不一定的,短線的周期短,中長線的周期長,而對於計算收益率來說,只關心買入和賣出那兩天的價格對吧?所以,我們可以為了方便計算的收益率,把買和賣中間持股的記錄都給刪掉,只保留買和賣那兩天的數據,這樣計算的話就比較方便了,所以基於這樣的思路咱們來寫一下邏輯:
接下來計算收益率為:
其中發現,對於買的情況很顯然是不需要顯示收益率的,所以這里再過濾一下:
2、獲取上市以來所有數據:
目前咱們只獲取了二個月的數據,那如果我想獲取單支股票自上市以來的所有數據來進一步觀察咱們策略的有效性呢?此時可以回到JQData的官網找一下如何獲得上市以來的數據,其實就是在獲取股票單個行情數據函數中的start_date傳上市的時間既可:
那上官網查一下:
好,咱們來改一下:
此時咱們在調用時就可以傳None了:
其中需要導一下datetime:
3、重構week_peroid_strategy函數:
目前對於week_peroid_strategy可以稍加優化一下代碼,因為看着有點亂,就是把這倆邏輯抽離出去:
說不定未來還能在其它的功能上使用上,如下:
這樣,整個邏輯就變得清爽了:
這個思想不管是用啥語言寫程序都是應該我們來進行考慮的,因為可以大大增加代碼的可讀性。
另外目前咱們代碼這有個報警:
是因為在main中也是用的data:
這里將它改一下名稱既可:
4、打印一下數據的均值:describe()
對於平安銀行自上市至今的數據太多了,可以用下面來查看一下均值,對整體數據有一個大概的了解:
關於各參數的函數可以參考:
3、將其可視化輸出:plot()
接下來咱們也可以使用plot()函數來將整個的收益率情況可視化一下,如下:
是不是似曾相識,是的,在之前線性代數的學習https://www.cnblogs.com/webor2006/p/14271706.html中也使用到了該可視化庫了,這也就是知識的關聯系,學習的任何一個知識在未來的某一個時刻一定還能出現它的身影,並不是完全孤立的,好,運行,一個可視化的界面就出現了:
計算累計收益率:
概述:
現在我們已經能計算出一支股票的單次的收益率對吧?那如何根據單次收益率來計算出一個累計收益率呢?比如你支付寶中的基金【關於基金的學習,也是今年的一個計划,將來得實際行動起來】,通常在這個頁面會展示累計收益率:
那你有沒有搞清楚這個值是如何計算出來的呢?這里正好可以彌補一下這個知識,假如你買的一個基金,整體的收益情況如下:
那累計收益的計算,咱們口算一下,算完之后你就會發現公式出來了,公式有了那們咱們的程序就有戲了:
這個比較簡單對吧,也就是原來100塊錢,經過一天之后的收益利息,掙了3塊錢,總金額變成了103了,好,接着算第二天的累計收益了:
接下來再把剩下的兩天算出來,你就會發現公式了:
其中“(1 + 當天收益率)的累積乘積 ”,其實就是一個通用計算累計收益率的公式【一定要注意累計收益和累計收益率,累計收益是有本金在里面,而累計收益率是不包含本金的,就是一個比例嘛,這一點必須要搞清楚】,但是!!!它還是有問題的,為啥呢?那咱們以這個公式來算一下圖中第一天的累積收益就知道了,很明顯第一天的累計收益率就是3%,因為只有一天嘛,但是如果以"(1 + 3%)"來算,很明顯它的收益率就為103%,需要將本金去掉,也就是累計收益率的公式為“(1 + 當天收益率)的累積乘積 - 1”,(1 + 3%) - 1 是不是收益率就是3%了?
實現:
接下來則回到python的世界,來計算一下平安銀行的累計收益率:
接下來咱們來調用一下:
而看一下可視化的累積收益率的圖:
從這個收益曲線圖來看,貌似周四買入,周一賣出的簡單策略,收益率還是蠻不錯的嘛,當然啦,不可能按這么簡單的規則來炒股的,純學習。
最后在寫python時有一個小的細節這里提一下,就是在計算累計收益率不是用到了這個函數嘛:
但是!!!你這個函數完全得要你手動敲全,不能智能的提示對吧?其實不提示的原因也很簡單,原因是由於:
不知道它加出來的是DataFrame,所以當然也就不知道給你提示它里面的函數嘍,要想解決這個提示問題,咱們可以顯示的指定這數據是DataFrame既可,如下:
此時再輸入時就可以看到提示了: