python量化投资培训清华大学深研院_GitHub - CatsJuice/quantitative-investment-learning: 使用Python进行量化投资的学习报告...
quantitative-investment-learning
使用Python進行量化投資的學習報告
Python量化投資學習報告
CatsJuice 編輯于 2019-4-26
上一次更新: 2019-05-21 16:04
CONTENTS:
1. 數據抓取
1.1. 通過第三方數據平臺直接調用api
1.1.1. TuShare(挖地兔)
1.1.1.1. A. 概述
Tushare是一個免費、開源的python財經數據接口包。主要實現對股票等金融數據從數據采集、清洗加工 到數據存儲的過程,能夠為金融分析人員提供快速、整潔、和多樣的便于分析的數據,為他們在數據獲取方面極大地減輕工作量,使他們更加專注于策略和模型的研究與實現上。
1.1.1.2. B. 基本使用
使用前提:
安裝Python
安裝Pandas, lxml
下載安裝:
使用:
以歷史行情接口為例, 如下調用:
import tushare as ts
ts.get_hist_data('600848') #一次性獲取全部日k線數據
結果顯示:
Tushare返回的絕大部分的數據格式都是pandas DataFrame類型,非常便于用pandas / NumPy / Matplotlib進行數據分析和可視化
1.1.2. 其他平臺
1.1.2.1. Win.d
中國市場的精準金融數據服務供應商,為量化投資與各類金融業務系統提供準確、及時、完整的落地數據,內容涵蓋
股票、債券、基金、衍生品、指數、宏觀行業等各類金融市場數據,助您運籌帷幄,決勝千里
1.1.2.2. 優礦
提供各類資產的財務、因子、主題、宏觀行業特色大數據,以及量化場景下的PIT數據,保障量化過程不引入未來數據。
股票、期貨、指數、場內外基金等多資產多策略回測。豐富的衍生工具,保證多因子策略、事件驅動等快速實現。
1.2. 使用爬蟲抓取
手動寫爬蟲進行抓取, 首先需要確定數據來源, 較為主流的財經數據平臺有新浪財經, 東方財富, 網易財經等。 接下來針對若干平臺, 分析抓取的過程以及可能遇到的問題;
1.2.1. 新浪財經
1.2.1.1. 獲取所有股票代碼(這里只考慮滬深A股)
新浪財經的數據地址為http://vip.stock.finance.sina.com.cn/q/go.php/vIR_CustomSearch/index.phtml, 通過切換頁面可以發現, 地址欄的url的參數發生了變化,變化的規律為:p=n, n為當前頁碼, 而股票代碼和股票名稱, 通過F12打開開發者工具, 表單中的值均可定位到, 應該可以通過爬蟲抓取所有股票的基本信息, 通過新浪財經抓取股票的代碼我這里沒有寫, 暫先略過;
1.2.1.2. 獲取日線數據
在新浪財經數據中心, 我并沒有找到交易數據的表格, 要查看某只股票的日線 / 分線只能通過點擊某只股票進到詳情頁(而url中的股票代碼對應著不同的股票), 而新浪財經的數據并非以表格的形式展示, 而是通過圖表展示, 鼠標移動時通過JavaScript更新日期及當前股票的數據信息;那么直接抓取網頁是無法獲取到數據的, 現在的思路是分析js文件, 找到鼠標移動時監聽到的事件, 查明js是如何更新數據的, js的數據從哪里提取,以此來抓取信息;
首先, 通過頁面元素審查可以發現, 分時線是通過HTML5的canvas繪制的, 在Sources找相關的js文件, 可以找到paintSth.js文件, 由于在鼠標移動時會更新頁面元素, 所以可以直接在文件中查找mousemove, 找到了相關代碼如下:
C = this.interactCanvas,
...
...
...
C.addEventListener("mousemove", o),
可以看到Canvas加了一個mousemove的監聽器, 執行o, 再查找o(), 可以找到如下代碼:
function o() {
if (!c) {
var t = document.createElement("canvas");
c = t.getContext("2d")
}
return c
}
這里c又是一個未知量, 所以應該繼續檢索c的信息, 由于關聯的js文件較多, 這種做法過于費時費力, 爬取新浪財經的交易數據應該不是明智的選擇;
換種思路, 既然可以瀏覽, 那么使用 selenium 就有可能, selenium 可以進行自動化測試, 讓鼠標在固定位置移動, 同時抓取更新的信息, 這種做法是可行的, 我也嘗試著做了, 由于這一方法過于不實用, 源碼略;
這種做法局限性太大, 首先, 效率過低, 這是很致命的一個缺陷, 除此之外, 由于移動導致的像素不同, 可能會出現數據遺漏或重復;最后, 新浪財經默認只顯示一定日期的交易數據, 要查看更早的需要手動拖動進度條, 這就使得selenium的操作誤差更大;
綜上所述, 在新浪抓取交易數據是挺不容易的事情;
1.2.1.3. 獲取財務數據
新浪的財務數據在http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/profit/index.phtml?p=1, 這應該是爬蟲抓取中喜聞樂見的格式了,換頁不需要通過ajax, 所以抓取的時候只需要設定好抓取的總頁數, 循環抓取頁面再解析即可, 接下來即可直接進行代碼的編寫(未親自驗證)
1.2.2. 網易財經
1.2.2.1. 獲取所有股票代碼(這里只考慮滬深A股)
網易財經的所有滬深A股數據位于http://quotes.money.163.com/old/#query=EQA&DataType=HS_RANK&sort=PERCENT&order=desc&count=24&page=0, 從url來看, 換頁通過url傳參即可改變了, 但是實際操作可以發現, 點擊換頁時url中的page并不會改變, 但改變url中的page參數, 當前頁面序號會改變;但這不意味著可以像1.2.1. 新浪財經中爬取新浪財經一樣, 枚舉url的page參數來爬取所有信息;因為股票數據是異步加載的, 直接抓取無法獲取到值;
對于網易財經,由于點擊換頁時頁面的url沒有更新,所以應該是使用了Ajax或Js來更新數據, 通過F12調起開發者工具, 在Network選型卡中, 篩選XHR, 每當點擊換頁時, 就會有新的XHR, 分析這些XHR的url可以發現,只有page值在改變:
直接復制Request URL并使用瀏覽器訪問, 可以得到json格式的數據, 但是可以看到中文通過Unicode編碼了, 在獲取后, 可以通過s.decode('unicode_escape')來解碼;接下來就是對json解析并提取需要的信息了, json格式如下:
在list中有[0]到[23]共24條數據, 對應請求中的參數count=24, 關于字段名的解釋, 以下為我的分析:
No.
key_name
meaning
1
ANNOUNMT
公告信息,并非必須,對應在頁面中有公告標簽的股票才有這個字段
2
CODE
股票代碼
3
FIVE_MINUTE
5分鐘漲跌額
4
HIGH
最高
5
HS
換手率(不帶%)
6
LB
量比
7
LOW
最低
8
MCAP
流通市值
9
MFRATIO
list, 包含2個值MFRATIO2:凈利潤, MFRATIO10: 主營收
10
MFSUM
每股收益
11
NAME
名稱
12
NO
網易財經中的編號
13
OPEN
今日開盤
14
PE
市盈率
15
PERCENT
漲跌幅
16
PRICE
價格
17
TCAP
總市值
18
TURNOVER
成交額
19
VOLUME
成交量
20
WB
委比
所以可以直接抓取這個url來獲取相關的數據, 更有趣的是, 請求參數中有個count參數, 決定了數據的數量, 所以我嘗試將count設置成全部數量, 查看網易財經滬深A股, 網易的編號最后一只為3607, 所以如下請求:
'http://quotes.money.163.com/hs/service/diyrank.php?host=http%3A%2F%2Fquotes.money.163.com%2Fhs%2Fservice%2Fdiyrank.php&page=0&query=STYPE%3AEQA&fields=NO%2CSYMBOL%2CNAME%2CPRICE%2CPERCENT%2CUPDOWN%2CFIVE_MINUTE%2COPEN%2CYESTCLOSE%2CHIGH%2CLOW%2CVOLUME%2CTURNOVER%2CHS%2CLB%2CWB%2CZF%2CPE%2CMCAP%2CTCAP%2CMFSUM%2CMFRATIO.MFRATIO2%2CMFRATIO.MFRATIO10%2CSNAME%2CCODE%2CANNOUNMT%2CUVSNEWS&sort=PERCENT&order=desc&count=3607&type=query'
即可返回所有json格式的數據, 然后再進行解析, 并寫入文件, 完整代碼如下:
import urllib
import json
import csv
from tqdm import tqdm
# 下載器
class Downloader(object):
def __init__(self, url):
self.url = url
def download(self):
html_content = urllib.request.urlopen(self.url).read()
html_content = html_content.decode("utf-8")
return html_content
# 調度器
class Controller(object):
def __init__(self):
self.downloader = None
self.parser = None
self.saver = None
def get_data(self):
url = "http://quotes.money.163.com/hs/service/diyrank.php?host=http%3A%2F%2Fquotes.money.163.com%2Fhs%2Fservice%2Fdiyrank.php&page=0&query=STYPE%3AEQA&fields=NO%2CSYMBOL%2CNAME%2CPRICE%2CPERCENT%2CUPDOWN%2CFIVE_MINUTE%2COPEN%2CYESTCLOSE%2CHIGH%2CLOW%2CVOLUME%2CTURNOVER%2CHS%2CLB%2CWB%2CZF%2CPE%2CMCAP%2CTCAP%2CMFSUM%2CMFRATIO.MFRATIO2%2CMFRATIO.MFRATIO10%2CSNAME%2CCODE%2CANNOUNMT%2CUVSNEWS&sort=PERCENT&order=desc&count=3607&type=query"
html_content = urllib.request.urlopen(url).read()
# 這時候解碼可能導致json解析錯誤!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# html_content = html_content.decode("unicode_escape")
# 當數量大于2995會報錯, json解析失敗, 原因是編號為2996的股票, 在公告中嵌套了引號導致json解析失敗
data = json.loads(html_content)
self.saver = Saver(data=data)
self.saver.save()
class Saver(object):
def __init__(self, data):
self.data = data
self.prifix = 'F:\\files\\sharesDatas\\code_list\\' # 存放目錄
def save(self):
# 新建文件, 寫入文件頭
file_header = ["代碼", "名稱", '流通市值', '每股收益', '總市值']
csv_file = open(self.prifix+'滬深A股.csv', 'w', newline='')
writer = csv.writer(csv_file)
writer.writerow(file_header)
list = self.data['list']
sum = len(list)
for i in tqdm(range(0, sum)):
item = list[i]
# 處理股票代碼, 去掉網易財經的0/1前綴, 并使其在Excel中顯示正常(加`)
code = str(item['CODE'])
code = code[1:7] if len(code) == 7 else code[:]
code = "`" + code
row = [code, item['NAME'], item['MCAP'], item['MFSUM'], item['TCAP']]
csv_file = open(self.prifix + '滬深A股.csv', 'a', newline='') # 追加
writer = csv.writer(csv_file)
writer.writerow(row)
if __name__ == '__main__':
controller = Controller()
controller.get_data()
1.2.2.2. 獲取日線數據
網易財經的日線交易數據可在http://quotes.money.163.com/trade/lsjysj_601318.html#06f01查看, 需要將url中的601318替換成相應的股票代碼, 在這個頁面沒有換頁按鈕, 僅顯示若干條數據, 但是在數據表的右上角有個下載數據的鏈接, 點擊后, 需要勾選需要下載的字段, 點擊下載后會下載一個code.csv文件, 所以要做的就是抓取下載的真實url, 按F12打開開發者工具, 點擊下載按鈕后, 在控制臺看到如下提示:
其中已經包含了請求的真實地址, 即:
"http://quotes.money.163.com/service/chddata.html?code=0601318&start=20070301&end=20190426&fields=TCLOSE;HIGH;LOW;TOPEN;LCLOSE;CHG;PCHG;TURNOVER;VOTURNOVER;VATURNOVER;TCAP;MCAP"
而想要搞清楚url的參數, 可以轉到submit的文件, 這里即b.667271.min.js:1, 這是一個壓縮的js文件, 格式化后, 在最后可以找到如下關鍵代碼:
submit: function() {
var e = n.value;
if (e) e = e.replace(/-/g, "");
var a = i.value;
if (a) a = a.replace(/-/g, "");
var o = t.elem.getElementsByTagName("input"),
r = [];
for (var d = 0; d < o.length; d++) {
if (o[d].type == "checkbox" && o[d].checked) {
r.push(o[d].value)
}
}
var c = "/service/chddata.html?code=" + window["STOCKCODE"];
e && /^\d{8}$/.test(e) && (c += "&start=" + e);
a && /^\d{8}$/.test(a) && (c += "&end=" + a);
r.length && (c += "&fields=" + r.join(";"));
location.href = c
}
能夠清楚地看到url的拼接過程, 具體參數如下:
編號
參數
解釋
規范
1
start
起始日期
年月日直接拼接,如20190427,不足2位補0
2
end
截止日期
年月日直接拼接,如20190427,不足2位補0
3
fields
即需要下載的字段
使用;分割,如:TCLOSE;HIGH;LOW;TOPEN;LCLOSE;CHG;PCHG;TURNOVER;VOTURNOVER;VATURNOVER;TCAP;MCAP 具體意義不作詳述
4
code
股票代碼
關于股票代碼, 網易財經的股票代碼在傳參時, 如果是以6開頭的股票, 需要在前面加0, 而以0和3開頭的股票需要在前面加1,如:1000333, 1300001, 0601318
接下來是做抓取, 抓取就是根據已經獲取的股票代碼, 枚舉股票代碼并下載對應的日線數據, 僅需注意每次循環最好使用time.sleep(random.random()*2), 否則可能因操作頻繁被拒絕訪問;下面是部分源代碼(注: 這里源代碼中的股票代碼是實時獲取的, 抓取的是股城網, 因為只需要抓取股票代碼即可):
import random
import time
from stock.classes.Downloader import Downloader
from stock.classes.Parser import Parser
from stock.classes.Saver import Saver
# 控制器
class Controller(object):
# 構造函數
def __init__(self, url, kline_filepath, codelist_filepath, date):
'''
:param url: 股票基本信息url( 股城網>行情>滬深A股 )
:param kline_filepath: 日線數據文件保存路徑
:param codelist_filepath: 股票代碼文件保存路徑
:param date: 查詢截止日期
'''
self.url = url
self.kline_filepath = kline_filepath
self.codelist_filepath = codelist_filepath
self.date = date
self.downloader = None # 下載器實例
self.parser = None # 解析器實例
self.saver = Saver() # 存儲器實例
# 執行函數
def start(self):
page = 1
all_code = []
while page <= 181:
print("當前為第%s頁" % page)
time.sleep(random.random()*2)
self.downloader = Downloader(self.url % page)
page += 1
html_content = self.downloader.download()
self.parser = Parser(html_content)
codeList = self.parser.parseSharesCode()
for code in codeList:
all_code.append(code)
# print(codeList)
self.saver.saveKlineToCSV(codeList=codeList, filepath=self.kline_filepath, date=self.date)
self.save(codelist=all_code)
def save(self,codelist):
self.saver.saveCodeListToCSV(codeList=codelist, filepath=self.codelist_filepath) # 保存代碼到csv
# self.saver.saveKlineToMySQL(filepath=self.kline_filepath) # 保存所有股票的信息到數據庫
# 程序入口
if __name__ == '__main__':
url = 'https://hq.gucheng.com/HSinfo.html?en_hq_type_code=&sort_field_name=px_change_rate&sort_type=desc&page=%s' # 股城網>行情>滬深A股
kline_filepath = 'F:\\files\\sharesDatas\\dayline\\' # 定義數據文件保存路徑
codelist_filepath = 'F:\\files\\sharesDatas\\code_list\\' # 定義數據文件保存路徑
controller = Controller(url=url, kline_filepath=kline_filepath, codelist_filepath=codelist_filepath, date='20190428')
controller.start()
或者:
git clone https://github.com/CatsJuice/netease-stock-day-line.git
1.2.2.3. 網易財經財務數據
網頁url為http://quotes.money.163.com/f10/zycwzb_601318.html#01c01, 可以看到同交易日線數據一樣, 這里有一個下載數據的按鈕, 對應盈利能力, 償還能力等, 而這里直接通過開發者工具審查元素, 即可看到超鏈接的href:/service/zycwzb_601318.html?type=report&part=ylnl, 很快, 就能拿到完整的url, 然后操作同網易日線數據, 項目地址同樣位于https://github.com/CatsJuice/netease-stock-day-line)
1.2.3. 東方財富
1.2.3.1. 股票信息列表
1.2.3.2. 交易數據, 財務數據
東方財富的交易數據/財務數據我也嘗試過使用爬蟲爬取, 但是還是要走不少彎路的, 而且可能最后還沒成功, 首先, 東方財富的數據頁面和其他平臺一樣, 股票代碼在url中, 如http://data.eastmoney.com/bbsj/yjbb/600175.html, 直接爬取, 或者使用開發者工具定位頁面元素會發現:
對應的數據是亂碼的, 而如果繼續挖掘其js文件, 是可以找到有加密的函數的,如下圖所示:
在這個名字特別明顯直白的js文件load_table_data_pc.js?201606021831中, 可以看到加密, 解密的方法, 這也使得爬取成為可能, 但是太大費周章暫不考慮。
2. 熱點獲取
2.1. 東方財富
這一頁面的新聞列表不是通過Ajax異步加載的, 可以直接抓取, 而頁碼由url中cgnjj_x(x為頁碼)決定,抓取過程較為簡單;這里統計的思路是, 將所有的要聞簡述拼接成字符串(以;分隔并寫入txt作備份), 然后利用jieba庫分詞并統計詞頻, 項目地址:https://github.com/CatsJuice/eastmoney-yaowen-keyword
也可以直接clone至本地
git clone https://github.com/CatsJuice/eastmoney-yaowen-keyword.git
3. 數據分析
在證券投資中, 有很多技術層面的投資策略, 如各種公式, 通過策略可以篩選出股票, 但并不意味著一定能盈利, 而量化投資可以驗證這一策略的可靠性, 接下來就是我對若干策略的驗證;
3.1. 換手率分析
分析連續處于低換手率的股票, 脫離低換手率后, 出現連續處于高換手率, 判斷2個時期的收盤價均價, 分析滿足這一特征的股票的價格是否會上漲;
程序設計的思路如下
迭代日線數據文件
判斷是否是連續高換手率
判斷是否在連續高換手率后出現連續低換手率
結果展示
注: 因為數據文件是按日期的倒序排序的, 所以分析迭代時, 先判斷是否出現連續高換手率
核心代碼如下:
high = []
low = []
for row in arr:
rate = row[10]
if rate == "None":
high = []
low = []
continue
if rate == 0:
if len(high) >= self.min_days and len(low) >= self.min_days:
dic = {'high': high, 'low': low}
self.res.append(dic)
high = []
low = []
continue
rate = float(rate)
# 4. 判斷是否是 高 換手率
if rate >= self.border_rate:
# 4.3 判斷是否是: 連續高 ->連續低 -> 結束連續低
if len(low) >= self.min_days:
# 符合條件, 寫入
dic = {'high': high, 'low': low}
self.res.append(dic)
high = []
low = []
elif len(low) > 0: # 連續低中有值
# 不滿足連續 低, 重置
high = []
low = []
high.append(row)
elif len(high) < self.min_days: # 是低換手率, 判斷是否前面是連續高換手率
# 4.1 前面不是連續的高換手率, 重置高換手率數組
high = []
else:
# 4.2 前面是連續高換手率, 地換手率寫入
low.append(row)
# 判斷日期是否已達到最后
if row[0] <= self.end_date:
# 判斷是否滿足條件
if len(high) >= self.min_days and len(low) >= self.min_days:
dic = {'high': high, 'low': low}
self.res.append(dic)
high = []
low = []
break
簡易流程圖如下:
程序可調整參數如下:
No
param
type
meaning
demo
1
file_path_prefix
str
日線數據目錄前綴
'F:\\files\\sharesDatas\\kline\\'
2
min_days
int
最小連續天數
10
3
border_rate
float
換手率高低邊界
2
4
end_date
str
統計的最早日期
'2018-12-28'
程序運行結果截圖如下:201905012052.png
或者:
git clone https://github.com/CatsJuice/low-switch-hand-rate.git
3.2. CCI指標
3.2.1. 概念
順勢指標又叫CCI指標,CCI指標是美國股市技術分析 家唐納德·藍伯特(Donald Lambert)于20世紀80年代提出的,專門測量股價、外匯或者貴金屬交易是否已超出常態分布范圍。屬于超買超賣類指標中較特殊的一種。波動于正無窮大和負無窮大之間。但是,又不需要以0為中軸線,這一點也和波動于正無窮大和負無窮大的指標不同。
3.2.2. 指標用法
1. 當CCI指標曲線在+100線~-100線的常態區間里運行時,CCI指標參考意義不大,可以用KDJ等其它技術指標進行研判。
2. 當CCI指標曲線從上向下突破+100線而重新進入常態區間時,表明市場價格的上漲階段可能結束,將進入一個比較長時間的震蕩整理階段,應及時平多做空。
3. 當CCI指標曲線從上向下突破-100線而進入另一個非常態區間(超賣區)時,表明市場價格的弱勢狀態已經形成,將進入一個比較長的尋底過程,可以持有空單等待更高利潤。如果CCI指標曲線在超賣區運行了相當長的一段時間后開始掉頭向上,表明價格的短期底部初步探明,可以少量建倉。CCI指標曲線在超賣區運行的時間越長,確認短期的底部的準確度越高。
4. CCI指標曲線從下向上突破-100線而重新進入常態區間時,表明市場價格的探底階段可能結束,有可能進入一個盤整階段,可以逢低少量做多。
5. CCI指標曲線從下向上突破+100線而進入非常態區間(超買區)時,表明市場價格已經脫離常態而進入強勢狀態,如果伴隨較大的市場交投,應及時介入成功率將很大。
6. CCI指標曲線從下向上突破+100線而進入非常態區間(超買區)后,只要CCI指標曲線一直朝上運行,表明價格依然保持強勢可以繼續持有待漲。但是,如果在遠離+100線的地方開始掉頭向下時,則表明市場價格的強勢狀態將可能難以維持,漲勢可能轉弱,應考慮賣出。如果前期的短期漲幅過高同時價格回落時交投活躍,則應該果斷逢高賣出或做空。
3.2.3. 公式
關于公式有兩種:
3.2.3.1. 公式一:
TYP:=(HIGH+LOW+CLOSE)/3;
CCI:(TYP-MA(TYP,N))/(0.015*AVEDEV(TYP,N));
該公式來自通達信, 含義如下:
TYP賦值:(最高價+最低價+收盤價)/3
輸出CCI:(TYP-TYP的N日簡單移動平均)/(0.015*TYP的N日平均絕對偏差)
3.2.3.2. 公式二:
程序中我采用的是公式二, 生成的 CCI 指標追加到日線文件中, 在相應日期后增加 CCI 的值, 在 Python 中對 csv 文件進行增加列的方法, 在網上可找到的方法較少, 以下為一個提供了多種解決方案的, 較為完善的鏈接: https://stackoverflow.com/questions/11070527/how-to-add-a-new-column-to-a-csv-file, 我使用的是 pandas 庫, 關鍵代碼如下:
df = pd.read_csv(filename, encoding='gbk')
df['cci'] = ''
for index, row in pd.iterrows():
df.loc[index, 'cci'] = cci
3.2.4. 程序設計
總共設計了4個類如下:
CCIAnalyze(object) ???????//??CCI分析類
analyze_all(self) ????//??分析所有股票
analyze_one(self) ????//??分析一只股票
test_buy(self) ??????//??測試購買(策略一:在下文說明)
test_buy_2(self) ?????//??測試購買(策略二:在下文說明)
CCICalculate(object) ?????//??根據3.2.3. 公式中的公式二計算CCI
CCICalculate_2(object)????//??根據3.2.3. 公式中的公式一計算CCI
BuyInfo(object)????????//??購買輔助類
3.2.5. 參數說明
id
param
type
default
mean
demo
necessary
1
file_path_prefix
str
None
日線數據文件目錄前綴
'F:\\files\\sharesDatas\\kline\\'
True
2
cci_path_prefix
str
None
CCI數據將要保存的目錄前綴
'F:\\files\\sharesDatas\\cci\\'
True
3
code
str / int
None
股票代碼
000001
False
4
end_date
str
'0000-00-00'
最早的日期(截止日期)
'2009-01-01'
False
或者clone:
git clone https://github.com/CatsJuice/stock-cci
3.2.6. 測試結果
在進行測試購買時, 采用的策略有2種,如下:
策略一:
對應CCIAnalyze(object)中的test_buy(self)方法, 當CCI向下突破-100時, 后一交易日買入, 等到CCI向上突破100時, 后一交易日賣出(由于計算出當日的CCI, 當日已不可買入/賣出,所以計算后一交易日買入/賣出)
策略二:
對應CCIAnalyze(object)中的test_buy_2(self)方法, 當CCI向下突破-100時, 繼續觀察, 等到向下達到第一個峰值時, 后一交易日買入,等到CCI向上突破100時, 繼續觀察, 等到向上達到第一個峰值時, 后一交易日賣出(和策略一一樣, 計算出CCI后只能后一日買入, 而策略二不同的是, 要判斷達到第一個峰值, 必須確定峰值后一日CCI降低, 所以測試時, 購買的是峰值的后第2個交易日)
經過若干次測試,分別使用的數據是2019-01-01 ~ 2019-04-26, 2018-01-01 ~ 2019-04-26, 2017-01-01 ~ 2019-04-26, 測試的結果, 購買策略一的收益率大概為56%, 可見這個指標有一定依據, 但是盈利的幾率不夠高; 而策略二的收益率大概為57%, 較策略一高一點, 但是依舊無法將其作為購買的唯一指標, 以下是某次運行結果的部分截圖:
3.3. 《胡立陽股票投資100招》 由“價量關系”來為個股打分數
3.3.1. 概念
胡立陽根據股票的價量關系對股票進行打分(第21招), 而其打分的依據如下:
當日個股表現:
(1)價漲量增 +2 分
(2)價漲量縮 +1 分
(3)價跌量增 -2 分
(4)價跌量縮 -1 分
每日累計評分。你只要連續計算一個星期,以最高分或者是評分穩定增加的作為你投資的第一選擇,因為那只個股具備了“價量配合”的上漲條件
3.3.2. 程序設計
根據以上打分標準, 不難計算每只股票某日的得分, 然后將所有得分排序即可, 這樣能夠得到某一天的所有股票打分, 而需要驗證每只股票是否盈利, 這里我簡單地判斷后一天收盤價是否比當天高,所以能夠得出每只股票是否盈利, 計算出當天得分前n只股票的盈利與否, 再用 盈利股票的只數 / n 即當天的盈利率;
以此類推m天可得出m天這一策略的盈利幾率
程序流程圖如下:
參數詳情如下表
id
param
type
mean
demo
1
prefix
str
網易財經日線數據文件前綴
'F:\\files\\sharesDatas\\kline\\'
2
date_now
str
最新數據的第一個日期, 對應爬取的最新數據表第一行的日期
'2019-04-26' ; Format( yyyy-mm-dd )
3
days
int
加分的天數
5
4
calculate_days
int
要統計的天數
20
5
best_num
int
選出的最佳的天數
10
3.3.3. 結果
鍵值說明:
date: 統計的日期
rate_low: 打分最低的盈利幾率
rate_high: 打分最高的盈利幾率
在分別統計了每個分數的股票的盈利率, 和排名靠前的股票的盈利率來看, 胡立陽的打分欠缺科學性, 盈利率不穩定, 不建議做參考;
3.3.4. Source Code
直接clone:
git clone https://github.com/CatsJuice/stock-price-num-score.git
3.4. 移動平均線分析
3.4.1. 概念
移動平均線,Moving Average,簡稱 MA, MA 是用統計分析的方法,將一定時期內的證券價格(指數)加以平均,并把不同時間的平均值連接起來,形成一根 MA ,用以觀察證券價格變動趨勢的一種技術指標。
常見的均線有5日均線(MA5), 十日均線(MA10), 二十日均線(MA20), 三十日均線(MA30), 六十日均線(MA60)
3.4.2. 計算
在這里, 我的計算方法是簡單地計算 n 日內的 收盤價 的算數平均值
3.4.3. 程序設計
基本思路如下:
定義要計算的幾日(假設為n)均線數組, 進行遍歷計算
迭代文件列表
打開某個文件, 選取 n 行, 計算這n行的 收盤價 總和
迭代 csv 文件的每一行, 每次迭代, 將上述收盤價的總和減去首行, 追加新的一行
計算 ma 并寫入原文件
和之前計算cci不同, 這里不再做多余的迭代, 以此能大大提高效率
3.4.4. 參數說明
id
param
type
mean
demo
necessary
1
file_path_prefix
str
日線數據文件目錄前綴
'H:\\sharesDatas\\kline\\'
true
2
code
int / str
股票代碼
'000001'
false
3
end_date
str
最早的日期(截止日期)
'2019-01-01'
false
4
all_n
Array
要計算的幾日ma 數組
[5,10]
false
3.4.5. 購買策略分析
3.4.5.1. 策略一: '老太太選股法' 一根均線打天下
購買的策略較為簡單, 收盤價低于均線即買入, 將收盤價作為買入價格(假設第二個交易日開盤即買入), 收盤價高于均線即賣出, 同樣將收盤價作為賣出價格
3.4.5.2. 策略二:'黃金交叉'和'死亡交叉'
1、”黃金交叉”
當10日均線由下往上穿越30日均線,形成10日均線在上,30日均線在下時,其交叉點就是黃金交叉,黃金交叉是多頭的表現,出現黃金交叉后,后市會有一定的漲幅空間,這是進場的最佳時機。
2、”死亡交叉”
當10日均線由上往下穿越30日均線,形成30日均線在上,10日均線在下時,其交點稱之為”死亡交叉”,”死亡交叉”預示空頭市場來臨,股市將下跌此時是出場的最佳時機。
3.4.6. 分析結果
3.4.6.1. 老太太選股法
對于'老太太選股法', 我分別測試了5日均線和10日均線(由于20日數據較大, 電腦處理太慢, 暫時不考慮), 最終得到的結果為:
ma5 的盈利幾率為63.3132%; 而 ma10 的盈利幾率為67.0026%
以下是某次運行的截圖:
運行結果中, 可以發現, 無論是盈利或是虧損, 金額基本都在1元以內, 可見這一策略 首先具有一定的科學性, 同時風險也不是很大, 但盈利金額較高的可能性很小;
對此, 我增加了結果判斷, 統計虧損 / 盈利 超過 1 元的比例, 以下是結果:
對于 ma5:
對于 ma10:
對于 ma20:
對于 ma30:
對于 ma60:
從更詳細的結果可以看到, 無論盈利或虧損, 超過 1 元的概率都不大, 但是虧損的時候超過 1 元的概率比盈利大; 更有趣的是, 當 ma的計算天數越多, 盈利的幾率越大, 由于一般看盤軟件中僅提供了上述這些均線(MA5, MA10, MA20, MA30, MA60), 所以這里不再多更高的天數進行測試; 雖然天數越大時, 盈利幾率越高, 但是從更細節數據可以看到, 盈利的情況大于1元的概率始終在 10% ~ 15%, 而虧損時超過 1 元的概率卻表現出和天數正相關的趨勢, 并且從 24% 跳躍到高達 50%
3.4.6.2. '黃金交叉'和'死亡交叉'
運行結果如圖:
對于這個結果的確是非常出乎意料, 這個盈利率低得感覺好像可以作為一個反向指標使用;
對于這個結果, 首先應該懷疑是自己的代碼邏輯出現了問題, 于是我檢查了關鍵代碼如下:
# 前一天 ma30 > ma10 , 今天ma10 >= ma30
if prev_row is not None and prev_row['ma30'] > prev_row['ma10'] and row['ma10'] >= row['ma30']:
# 買入
...
...
# 前一天 ma30 < ma10 , 今天ma10 <= ma30
if prev_row['ma30'] < prev_row['ma10'] and row['ma10'] <= row['ma30']:
# 賣出
關鍵代碼部分好像并沒有什么邏輯問題, 但是不排除其他部分出現問題, 由于這個指標的預期過于不符, 我試著嘗試了交換 '黃金交叉'和'死亡交叉', 修改上述關鍵代碼如下(其實就是把所有大小判斷符號反置):
# 前一天 ma30 < ma10 , 今天ma10 <= ma30
if prev_row is not None and prev_row['ma30'] < prev_row['ma10'] and row['ma10'] <= row['ma30']:
# 買入
...
...
# 前一天 ma30 > ma10 , 今天ma10 >= ma30
if prev_row['ma30'] > prev_row['ma10'] and row['ma10'] >= row['ma30']:
# 賣出
以下為運行結果:
這時候盈利率可以說是馬馬虎虎還算過得去了, 但是盈利率卻還是不如 ‘老太太選股法’
3.4.7. Source Code
直接 clone :
git clone https://github.com/CatsJuice/line-of-ma.git
3.5. 分時數據 成交手分析
3.5.1. 基本概念
成交手指的是每筆買賣流動的股票數量,現行股票交易所用手方便表示成交數量,一手相當于股票一百股,各類炒股軟件中的成交量一般用手表示
3.5.2. 分析的形態
當某日開盤初出現集中的大量 買入/賣出 , 即成交手出現連續峰值的情況(大概如下圖所示情形), 分析該股在當日的趨勢
3.5.3. 程序設計
設計的關鍵, 是為了找出某日開盤時的集中峰值, 在這里我首先設定分界線, 默認 10 點前為開盤初, 統計全天的換手量平均值,以及開盤初的換手量平均值, 當開盤初的換手量大于全天的某倍數(默認為2)時, 即認定當日開盤初的換手量為集中峰值;
但將今日的換手量平均值作為比較, 在實際運用中可能無法實施, 所以另一策略是統計 前一日 的平均換手量作為參考(在此尚未實現)
3.5.4. 參數說明
no.
param
type
mean
format
default
necessary
demo
1
tick_file_prefix
str
分時數據文件目錄前綴
/
none
true
"F:\\files\\sharesDatas\\tushare_tick_data\\"
2
end_date
str
終止日期
'yyyymmdd'
'00000000'
false
'20190426'
3
end_time
str
開盤初的終止時間
'hh:mm:ss'
'10:00:00'
false
'11:00:00'
4
multiple
int/float
設定倍數
/
2
false
2.5
3.5.5. 結果及分析
以下為不區分買盤或賣盤, 倍數為 2 時, 當天前后均價變化是否上漲的結果:
而只考慮買盤時, 這個幾率為34%, 只考慮賣盤時幾率為47%, 可見開盤初集中的成交量不一定會導致今日價格走上升的趨勢,但在這個部分的程序設計中, 分析得不是很到位, 首先實際中沒法考慮當天后面的成交量情況, 所以更穩妥的方法應該是計算 前一交易日的平均成交量 , 而在判斷這這股票是否處于上漲的趨勢時, 簡單判斷今日的前后均價也不是最不合理的。
3.5.6. Source Code
直接 clone:
git clone https://github.com/CatsJuice/stock-volume-analyze.git
3.6. MACD
3.6.1. 基本概念
3.6.1.1. MACD
百度百科的解釋:
MACD 稱為異同移動平均線,是從雙指數移動平均線發展而來的,由快的指數移動平均線( EMA12 )減去慢的指數移動平均線( EMA26 )得到快線 DIF ,再用 2 ×(快線DIF-DIF的9日加權移動均線DEA) 得到 MACD柱
3.6.1.2. MACD 金叉
MACD 指標是股票技術分析中一個重要的技術指標,由兩條曲線和一組紅綠柱線組成。兩條曲線中波動變化大的是 DIF 線,通常為白線或紅線,相對平穩的是 DEA 線(MACD線),通常為黃線。當 DIF 線上穿 DEA 線時,這種技術形態叫做 MACD金叉,通常為 買入信號
3.6.1.3. MACD 死叉
DIF 由上向下突破 DEA,為賣出信號
3.6.2. 程序設計
3.6.2.1. 數據計算
【MACD公式】
以下為通達信的公式:
DIF:EMA(CLOSE,SHORT)-EMA(CLOSE,LONG);
DEA:EMA(DIF,MID);
MACD:(DIF-DEA)*2,COLORSTICK;
需要的變量參數為 SHORT , LONG , MID, 在通達信中默認分別為 12 , 26 , 9;
要計算 MACD ,關鍵在于求 EMA(指數移動平均值), 其公式如下:
其中, α 為平滑指數, 一般取 2 / ( N + 1 )
該公式是依賴于前一日的遞歸的計算,最大的問題便是第一天的 EMA[yesterday] 無法求得, 在程序設計時, 我將它設置為當日收盤價,
再求 DIF, 第一天的 DIF 就變成了 0 (當日收盤價 - 當日收盤價), 而求 DEA (即 DIF 的 EMA) 時,第一天所需的 DEA[yesterday] 同樣不知道, 將其設置為 DIF ( 即 0 )。
【性能優化之多線程】
由于數據量較大, 在這里我嘗試使用 Python 的多線程進行分析, 添加了一個 calculate_all_by_thread 的方法, 接收 1 個參數(thread_num)即需要的線程數, 然后根據線程數動態創建線程, 將文件劃分為等分的塊分別計算, 創建線程部分的代碼如下:
def calculate_all_by_thread(self, thread_num):
file_list = os.listdir(self.file_prefix)
file_count = len(file_list)
offset = file_count / thread_num
offset = math.ceil(offset)
threads = []
for i in range(thread_num):
start = i * offset
end = (i+1) * offset if (i+1) * offset < file_count else -1
thread = threading.Thread(target=self.calculate_block, args=(start, end))
threads.append(thread)
for t in threads:
t.setDaemon(True)
t.start()
t.join()
這里我使用的 2 個線程進行計算, 實際測試速度雖然不是單線程的 2 倍,但是計算的時間上是有所優化的。
【數據存儲】
在了解公式后, 便可迭代文件進行帶入計算, 計算結果這里我采用的是將其寫入原文件, 在列末尾追加新的 5 列:
EMA26 (26根據傳參而定)
EMA12 (12根據傳參而定)
DIF
DEA
MACD
【數據驗證】
完成計算后, 有必要對計算結果進行驗證, 這里, 我簡單寫了一個輸出來進行簡單驗證, 該部分代碼如下:
def verify_calculate(self, code):
try:
df = pd.read_csv(self.file_prefix + str(code) + '.csv', encoding='gbk')
except:
print('文件%s.csv打開失敗' % code)
return
df = df[df.日期 > self.end_date]
df = df[::-1]
mid = []
dates = []
difs = []
deas = []
macds = []
color_macd = []
for index, row in df.iterrows():
mid.append(0)
dates.append(row['日期'])
difs.append(row['DIF'])
deas.append(row['DEA'])
macds.append(row['MACD'])
if row['MACD'] > 0:
color_macd.append('red')
else:
color_macd.append('green')
# 繪制圖表
fig = plt.figure(dpi=128, figsize=(100, 6))
plt.plot(dates, mid, c='blue')
plt.plot(dates, difs, c='yellow')
plt.plot(dates, deas, c='black')
plt.bar(dates, macds, color=color_macd)
plt.xticks(fontsize=5)
plt.xlabel('', fontsize=5)
fig.autofmt_xdate()
plt.show()
在對股票 '000002' 進行驗證, 驗證結果如下(上圖為通達信MACD截圖, 下圖為程序輸出的圖片, 時間為2018-01-01 ~ 2019-04-26):
可以看到圖形基本一致, 但是同樣對股票 000001 進行驗證, 結果如下:
此時, 兩圖形基本沒有重合, 原因是因為計算 MACD 時, 第一天的 EMA并不是前一天計算的,同樣 DEA 也是, 而這里數據只計算了 2018-01-01 ~ 2010-04-26, 所以前面基本一致的時候, 是因為第一日剛好相近導致后面計算偏差不大, 對此, 應該不設置截止日期以保證數據的可靠性, 我將時間設置到了 2014-01-01, 重新驗證 000001 得到如下:
然后再隨機抽取一直股票進行驗證, 如下為 600702 的結果
3.6.2.2. 數據分析
這里主要是對 MACD 的分析, 所以以上計算的數據實際上只要用到 MACD , 對 MACD 柱滿足以下形態的股票進行分析:
該形態需滿足以下特點:
出現連續的紅色塊(MACD > 0)
緊接著出現連續藍色塊(MACD < 0)
藍色塊小于第一個紅色塊
藍色塊后面跟著一個紅色塊, 且后一紅色塊大于前一紅色塊
在程序設計時, 使用的是迭代數據行, 通過 if 判斷, 來定位上述的 3 個塊, 在第三塊大于第一塊時即為符合條件的形態;大致思路是, 定義 3 個變量來標記 3 塊的合計值:
red_1
green_1
red_2
判斷三個塊的條件分別為:
塊 1 :green_1 == 0 and red_2 == 0
塊 2 :red_1 != 0 and red_2 == 0
塊 3 :red_1 != 0 and red_2 != 0 and green_1 != 0
3.6.3. 參數說明
no.
param
type
mean
format
default
necessary
demo
1
file_prefix
str
日線文件前綴
/
None
True
'F:\\files\\sharesDatas\\kline\\'
2
end_date
str
截止日期
yyyy-mm-dd
'0000-00-00'
False
'2019-01-01'
3
short
int
短期的天數
/
12
False
12
4
long
int
長期的天數
/
26
False
26
5
mid
int
計算DEA時, DIF的EMA天數
/
9
False
9
6
count_max
int
判斷后面的多少天漲跌情況
/
5
False
10
7
count_border
int
設置最大天數內至少多少天漲才符合
/
3
False
6
3.6.4. function注釋
calculate_noe
計算單只股票的 MACD 、 DIF 、 EMA 、 DEA
需要參數:
code: 股票的代碼
返回值:
None
calculate_all_by_thread
通過多線程計算所有股票
需要參數:
thread_num: 線程數量
返回值:
None
calculate_block:
計算指定代碼塊的股票
需要參數:
start:塊下標開始
end: 塊下標結束
返回值:
None
verify_calculate
驗證計算的結果
需要參數:
code: 股票的代碼
返回值:
None
analyze_macd_one:
分析單只股票 的 macd
需要參數:
code: 股票的代碼
返回值:
None
analyze_macd_by_thread:
通過多線程分析所有股票
需要參數:
thread_num: 線程數量
返回值:
None
analyze_block:
分析指定代碼塊的股票
需要參數:
start:塊下標開始
end: 塊下標結束
返回值:
None
show_res:
輸出分析結果并寫入相關文件
需要參數:None
返回值: None
3.6.5. 結果分析
【某次結果截圖】
計算 MACD 時候的傳參均為默認, 即通達信公式中的默認值, 對上述情況進行分析得到的結果是, 后 10 天內至少 6 天比符合條件時漲了的比例約為 55% , 而如果將參數調整為 5 天內至少 3 天, 則比例降低到約47%, 而如果不是 MACD 的值計算有誤的話, 可見這個 MACD 的指標并不是很可靠, 但可以調整計算 MACD 的參數的值
3.6.6. Source Code
直接 clone:
git clone https://github.com/CatsJuice/MACD-Analyze.git
總結
以上是生活随笔為你收集整理的python量化投资培训清华大学深研院_GitHub - CatsJuice/quantitative-investment-learning: 使用Python进行量化投资的学习报告...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 弧度转为角分秒的c语言程序_用弧度表示角
- 下一篇: vue在js上处理后台返回的数组_vue