简单回测程序
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 9 22:49:09 2021
@author: ASUS
"""
import talib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import tushare as ts
def shizhixuangu(riqi):
pro = ts.pro_api()
shuju = pro.daily_basic(ts_code='',trade_date=riqi,fields='ts_code,trade_date,pe,pe_ttm,pb,ps,total_mv,circ_mv')
#由于接口不同,只能在函数内部实现市值数据的提取
lx1 = ['市值大于1000亿']
lx2 = ['市值大于500亿']
lx3 = ['市值大于200亿']
qita = []
for i in range(len(shuju)):
shizhi = float(shuju.iloc[i]['total_mv'])
#print(shizhi) #经验证,可以得到市值的数据并输出
if shizhi >10000000.0:
lx1.append(shuju.iloc[i]['ts_code'])
elif shizhi>=5000000 and shizhi<10000000:
lx2.append(shuju.iloc[i]['ts_code'])
elif shizhi>=2000000 and shizhi<5000000:
lx3.append(shuju.iloc[i]['ts_code'])
else:
qita.append(shuju.iloc[i]['ts_code'])
return lx1,lx2,lx3,qita
def pandingzhibiao(shuju): #输出当前股票的买卖点
'''金叉买入,但是由于指标数据是按交易日的‘散点’,所以导致‘金叉’难以与当天对应,
所以,判断标准是‘前一天DIF<=DEA,当天DIF>=DEA’,同样,卖出标准是‘前天DIF>=DEA,
当天DIF<=DEA’;'''
shuju1 = shuju.reindex(index=shuju.index[::-1])
DIF,DEA,MACD = talib.MACD(shuju1['close'],fastperiod=12,slowperiod=26,signalperiod=9)
DIF[np.isnan(DIF)] = 0
DEA[np.isnan(DEA)] = 0
MACD[np.isnan(MACD)] = 0
maimai = {}
for i in range(len(shuju)):
if i > 32: #因为MACD函数输出指标数据的都是比行情数据少33个数的
DIF1 = DIF.iloc[i]
DIF0 = DIF.iloc[i-1]
DEA1 = DEA.iloc[i]
DEA0 = DEA.iloc[i-1]
if DIF0 <= DEA0:
if DIF1 >= DEA1:
maimai.update({i:[shuju1.iloc[i]['trade_date'],shuju1.iloc[i]['close'],'买入']})
#注意,要用已经倒过来的行情数据close,才对应指标
elif DIF0 >= DEA0:
if DIF1 <= DEA1:
maimai.update({i:[shuju1.iloc[i]['trade_date'],shuju1.iloc[i]['close'],'卖出']})
#或者可以直接用MACD来判断指标,更简单
#买卖点将会是一个买点,接着一个卖点,接着一个买点...但是,在其他的指标判定中却不一定了,还可能是连续几个买点或卖点
if i >= 19: #循环中i是从0开始的,当i=19时表示此时是shuju中的第20个数据
twenty = shuju1.iloc[(i-19):(i+1)]['vol'].mean() #以切片的方法,取shuju中以当前日期开始往回的20天的成交量数据的平均值
if shuju1.iloc[i]['vol'] >= 3*twenty: #我们要的是比20日平均成交量还高2倍的,也就是20日成交量3倍以上的
maimai.update({i:[shuju1.iloc[i]['trade_date'],shuju1.iloc[i]['close'],'卖出']})
# print(maimai) #注意,此时的买入卖出点的价格是‘字符串’类型
return maimai
def jiaoyi(d,benjin,maimai):
shuju1 = d.reindex(index=d.index[::-1])
shoushu = 0
jiaoyicishu = 0
shouyilv = []
zongshouyilv = []
shouyishijian = []
kuisun = ['亏损交易的日期']
for shijian in maimai:
if shoushu == 0:
if maimai[shijian][2] == '买入' :
mrujia = float(maimai[shijian][1])
shoushu = benjin // (mrujia*100.0)
shengyu = benjin % (mrujia*100.0)
benjin = shengyu
mairushijian = int(shuju1.iloc[shijian]['trade_date'])
else:
continue
else:
if maimai[shijian][2] == '卖出':
mchujia = float(maimai[shijian][1])
benjin = benjin + (mchujia*shoushu*100.0*0.999)
x = ((mchujia-mrujia)*shoushu*100.0)/(mrujia*shoushu*100.0)
shouyilv.append(x)
if x < 0:
kuisun.append(mairushijian) #如果此次交易结果亏损,则把买入时间加入列表,输出
jiaoyicishu = jiaoyicishu + 1
shoushu = 0
# print('买入时间%d,买入价%.4f,卖出价%.4f,本金%.2f,此次收益率%.4f' %(mairushijian,mrujia,mchujia,benjin,x))
zongshouyilv.append((benjin-1000000) / 1000000)
shouyishijian.append(shuju1.iloc[shijian]['trade_date']) #这里想把交易的时间打出去,但是shuju是没有倒过来的,要倒过来
else:
continue
geguchenggonglv = 1 - ((len(kuisun)-1)/jiaoyicishu) #个股成功率
# print(zongshouyilv[len(zongshouyilv)-1],jiaoyicishu,kuisun,geguchenggonglv) #输出个股最后总收益率和交易次数和亏损时间
return shouyishijian,zongshouyilv,geguchenggonglv
def xunhuanjiaoyi(lx,kaishiriqi,jieshuriqi):
lxzongshouyilv = []
lxgeguchenggonglv = []
bufuhe = 0
buzaiqijiandegupiao = ['不在期间的股票:']
shouyi = 0
chenggonglv = 0
for i in range(len(lx)):
try:
if i > 0: #因为lx1等列表第一个元素是市值提示标签
daima = lx[i]
shuju = ts.pro_bar(ts_code=daima,adj='qfq',start_date=kaishiriqi,end_date=jieshuriqi) #导入数据
benjin = 1000000
maimaidian = pandingzhibiao(shuju) #得到买卖点
shouyishijian,gegushouyilv,geguchenggonglv = jiaoyi(shuju,benjin,maimaidian)
#print(gegushouyilv)
geguzongshouyi = gegushouyilv[-1]
lxzongshouyilv.append([lx[i],geguzongshouyi])
lxgeguchenggonglv.append(geguchenggonglv)
#以列表[[代码,收益率],...]的格式把每个个股的交易结果集合起来
except:
bufuhe = bufuhe + 1 #用来记录发生错误的次数,也就是用来记录股票未在期间内上市的个数;
#交易不成功的原因包括:是期间未上市、期间上市
buzaiqijiandegupiao.append(lx[i]) #把不在期间的股票代码输出来,以便后面核对
continue
print('不在期间的股票数:%d'%bufuhe,'成功交易的股票数:%d'%len(lxzongshouyilv),buzaiqijiandegupiao)
#print(lxgeguchenggonglv) #验证是否成功得到个股成功率列表
for i in range(len(lxzongshouyilv)):
shouyi = shouyi + lxzongshouyilv[i][1]
chenggonglv = chenggonglv + lxgeguchenggonglv[i]
pingjunshouyi = shouyi/(len(lx)-bufuhe)
pingjunchenggonglv = chenggonglv/(len(lx)-bufuhe)
#print(lxzongshouyilv)
return pingjunshouyi,lxzongshouyilv,pingjunchenggonglv
print('请输入选股依据的日期:') #注意不能是节假日,节假日得不到数据
dangqianriqi = input()
lx1,lx2,lx3,qita = shizhixuangu(dangqianriqi)
#print(len(qita))
print('请输入开始时间:')
kaishiriqi = input()
print('请输入结束时间:')
jieshuriqi = input()
'''
del lx2[0]
lx1 += lx2'''
#经验证,将lx2和lx1合并后280左右只股票,可以运行程序
lx1pjshouyi,lx1shouyiliebiao,pjchenggonglv = xunhuanjiaoyi(lx1,kaishiriqi,jieshuriqi)
print('1000亿市值以上个股平均收益率:%.4f'%lx1pjshouyi,'平均成功率:%.4f'%pjchenggonglv)
#lx2pjshouyi,lx2shouyiliebiao,pjchenggonglv = xunhuanjiaoyi(lx2,kaishiriqi,jieshuriqi)
#print('500亿--1000亿市值个股平均收益率:%.4f'%lx2pjshouyi,'平均成功率:%.4f'%pjchenggonglv)
#lx3pjshouyi,lx3shouyiliebiao,pjchenggonglv = xunhuanjiaoyi(lx3,kaishiriqi,jieshuriqi)
#print('200亿--500亿市值个股平均收益率:%.4f'%lx3pjshouyi,'平均成功率:%.4f'%pjchenggonglv)
#qitapjshouyi,qitashouyiliebiao,pjchenggonglv = xunhuanjiaoyi(qita,kaishiriqi,jieshuriqi)
#print('小于200亿市值个股平均收益率:%.4f'%qitapjshouyi,)