量化投资的第一步在于需要一个数据库,数据库必须包括以下三部分内容。
1)股票历史行情数据、包括股票历史上每个交易日的交易状态、涨跌幅、开盘价、收盘价、成交量等数据。
2)上市公司的财务数据、包括上市公司公布的资产负债表、现金流量表、利润表上的各项指标
3)上市公司的重要指标、比如ROE、ROA、资产负债率、净利率等
首先,我们新建一个类,命名为Download_HistoryData,顾名思义就是获得股票历史数据。在同个目录下新建三个文件夹trading、indicator、finance分别用来存放历史行情数据、重要指标数据、财务数据。
# -*- coding: utf-8 -*-
'''
功能:爬区所有A股历史数据
'''
import pandas as pd
import requests as re
from lxml import etree
import os
class Download_HistoryData(object):
def __init__(self,it=10):
self.code_list = self.get_all_codes() #获取A股所有股票代码
self.path_trading = 'D:\\database\\history\\trading\\'#股票历史行情数据的路径
self.path_indicator = 'D:\\database\\history\\indicator\\'#股票历史指标数据的路径
self.path_finance = 'D:\\database\\history\\finance\\'#股票历史财务数据的路径
self.it=it #默认错误的重复相应次数为10次
self.headers = {
"User-Agent": ":Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"
}
#建立相应的文件夹
if not os.path.exists(self.path_trading):
os.makedirs(self.path_trading)
if not os.path.exists(self.path_indicator):
os.makedirs(self.path_indicator)
if not os.path.exists(self.path_finance):
os.makedirs(self.path_finance)
然后我们需要获取所有A股的股票代码。我们从东方财富网的股票行情区抓取了所有股票当天的历史行情数据,并提取股票代码,就是当前A股所有股票的代码,并赋值个self.code_list。
def get_all_codes(self):
#得到所有股票代码#
code_list=[]
for i in range(1,10):
url = 'http://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx?type=CT&cmd=C._A&sty=FCOIATA&sortType=(Code)&sortRule=1&page='+str(i)+'&pageSize=1000&js=var%20tccEBAuq={rank:[(x)],pages:(pc),total:(tot)}&token=7bc05d0d4c3c22ef9fca8c2a912d779c&jsName=quote_123&_g=0.628606915911589&_=1523438952920'
text = re.get(url).text
a=(text[(1+text.find('[')):(text.rfind(']'))]).split(sep=',')
t=text.count('"')
if t==0:
continue
n = int(len(a)*2/t)
for j in range(1 ,len(a), n):
code_list.append(a[j][0:7])
code_list=list(set(code_list))
code_list.sort()
print('The quantity of Chinese stocks in the market is ',len(code_list),'!')
return code_list
接下来,我们可以编写获取单个股票所有历史数据的程序。我们选择从网易财经中爬取所有股票历史行情的数据。
def download_trading(self,code):
start_url = "http://quotes.money.163.com/trade/lsjysj_" + code + ".html"
response = re.get(start_url)
if response.status_code == 200:
try:
html=etree.HTML(response.content)
start_date = ''.join(html.xpath('//input[@name="date_start_type"]/@value')[0].split('-'))#获取单只股票的最早交易日
end_date = ''.join(html.xpath('//input[@name="date_end_type"]/@value')[0].split('-'))#获取单只股票的最晚交易日
if ((code[0]=='6') | (code[0]=='9')):
name, flag = code+'.SH', '0'
else:
name, flag = code+'.SZ', '1'
download_url = "http://quotes.money.163.com/service/chddata.html?code="+flag+code+"&start="+start_date+"&end="+end_date+"&fields=TCLOSE;HIGH;LOW;TOPEN;LCLOSE;CHG;PCHG;TURNOVER;VOTURNOVER;VATURNOVER;TCAP;MCAP"
data = re.get(download_url)
f = open(self.path_trading + name + '.csv', 'wb')
for chunk in data.iter_content(chunk_size=10000):
if chunk:
f.write(chunk)
print ('股票---',code,'历史数据下载完毕!')
return 0
except:
return code
else:
return code
同理,我们可以爬取单只股票的重要指标数据和财务数据
def download_indicator(self, code):
#获取单只股票的历史指标数据
if ((code[0]=='6') | (code[0]=='9')):
name= code+'.SH'
else:
name= code+'.SZ'
rpt_type=['','&part=ylnl','&part=chnl','&part=cznl','&part=yynl']
d=''
try:
for i in rpt_type:
download_url='http://quotes.money.163.com/service/zycwzb_'+code+'.html?type=report'+i
data = re.get(download_url)
if i=='':
d=d+data.text[:data.text.find('\t')]
else:
d=d+data.text[data.text.find('\r\n')+2:data.text.find('\t')]
d='\r\n'.join(d.split(sep=',\r\n'))
f=open(self.path_indicator + name +'.csv','wb')
f.write(d.encode('GB18030'))
f.close()
print('股票--', code ,'主要指标下载完毕!')
return 0
except:
return code
def download_finance(self, code):
#获取单只股票的财务数据
if ((code[0]=='6') | (code[0]=='9')):
name= code+'.SH'
else:
name= code+'.SZ'
rpt_type=['zcfzb_','lrb_','xjllb_']
d=''
try:
for i in rpt_type:
download_url='http://quotes.money.163.com/service/'+i+code+'.html'
data = re.get(download_url)
if i=='zcfzb_':
d=d+data.text[:data.text.find('\t')]
else:
d=d+data.text[data.text.find('\r\n')+2:data.text.find('\t')]
d='\r\n'.join(d.split(sep=',\r\n'))
f=open(self.path_finance+name+'.csv','wb')
f.write(d.encode('GB18030'))
f.close()
print('股票--', code ,'财务数据下载完毕!')
return 0
except:
return code
然后,我们可以对code_list中所有A股代码进行循环,分别得到所有股票的历史行情数据、重要指标数据和财务数据。由于在爬虫过程中会出现很多失败的响应,我们把爬虫失败的代码存入eroors,等到爬虫结束后,再对error中的股票代码进行重爬,循环10次(self.it=10)。
def download_all_trading(self):
errors=[]
for code in self.code_list:
error = self.download_trading(code)
if error != 0:
errors.append(error)
print('股票: ', errors, ' 的历史数据下载发生错误!')
for i in range(self.it):
last_errors, errors = errors ,[]
if len(last_errors)==0:
print('所有股票历史数据下载完毕!')
break
else:
for e in last_errors:
error = self.download_trading(e)
if error != 0:
errors.append(error)
print('股票: ', errors, ' 的历史数据下载发生错误!')
def download_all_indicator(self):
errors=[]
for code in self.code_list:
error = self.download_indicator(code)
if error != 0:
errors.append(error)
print('股票: ', errors, ' 的主要指标下载发生错误!')
for i in range(self.it):
last_errors, errors = errors ,[]
if len(last_errors)==0:
print('所有股票主要指标下载完毕!')
break
else:
for code in last_errors:
error = self.download_indicator(code)
if error != 0:
errors.append(error)
print('股票: ', errors, ' 的主要指标下载发生错误!')
def download_all_finance(self):
errors=[]
for code in self.code_list:
error = self.download_finance(code)
if error != 0:
errors.append(error)
print('股票: ', errors, ' 的财务数据下载发生错误!')
for i in range(self.it):
last_errors, errors = errors ,[]
if len(last_errors)==0:
print('所有股票财务数据下载完毕!')
break
else:
for code in last_errors:
error = self.download_finance(code)
if error != 0:
errors.append(error)
print('股票: ', errors, ' 的财务数据下载发生错误!')
我们把爬取历史行情数据、财务数据与重要指标数据的函数进行整合。
def download_all_data(self):
self.download_all_trading()
self.download_all_indicator()
self.download_all_finance()
最后,我们对程序进行调试。
总共发现市场上有3530只股票。
股票历史行情数据正在下载。
最终的结果我们得到以下三个文件夹,每个文件夹中有每只股票的详细数据。
历史行情数据:
财务数据:
重要指标数据:
三个数据库大约为2个G,几乎能满足一般的量化投资需求。至此我们的量化分析系统已经迈出了第一步。