这是奔跑的键盘侠的第118篇文章
依旧,先贴一下目录:
├── README
├── MyQuant_v1 #量化分析程序目录
├── __init__.py
├── data #数据处理目录
│ ├── __init__.py
│ ├── basic_crawler.py# 爬取股票基础信息存入MongoDB数据库.
│ ├── data_crawler.py #爬取指数、股票数据
│ ├── data_module.py #从数据集中获取股票数据
│ └── finance_report_crawler.py #爬取财报数据
├──util # 公用程序
│ ├── __init__.py
│ ├── stock_util.py#获取股票交易日期、前一交易日日期、股票代码
│ └── database.py #链接数据库
├── backtest #回测
│ ├── __init__.py
│ └── _backtest_ #计划写一下回测走势图
├── factor #因子
│ ├── __init__.py
│ └── pe_factor.py#计算各股票的止盈率,用于后面的股票池策略
├── strategy #策略
│ ├── __init__.py
│ └── _strategy_ #计划简单写个,主要用于回测
├── trading #交易
│ ├── __init__.py
│ └── _trading_ #不准备开发
└── log #日志目录
├── __init__.py
├── backtest.log #不准备开发
└── transactions.log#不准备开发
开弓没有回头箭,no zuo no die……
这节要写的是从网站爬取财务报表信息,然后写入自己的数据集中。财务信息有了,接着就可以自己算各个股票的PE市盈率了。
1
finance_report_crawler.py
爬取上市公司财报数据,说白了就是个简单的爬虫,数据来源:东方财富官网。
#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-
# @Time : 2019-08-04 06:40
# @Author : Ed Frey
# @File : finance_report_crawler.py
# @Software: PyCharm
import json
import urllib3
from pymongo import UpdateOne
from util.database import DB_CONN
from util.stock_util import get_all_codes
import traceback
class FinanceReportCrawler:
def __init__(self):
self.finance_report = DB_CONN['finance_report']
def crawl_finance_summary(self):
"""
to get summary information from financial reports of the listed company
"""
codes = get_all_codes()
conn_pool = urllib3.PoolManager()
url = 'http://dcfm.eastmoney.com//em_mutisvcexpandinterface/api/js/get?' \
'type=YJBB20_YJBB&token=70f12f2f4f091e459a279469fe49eca5&st=reportdate&sr=-1' \
'&filter=(scode={0})&p={page}&ps={pageSize}&js={"pages":(tp),"data":(x)}'
for code in codes:
try:
response = conn_pool.request('GET', url.replace('{0}', code))
result = json.loads(response.data.decode('UTF-8'))
reports = result['data']
update_requests = []
for report in reports:
doc = {
'report_date': report['reportdate'][0:10],
'announced_date': report['latestnoticedate'][0:10],
'eps': report['basiceps'],
'code': code
}
update_requests.append(
UpdateOne(
{'code': code, 'report_date': doc['report_date']},
{'$set': doc}, upsert=True))
if len(update_requests) > 0:
update_result = self.finance_report.bulk_write(update_requests, ordered=False)
print('股票 %s, 财报,更新 %d, 插入 %d' %
(code, update_result.modified_count, update_result.upserted_count))
except:
print('获取业绩报表时,发生错误:%s' % code, flush=True)
traceback.print_exc()
if __name__ == '__main__':
frc = FinanceReportCrawler()
frc.crawl_finance_summary()
运行结果:
同样,建立一下索引,运行速度会大幅提升,没几分钟就爬取完毕。打开终端看一下数据集信息。
爬取到了138854条信息。
2
pe_factor.py
这篇依旧是爬取数据,爬取上市公司财报数据,说白了就是个简单的爬虫,数据来源:东方财富官网。
#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-
# @Time : 2019-08-04 07:20
# @Author : Ed Frey
# @File : pe_factor.py
# @Software: PyCharm
from util.database import DB_CONN
from data.data_module import DataModule
from util.stock_util import get_trading_dates
from pymongo import UpdateOne
import traceback
class PEFactor:
def __init__(self):
self.daily_collection = DB_CONN['daily']
self.finance_report = DB_CONN['finance_report']
self.dm = DataModule()
def compute_one_day(self, date):
df_daily = self.dm.get_all_stock_k_data_at_date(autype=None,date=date)
factors = []
for code in df_daily.index:
try:
eps = self.finance_report.find_one(
{
'code': code,
'report_date': '2018-12-31',
},
projection = {'_id': False, 'code': False})
# print(eps)
if eps is None:
continue
close = df_daily.loc[code]['close']
if 'eps' not in eps or eps['eps'] == '-':
print('计算pe因子,EPS有误, 股票代码:%s,日期:%s' %(code, date), eps)
continue
pe = close/eps['eps']
factors.append({
'date': date,
'code': code,
'pe': round(pe, 2)
})
except:
print('计算pe因子出错, 股票代码:%s,日期:%s' %(code, date))
traceback.print_exc()
return factors
def compute(self, begin_date, end_date):
dates = get_trading_dates(begin_date, end_date)
for date in dates:
factors = self.compute_one_day(date)
# print('计算pe因子:,日期:%s,数量:%4d。' %(date, len(factors)))
update_requests = []
for factor in factors:
update_requests.append(
UpdateOne({'code': factor['code'], 'date': factor['date'],'index':False},
{'$set': factor},
upsert=True))
if len(update_requests) > 0:
update_result = self.daily_collection.bulk_write(update_requests, ordered=False)
print('保存pe因子,日期:%s,插入:%4d,更新:%4d' %
(date, update_result.upserted_count, update_result.modified_count))
if __name__ == '__main__':
pf = PEFactor()
pf.compute('2018-01-01', '2019-07-28')
这个计算起来可能比较花时间,可以同时并行运算,比如按不同的季度同时运算上面的代码
关于市盈率的计算,简单起见,就直接取18年年度EPS来计算2018-2019的市盈率,要深入的话,需要在第二年三四月份财报发布后才知道前一年的EPS,可见PE的计算本身就不是完全合拍的。所以,就酱紫吧。