这是奔跑的键盘侠的第116篇文章
依旧,先贴一下目录:
├── README
├── MyQuant_v1 #量化分析程序目录
├── __init__.py
├── data #数据处理目录
│ ├── __init__.py
│ ├── basic_crawler.py# 爬取股票基础信息存入MongoDB数据库.
│ ├── data_crawler.py #爬取指数、股票数据
│ ├── data_fixing.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#不准备开发
继续填坑……
这期只写一个数据填充的模块
1
data_fixing.py
从第三方获取到的数据,其实有挺多内容要自己填充完善的,比如股票停牌日的信息,要知道停牌日的个股信息是空的,如刚好持仓股遇到停牌,是无法交易的,计算持仓市值时,停牌日没有信息会报错算不出该股当日数据(因为股价信息都是空白的),于是就需要参考停牌前交易日的数据,填充到停牌日中。另外,还有复权因子的计算,比如持仓股刚好遇到除权除息,如未进行相应的换算,持仓值会出现错误。
#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-
# @Time : 2019-08-03 21:44
# @Author : Ed Frey
# @File : data_fixing.py
# @Software: PyCharm
from pymongo import ASCENDING, UpdateOne
from util.database import DB_CONN
from util.stock_util import get_all_codes, get_trading_dates
class DailyFixing:
def __init__(self):
pass
def fill_is_trading(self, autype=None, begin_date=None, end_date=None):
dates = get_trading_dates(begin_date, end_date)
if autype is None:
collection = "daily"
elif autype == 'qfq':
collection = "daily_qfq"
elif autype == 'hfq':
collection = "daily_hfq"
else:
return []
for date in dates:
daily_cursor = DB_CONN[collection].find(
{'date': date},
projection={'code': True, 'volume': True, 'index': True})
update_requests = []
for daily in daily_cursor:
update_requests.append(
UpdateOne(
{'code': daily['code'], 'date': date, 'index': daily['index']},
{'$set': {'is_trading': (daily['volume'] > 0)}}
))
if len(update_requests) > 0:
update_result = DB_CONN[collection].bulk_write(update_requests, ordered=False)
print('Update is_trading, date: %s, inserted: %4d, modified: %4d'
% (date, update_result.upserted_count, update_result.modified_count),
flush=True)
def fill_suspension_dailies(self, autype=None, begin_date=None, end_date=None):
codes = get_all_codes()
dates = get_trading_dates(begin_date, end_date)
if autype is None:
collection = "daily"
elif autype == 'qfq':
collection = "daily_qfq"
elif autype == 'hfq':
collection = "daily_hfq"
else:
return []
for code in codes:
daily_cursor = DB_CONN[collection].find(
{'code': code, 'date': {'$gte': begin_date, '$lte': end_date}, 'index': False},
sort=[('date', ASCENDING)],
projection={'date': True, 'close': True}
)
date_daily_dict = dict([(daily['date'], daily) for daily in daily_cursor])
last_daily = None
update_requests = []
for date in dates:
if date in date_daily_dict:
last_daily = date_daily_dict[date]
else:
if last_daily is not None:
suspension_daily = {
'code': code,
'date': date,
'is_trading': False,
'index': False,
'volume': 0,
'open': last_daily['close'],
'close': last_daily['close'],
'high': last_daily['close'],
'low': last_daily['close']
}
update_requests.append(
UpdateOne(
{'code': code, 'date': date, 'index': False},
{'$set': suspension_daily},
upsert=True
))
if len(update_requests) > 0:
update_result = DB_CONN[collection].bulk_write(update_requests, ordered=False)
print('Fill suspension dailies, code: %s, inserted: %4d, modified: %4d'
% (code, update_result.upserted_count, update_result.modified_count),
flush=True)
def fill_aufactor_pre_close(self, begin_date=None, end_date=None):
dates = get_trading_dates(begin_date, end_date)
for date in dates:
daily_cursor = DB_CONN['daily'].find(
{'date': date, 'index': False},
projection={'code': True, 'close': True, '_id': False})
code_daily_dict = dict([(daily['code'], daily) for daily in daily_cursor])
daily_hfq_cursor = DB_CONN['daily_hfq'].find(
{'date': date, 'index': False},
projection={'code': True, 'close': True, '_id': False})
update_requests = []
for daily_hfq in daily_hfq_cursor:
code = daily_hfq['code']
if code in code_daily_dict:
aufactor = daily_hfq['close'] / code_daily_dict[code]['close']
aufactor = round(aufactor, 3)
update_requests.append(
UpdateOne(
{'code': code, 'date': date, 'index': False},
{'$set': {'aufactor': aufactor}}))
if len(update_requests) > 0:
update_result = DB_CONN['daily'].bulk_write(update_requests, ordered=False)
print('Fill aufactor, date: %s, modified: %4d'
% (date, update_result.modified_count),
flush=True)
"""
current_daily['aufactor'] * current_daily['pre_close'] =
last_daily['close'] * last_daily['aufactor']
current_daily['volume'] * current_daily['pre_close'] =
last_daily['close'] * last_daily['volume']
"""
if __name__ == '__main__':
df = DailyFixing()
# df.fill_is_trading(begin_date='2018-01-01', end_date='2019-7-28')
# df.fill_is_trading(autype='hfq', begin_date='2018-01-01', end_date='2019-7-28')
# df.fill_suspension_dailies(autype='hfq', begin_date='2018-01-01', end_date='2019-7-28')
# df.fill_suspension_dailies(begin_date='2018-01-01', end_date='2019-7-28')
df.fill_aufactor_pre_close(begin_date='2018-01-01', end_date='2019-7-28')
省事起见,新股、st股的涨跌停买卖限制,就不写了。后续按照开盘价买入的规则进行回测,假装遇到涨停开盘也能买入,遇到跌停卖出也能出手……