鉴于近期空闲时间比较少,本篇文章采用不定时更新的方式来写,如大家有更好的思路也可以评论区一起讨论....
目前进度:
2021-07-13 梳理、修改思路
2021-07-14 步骤1代码完成
1、前两篇文章我们分别对股票量化交易策略进行了定义(详见第一篇)、并对A股所有历史行情数据进行了爬取保存以备后续的数据分析(详见第二篇)
2、策略是否有效可行,我们需要根据大量的历史行情数据来进行回测验证。
1、将量化交易策略用于单个股票进行回测,并将回测结果可视化(策略收益趋势、最大回撤幅度),交易日志展现(交易日期、买入/卖出价格、交易金额、收益情况-金额/幅度)
2、将量化交易策略用于多个股票(可自定义股票池)进行回测,并将回测结果可视化(策略总收益趋势、最大回撤幅度),交易日志展现(交易日期、买入/卖出价格、交易金额、收益情况-金额/幅度)
3、优化策略,若发现原策略有缺陷或收益不符合预期,将进行适当的拟合以达到优化目的。
4、若策略验证可行,则开发自动提醒功能,监测股票池中个股的行情走势,出现买卖点时是自动推送邮件到自己的邮箱(股票名、策略建议-出现买入/卖出点、买入/卖出价格、近日行情趋势参考线)。
对交易策略进行量化定义→自定义股票池→准备历史行情数据→策略实现→进行回测→数据分析
买入点定义:当股价在连续下跌行情中,出现某日突破5日均线时,次日以5日均价挂买
即:设某交易日为N,MA5连续下跌天数为X,当N-1至N-X区间的MA5连续下跌,且N收盘价>N的收盘MA5价,则在N+1日以N+1的开盘MA5价挂买,如图:
卖出点定义:当买入后当日的收盘价未站稳5日均线,或买入之后在某日跌破5日均线的,则次日开盘卖出。
即:设买入日为N,卖出日为M,对N及N之后的每个交易日依次检验收盘价是否低于当日收盘后MA5价,若低于则次日卖出,如图:
准备:①第二篇文章中所讲的个股历史行情数据已爬取完成②安装本次所需的第三方模块
1、自定义股票池并读取
①创建自定义股票池文件,命名为“stockcodes.csv”,将待回测的股票代码复制到文件当中,本次回测股票池将加入所有A股代码,大家可根据需要自行添加股票代码到股票池文件中,例如:
读取:
#步骤-1
#读取股票池CSV文件,返回stockcodes['600010',...]
def ReadCSV(filename):
stockcodes = [] #个股代码列表
count = 0 #个股代码计数
try:
with open(filename,'r') as f:
reader = csv.reader(f)
for row in reader:
stockcodes.append(row[0])
count += 1
print('读取股票池成功:{}'.format(count))
re = 1 #请求继续运行
except:
print('股票池读取失败,检查文件位置及名称:{}'.format(filename))
re = 0 #请求停止运行
return stockcodes,re
②读取数据库中的所有个股表名
#读取数据库STOCK中的所有股票数据表 s_code,返回tablenames['s_600010',...]
def ReadTableName(dbinfo):
tablenames = [] #数据表名列表
#连接数据库
try:
db = pymysql.connect(
host = dbinfo['host'],
user = dbinfo['user'],
password = dbinfo['password'],
database = dbinfo['dbname'],
port = dbinfo['port']
)
cursor = db.cursor()
print('数据库连接成功!正在读取表名...')
re = 1
except:
print('数据库连接失败,请检查数据库信息配置!')
re = 0
if re :
sql_showtables = 'show tables'
cursor.execute(sql_showtables) #查询表名
for tablename in cursor:
tablenames.append(tablename[0])
print('数据表名称读取完成!')
return tablenames,re
③检查股票池中个股数据表是否已存在于数据库,若不存在则需要修改股票池或重新爬取数据
#检查股票池中个股数据是否已存在于数据库,返回miss_tablename['600010',...]
def CheckTableName(stockcodes,tablenames):
re = 1
for stockcode in stockcodes:
s_stockcode = 's_'+stockcode
if s_stockcode not in tablenames:
re = 0 #请求中止程序
print('数据表缺失,请检查或重新爬取该个股数据:{}'.format(s_stockcode))
else:
continue
print('股票池与数据表核对完成!')
return re
④步骤1运行函数
#运行步骤-1
def Step_1(filename,dbinfo):
sotckcodes,re = ReadCSV(filename) #读取股票池
if re: #检验是否继续-成功读取股票池
tablenames,re = ReadTableName(dbinfo) #读取表名
if re: #检验是否继续-成功获取数据库表名
re = CheckTableName(sotckcodes,tablenames) #检查是否有表缺失
return sotckcodes,re
2、读取目标个股历史行情数据,返回DataFrame
检验:可回测区间是否超过X+1天
3、计算个股在设定日期区间内的开盘及收盘MA5价格并与行情数据一起保存为新的数据表,命名为 “MA5_股票代码_股票名称”
收盘MA5价格:设某交易日为N,则N的收盘MA5价格=(N的收盘价+N-1的收盘价+...+N-4的收盘价)/5
开盘MA5价格:设某交易日为N,则N的开盘MA5价格=(N的开盘价+N-1的收盘价+...+N-4的收盘价)/5
传入参数:回测开始日期(包含当日)、回测结束日期(包含当日)
返回结果:创建数据表保存,字段如下图:
日期 | 收盘价 | 开盘价 | 最低价 | 收盘MA5 | 开盘MA5 |
4、读取个股“MA5_股票代码_名称”表
返回结果:股票代码,股票名称,['日期','收盘价',....]
5、遍历个股历史行情数据,找到所有策略买入点
传入参数:步骤4“MA5_股票代码表”读取结果、收盘MA5价下跌天数X的配置
返回结果:列表 ['买入点交易日','买入价']
6、根据买入点,找到对应的策略卖出点
传入参数:步骤4“MA5_股票代码_名称”表的读取结果、步骤5返回的结果(买入点列表)
返回结果:代码,名称,列表[['买入点交易日','买入价格','对应卖出点交易日','对应卖出价格'],...]
若买入之后暂未出现对应卖出点,则返回:代码,名称[...,['买入点交易日','买入价格','null','null'],..]
7、找出除权日(此处多谢一位粉丝的提醒)
除权日定义:判断标准为(N-1日收盘价)/(N日收盘价)-100%>11%,即次日跌幅超过11%
解释:以上为除权日的粗略判断,因我们爬取到的股价未进行动态除权处理,所以可能会极大地影响到结果准确性,所以本次分析中,我们将会把持仓期间出现除权日的交易进行抹除不做统计分析。
传入参数:步骤4“MA5_股票代码表”的读取结果
返回结果:代码,名称,列表['除权日期']
8、筛除含除权日的交易记录
传入参数:步骤6返回的买卖点对应列表、步骤7返回的除权日列表
若买点交易日与卖点交易日之间含有除权日,则删除该买点和对应卖点
返回结果:
代码,名称,列表[['买入点交易日','买入价格','对应卖出点交易日或null','对应卖出价格或null'],...]
9、生成交易策略表
对所有的”MA5_股票代码_名称表"进行买卖点标记,并合并为一张交易策略表,命名为tactics表
日期 | 股票代码 | 名称 | 开盘价 | 收盘价 | 最低价 | 收盘MA5 | 开盘MA5 | 操作策略 |
买/卖/null |
以上为准备工作,接下来我们还需要创建3个表来对资金情况、持仓情况、交易日志进行记录:
account表:记录每日资金变化
position表:记录每日持仓变化
log表:记录交易日志
9、记录资金账户情况,数据保存到数据表“account”(记录每日资金账户相关数据)
数据表初始化:
日期 | 可用资金 | 总资产 | 持仓价值 | 持股数 | 仓位占比 | 年化收益率 | 累计收益率 |
开始日期 | 设定初始资金 | 0 | 0 | 0 | 0.00% | 0.00% | 0.00% |
10、记录持仓情况,数据保存到数据表“position”(记录每日持仓相关数据)
数据表初始化:
日期 | 股票代码 | 名称 | 市值 | 收盘价 | 成本 | 数量 | 盈亏幅度 | 盈亏金额 |
每股 | 股 | 0.00% |
市值=现价*数量
成本=(Σ买入时股票单价*买入数量) / 总数量
10、记录交易日志,数据保存到数据表“log”(记录每天的交易数据)
数据表初始化:
日期 | 股票代码 | 名称 | 交易行为 | 价格 | 数量 | 结果 |
日期 | 代码 | 名称 | 买入/卖出 | 每股价格 | 交易数量 | 成功/失败 |
数量示例:买入100股记 “100”,卖出100股记 “-100”
结果:“可用资金不足”、“当日最低价未触达挂买价”时将买入失败;“持仓不足卖出时”将卖出失败;
接下来,开始进行回测
11、遍历个股交易日数据,进行回测
读取交易策略表tactics,
读取资金账户表account,
读取持仓表position,
读取日志表log
对tactics交易策略表进行遍历,判断当日是否符合策略交易条件
是:执行买入/卖出操作,更新log(日志表)、position(持仓表)、account(资金账户表)
否:更新position(持仓表)、account(资金账户表)
12、定义执行买入/卖出操作函数
传入参数:单笔交易数(默认100股),策略(买/卖),资金可用余额
买入检验:①当日最低股价应不小于开盘MA5价格;②账户可用资金应不小于本次交易预算
卖出检验:①可用持仓数量应不小于将卖出数量
返回结果:是否买入/卖出 成功 True/False,买入/卖出总金额
接下来我们对回测结果进行数据统计及可视化展示
13、数据统计
策略总胜率:策略交易盈利次数/总交易次数
策略个股胜率:每只股票的策略交易盈利次数/总交易次数
策略平均收益率:Σ每次策略交易盈利率/总交易次数
策略个股平均收益率:每只股票的策略平均收益率
14、数据展示
策略的总收益率趋势图,年化收益率,最大回撤
15、邮件自动推送
实盘监控,出现策略买卖点后推送邮件提醒:股票名、代码、近30日趋势图、建议买入/卖出价