在哈尔兹法则策略量化一,我们的买点完全遵照哈尔兹法则的买入条件:即股从低价上涨10%之后才介入,然而我们看到最近三年收益及正收益概率都不太理想,如下图:
今天我们尝试买点做一些优化,即在哈尔兹法则的买入条件,股从低价上涨10%的基础上加入5日均线大于10均线,最近三年收益如下图:
从图二对比图一原始的哈尔兹法则的买入条件收益,我们可以看到,收益,以及正收益率都是有所提高,尤其是2019年,收益达到5.58倍,但是,但是,我们看到最近年正收益率都很低,2018年尤其差0.26,2019年最好,也只有0.45,连50%都没有超过,这个胜率,实战操作很尴尬了。
另外贴上最近三年收益汇总,可以看到买入之后亏损的次数非常多
2017年 收益,0.64 概率 0.37 正收益次数-> 74 负收益次数-> 126
2018年 收益0.06 概率 0.26 正收益次数-> 41 负收益次数-> 117
2019年 total_shouyi= 5.58 概率 0.45 正收益次数-> 74 负收益次数-> 91
代码部分如下:
# -*- coding: utf-8 -*-
import numpyas np
import pandasas pd
import matplotlib.pyplotas plt
import os
import shutil
import time
import matplotlib
def Haerzi(start,end):
mairuhou_mark =0
zhengshouyi_num =0
fushouyi_num =0
cur_dir = os.getcwd()# get current path
folder_name ='result'
dir_new = os.path.join(cur_dir, folder_name)
end_date = ['2019/12/31','2019/12/30','2019/12/29','2019/12/28']
#删掉结果
if os.path.exists(dir_new +"\\" + start.replace("/","_") +"__" + end.replace("/","_") +"celie_xiangxi" +'.txt'):
os.remove(dir_new +"\\" + start.replace("/","_") +"__" + end.replace("/","_") +"celie_xiangxi" +'.txt')
if os.path.exists(dir_new +"\\" + start.replace("/","_") +"__" + end.replace("/","_") +"dangtian_buy" +'.txt'):
os.remove(dir_new +"\\" + start.replace("/","_") +"__" + end.replace("/","_") +"dangtian_buy" +'.txt')
if os.path.exists(dir_new +"\\" + start.replace("/","_") +"__" + end.replace("/","_") +'"dangtian_sell"' +'.txt'):
os.remove(dir_new +"\\" + start.replace("/","_") +"__" + end.replace("/","_") +'"dangtian_sell"' +'.txt')
#设置路径
dir_list = []
lujing =r'C:\gupiao\gupiaoci'
for iin os.listdir(r'C:\gupiao\gupiaoci'):
a = i.split('.')[0]
if a[0] !='3':
dir_list.append(lujing+'\\'+i)
#创业板指数
df = pd.read_table(r'C:\gupiao\gupiaoci' +"\\399006.txt",header=1,usecols=range(6), parse_dates=[0], index_col=0,encoding='gb2312')
df.index.rename('date', inplace=True)
df.rename(columns={' 开盘':'open', ' 最高':'high', ' 最低':'low', ' 收盘':'close',' 成交量':'vol'}, inplace=True)
df = df.drop('数据来源:通达信')
df.close = df.close.astype(np.float32)#设置为32位,4字节,默认64位,8字节,append到list之后就会多小数位
df.low = df.low.astype(np.float32)#设置为32位,4字节,默认64位,8字节,append到list之后就会多小数位
df.vol = df.vol.astype(np.int64)
df2 = df[start:end][:len(df.vol)-1]
#均线
ma5_chuangzhi = df2.close.rolling(window=5,center=False).mean()
ma5_chuangzhi = ma5_chuangzhi [start:end]
ma10_chuangzhi = df2.close.rolling(window=10,center=False).mean()
ma10_chuangzhi = ma10_chuangzhi [start:end]
ma30_chuangzhi = df2.close.rolling(window=30,center=False).mean()
ma30_chuangzhi = ma30_chuangzhi [start:end]
#深圳指数
df = pd.read_table(r'C:\gupiao\gupiaoci' +"\\399107.txt",header=1,usecols=range(6), parse_dates=[0], index_col=0,encoding='gb2312')
df.index.rename('date', inplace=True)
df.rename(columns={' 开盘':'open', ' 最高':'high', ' 最低':'low', ' 收盘':'close',' 成交量':'vol'}, inplace=True)
df = df.drop('数据来源:通达信')
df.close = df.close.astype(np.float32)#设置为32位,4字节,默认64位,8字节,append到list之后就会多小数位
df.low = df.low.astype(np.float32)#设置为32位,4字节,默认64位,8字节,append到list之后就会多小数位
df.vol = df.vol.astype(np.int64)
df2 = df[start:end][:len(df.vol)-1]
#均线
ma5_shenzhen = df2.close.rolling(window=5,center=False).mean()
ma5_shenzhen = ma5_shenzhen[start:end]
ma10_shenzhen = df2.close.rolling(window=10,center=False).mean()
ma10_shenzhen = ma10_shenzhen[start:end]
ma30_shenzhen = df2.close.rolling(window=30,center=False).mean()
ma30_shenzhen = ma30_shenzhen[start:end]
#个股
total_shouyi=1
total_date_buy = []
total_date_sell = []
buy_date_zhengshouyi = []
buy_date_fushouyi = []
for jin dir_list:
#获取数据
name = (j.split('\\')[-1]).split('.')[0]
df = pd.read_table(j,header=1,usecols=range(6), parse_dates=[0], index_col=0,encoding='gb2312')
df.index.rename('date', inplace=True)
df.rename(columns={' 开盘':'open', ' 最高':'high', ' 最低':'low', ' 收盘':'close',' 成交量':'vol'}, inplace=True)
df = df.drop('数据来源:通达信')
df.close = df.close.astype(np.float32)#设置为32位,4字节,默认64位,8字节,append到list之后就会多小数位
df.low = df.low.astype(np.float32)#设置为32位,4字节,默认64位,8字节,append到list之后就会多小数位
df.vol = df.vol.astype(np.int64)
df2 = df[start:end][:len(df.vol)-1]
df = df[start:end]
#均线
ma5 = df2.close.rolling(window=5,center=False).mean()
ma5= ma5[start:end]
ma3 = df2.close.rolling(window=3,center=False).mean()
ma3= ma3[start:end]
ma8 = df2.close.rolling(window=8, center=False).mean()
ma8 = ma8[start:end]
ma15 = df2.close.rolling(window=15, center=False).mean()
ma15 = ma15[start:end]
ma10 = df2.close.rolling(window=10,center=False).mean()
ma10= ma10[start:end]
ma12 = df2.close.rolling(window=12, center=False).mean()
ma12 = ma12[start:end]
ma20 = df2.close.rolling(window=20,center=False).mean()
ma20= ma20[start:end]
ma30 = df2.close.rolling(window=30,center=False).mean()
ma30 = ma30[start:end]
ma60 = df2.close.rolling(window=60,center=False).mean()
ma60 = ma60[start:end]
ma120 = df2.close.rolling(window=120,center=False).mean()
ma120 = ma120[start:end]
ma200 = df2.close.rolling(window=200, center=False).mean()
ma200 = ma200[start:end]
ma250 = df2.close.rolling(window=250,center=False).mean()
ma250 = ma250[start:end]
#均量线
vol5 = df2.vol.rolling(window=5,center=False).mean()
vol5 = vol5[start:end]
vol3 = df2.vol.rolling(window=3,center=False).mean()
vol3 = vol3[start:end]
vol8 = df2.vol.rolling(window=8,center=False).mean()
vol8 = vol8[start:end]
vol10 = df2.vol.rolling(window=10,center=False).mean()
vol10 = vol10[start:end]
vol20 = df2.vol.rolling(window=20,center=False).mean()
vol20 = vol20[start:end]
vol15 = df2.vol.rolling(window=15,center=False).mean()
vol15 = vol15[start:end]
vol30 = df2.vol.rolling(window=30,center=False).mean()
vol30 = vol30[start:end]
vol60 = df2.vol.rolling(window=60, center=False).mean()
vol60 = vol60[start:end]
#设置初始化数据
#买入条件,buy_status = True,其次是chicang_status = False
chicang_status =False#chicang_status==False的时候表示空仓状态,可以买入,买入之后需要将其设置为True,表示股票为持有状态
buy_status =False#初始化buy的状态为False,当遇到买点出现时,设置状态为True,表示之后可以买入
buy_price = []
buy_date = []
sell_price = []
sell_date = []
low_vol = []
low_vol_index = []
fengexian_mark =0
#low_vol
df_temp = df
temp_vol = df.vol
temp_index = df.index
ma3_temp = ma3
ma5_temp = ma5
ma8_temp = ma8
ma15_temp = ma15
ma10_temp = ma10
ma12_temp = ma12
ma20_temp = ma20
ma30_temp = ma30
ma60_temp = ma60
ma120_temp = ma120
ma200_temp = ma200
ma250_temp = ma250
vol5_temp = vol5
vol3_temp = vol3
vol8_temp = vol8
vol15_temp = vol15
vol10_temp = vol10
vol20_temp = vol20
vol30_temp = vol30
vol60_temp = vol60
temp_vol_max =0
for jin range(60,len(temp_vol)):
close_min = df_temp.close[j-60:j-1].min()#60天内最低价
if buy_status ==False and chicang_status ==False and df_temp.close[j] < close_min:
buy_status =True#买点出现时,设置状态为True,表示之后可以买入
vol_max_dangtian_index = temp_index[j]
close_min_new = df_temp.close[j]
elif buy_status ==True and chicang_status ==False:
if df_temp.at[temp_index[j-1],'close'] >=300 or (df_temp.high[j]-df_temp.close[j-1])/df_temp.close[j-1] < -0.09:
chicang_status =False
buy_status =False
break
elif (df_temp.close[j]-close_min_new)/close_min_new >0.1 and ma5_temp[j] > ma10_temp[j]:#买点优化,即在哈尔兹法则的买入条件,股从低价上涨10%的基础上加入5日均线大于10均线
if len(gupiaochichang) >=1:
for gupiaochichang_itemin gupiaochichang:
if gupiaochichang_item[0] == temp_index[j]and gupiaochichang_item[1] >=1:
#print(gupiaochichang_item)
chicang_status =False
buy_status =False
if chicang_status ==False and buy_status ==False:
continue
chicang_status =True
buy_status =False#买入之后设置状态为False
buy_price_m = df_temp.at[temp_index[j],'close']
buy_date_temp = temp_index[j]
buy_price.append(df_temp.at[temp_index[j],'close'])
buy_date.append(temp_index[j])
total_date_buy.append([name,temp_index[j]])
fengexian_mark =1
close_max = df_temp.close[j]#加入买入当天就是当前最高价
#股票持仓,买日期加1,买日期不在list,则加入
for gupiaochichuang_itemin gupiaochichang:
if gupiaochichuang_item[0] == temp_index[j]:
gupiaochichuang_item[1] +=1
if len(gupiaochichang) ==0:
gupiaochichang.append([temp_index[j],1])
else:
for i22in range(len(gupiaochichang)):
if len(gupiaochichang) !=0 and i22 ==len(gupiaochichang) -1 and gupiaochichang[i22][0] != temp_index[j]:#找到最后一个还没有找到买日期,加将买日期加入
gupiaochichang.append([temp_index[j],1])
#存结果
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write("股票名称--" + name +'\n' +"开始日期--" + start +'\n' +"结束日期--" + end +'\n' )
f.write("最大vol/当天vol 大于20当天日期----->" +str(vol_max_dangtian_index) +'\n')
f.write('\n' +"股票名称--" + name +"--买入价格--" +"--步长--" +"--" +"--j--" +str(j) +"--" +str(df_temp.at[temp_index[j],'close']) +"--买入日期--" +str(temp_index[j]))
elif chicang_status ==True:
if close_max < df_temp.close[j]:
close_max = df_temp.close[j]#动态计算最高点价格
if (close_max - df_temp.close[j])/ df_temp.close[j] >=0.1:#最高点下跌10%,卖出
chicang_status =False
sell_price.append(df_temp.close[j])
sell_date.append(temp_index[j])
total_date_sell.append([name,temp_index[j]])
if (df_temp.close[j]-buy_price_m)/buy_price_m >0:
zhengshouyi_num +=1#正收益次数加1
else:
fushouyi_num +=1#负收益次数加1
total_shouyi *= (1 + (df_temp.close[j]-buy_price_m)/buy_price_m)
mairuhou_mark =0
buy_date_zhengshouyi.append(buy_date_temp)
#存结果
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write('\n' +"股票名称--" + name +"--最高点下跌10%,卖出价格--" +"--步长--" +"--" +"--j--" +str(j) +"--" +str(df_temp.close[j]) +"--卖出日期--" +str(temp_index[j]))
elif (df_temp.close[j] - buy_price_m)/buy_price_m < -0.05:
chicang_status =False
sell_price.append(df_temp.close[j])
sell_date.append(temp_index[j])
total_date_sell.append([name,temp_index[j]])
fushouyi_num +=1#负收益次数加1
total_shouyi *= (1 -0.05)
mairuhou_mark =0
# buy_date_fushouyi.append([name,buy_date_temp])
buy_date_fushouyi.append(buy_date_temp)
#存结果
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write('\n' +"股票名称--" + name +"--止损-0.05,卖出价格--" +"--步长--" +"--" +"--j--" +str(j) +"--" +str(df_temp.close[j]) +"--卖出日期--" +str(temp_index[j]))
elif mairuhou_mark >=10 and df_temp.close[j] < ma8_temp[j]:
total_shouyi *= (1 + (df_temp.close[j] - buy_price_m)/buy_price_m)
chicang_status =False
sell_price.append(df_temp.close[j])
sell_date.append(temp_index[j])
total_date_sell.append([name,temp_index[j]])
mairuhou_mark =0
if (df_temp.close[j] - buy_price_m)/(buy_price_m) >0:
zhengshouyi_num +=1
buy_date_zhengshouyi.append(buy_date_temp)
#print("持有超过10天卖出:",(df_temp.close[j] - buy_price_m)/(buy_price_m))
#存结果
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write('\n' +"股票名称--" + name +"--持有超过5天卖出,大于0.01,卖出价格--" +"--" +"--j--" +str(j) +"--" +str(df_temp.close[j]) +"--卖出日期--" +str(temp_index[j]))
else:
fushouyi_num +=1
buy_date_fushouyi.append(buy_date_temp)
#print("持有超过10天卖出:",(df_temp.close[j] - buy_price_m) / (buy_price_m ))
#存结果
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write('\n' +"股票名称--" + name +"--持有超过15天卖出,小于0.01,大于0,卖出价格--" +"--j--" +str(j) +"--" +str(df_temp.close[j]) +"--卖出日期--" +str(temp_index[j]))
else:
mairuhou_mark +=1
gupiaochichang.sort(key=lambda x:x[0])
#if zhengshouyi_num+fushouyi_num != 0:
#print(start,"----",end,"total_shouyi=",'%.2f'%(total_shouyi),"概率",'%.2f'%(zhengshouyi_num/(zhengshouyi_num+fushouyi_num)),"正收益次数->",zhengshouyi_num,"负收益次数->",fushouyi_num)
#存结果
cur_dir = os.getcwd()# get current path
folder_name ='result'
dir_new = os.path.join(cur_dir, folder_name)
#存买入,卖出价格,日期
if len(buy_price) >len(sell_price):
if buy_date[-1]in end_date:
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"dangtian_buy" +'.txt','a')as f:
f.writelines('\n' + name +'\n' +'buy: ' +str(buy_price[-1]) +str(buy_date[-1]) +'\n')
if len(sell_price) !=0:
if sell_date[-1]in end_date:
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +'"dangtian_sell"' +'.txt','a')as f:
for rrin range(len(sell_price)):
if rr ==0:
f.writelines('\n' + name +'\n' +'buy: ' +str(buy_price[rr]) +str(buy_date[rr]) +'\n' +'sell: ' +str(sell_price[rr]) +str(sell_date[rr]) +'\n')
else:
f.writelines('buy: ' +str(buy_price[rr]) +str(buy_date[rr]) +'\n' +'sell: ' +str(sell_price[rr]) +str(sell_date[rr]) +'\n')
if zhengshouyi_num+fushouyi_num !=0:
zshouyi =float(zhengshouyi_num/(zhengshouyi_num+fushouyi_num))
fshouyi =float(fushouyi_num/(zhengshouyi_num+fushouyi_num))
total_num = zhengshouyi_num+fushouyi_num
total_date_buy.sort(key=lambda x:x[1])
total_date_sell.sort(key=lambda x:x[1])
x = []
y = []
for iin buy_date_zhengshouyi:
if inot in x:
x.append(i)
for iin buy_date_fushouyi:
if inot in y:
y.append(i)
if fengexian_mark ==1:
with open(dir_new +"\\" + start.replace("/","_") +"__" + end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write('\n' +'\n' +"--------------------分割线----------------------" +'\n' +'\n')
buy_date_zhengshouyi_count = []
buy_date_fushouyi_count = []
i =0
x.sort()
y.sort()
for iin x:
if buy_date_zhengshouyi.count(i) >=0:
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write('\n' +"正收益买日期--" +str(buy_date_zhengshouyi.count(i)) +"--" +str(i))
buy_date_zhengshouyi_count.append(buy_date_zhengshouyi.count(i))
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write('\n' )
for iin y:
if buy_date_fushouyi.count(i) >=0:
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write('\n' +"负收益买日期--" +str(buy_date_fushouyi.count(i)) +"--" +str(i))
buy_date_fushouyi_count.append(buy_date_fushouyi.count(i))
if zhengshouyi_num+fushouyi_num !=0:
print(start,"----",end,"total_shouyi=",'%.2f'%(total_shouyi),"概率",'%.2f'%(zhengshouyi_num/(zhengshouyi_num+fushouyi_num)),"正收益次数->",zhengshouyi_num,"负收益次数->",fushouyi_num)
with open(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt','a')as f:
f.write('\n' +'\n' +"正收益买日期count--" +str(len(buy_date_zhengshouyi_count)) +"--" +str(buy_date_zhengshouyi_count))
f.write('\n' +"负收益买日期count--" +str(len(buy_date_fushouyi_count)) +"--" +str(buy_date_fushouyi_count))
f.write('\n' +"买卖总次数" +str(zhengshouyi_num+fushouyi_num) +"--正收益买概率--" +str('%.2f'%(zhengshouyi_num/(zhengshouyi_num + fushouyi_num))) +"--正收益次数--" +str(zhengshouyi_num))
for iin range(len(gupiaochichang)):
if i ==0:
f.write('\n' +"买日期count:" +'\n' +str(gupiaochichang[i]))
else:
f.write('\n' +str(gupiaochichang[i]))
shutil.copy(dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +"celie_xiangxi" +'.txt',dir_new +"\\" + start.replace("/","_") +"__" +end.replace("/","_") +'_' +"--正收益买概率--" +str('%.2f'%(zhengshouyi_num / (zhengshouyi_num + fushouyi_num))) +"--倍数--" +str('%.2f'%(total_shouyi))+"--正收益次数--" +str(zhengshouyi_num) +"--负收益次数--" +str(fushouyi_num)+'.txt')
return [total_shouyi,zhengshouyi_num / (zhengshouyi_num + fushouyi_num)]
if __name__ =='__main__':
print('start',time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
cur_dir = os.getcwd()# get current path
folder_name ='result'
dir_new = os.path.join(cur_dir, folder_name)
start = ['2016/09/01','2017/09/01','2018/09/01']
end = ['2017/12/31','2018/12/31','2019/12/31']
gupiaochichang = []
total_shouyi_gailv = []
shouyi_huizong = []
gailv_huizong = []
for iin range(len(start)):
total_shouyi_gailv.append(Haerzi(start[i],end[i]))
for iin range(len(total_shouyi_gailv)):
shouyi_huizong.append(total_shouyi_gailv[i][0])
gailv_huizong.append(total_shouyi_gailv[i][1])
#绘图
matplotlib.rcParams['font.family'] ='SimHei' # SimHei黑体
matplotlib.rcParams['font.size'] =10
dir_new = os.path.join(cur_dir, folder_name)
file_name = dir_new +r'/' +'shouyi'
#收益图
plt.subplots_adjust(hspace=0.5)
fig1 = plt.subplot(211)
fig1.set_title("收益")
# 设置坐标轴范围
fig1.set_xlim(-1, 3)
fig1.set_ylim(0, 20)
# 设置坐标轴名称
fig1.set_xlabel('日期')
fig1.set_ylabel('收益')
# 设置坐标轴刻度
fig1.set_xticks = np.arange(-1, 4, 1)
for a, bin zip(end, shouyi_huizong):
fig1.text(a, b +0.1, '%.2f' % b, ha='center', va='bottom', color='red', fontsize=20)
fig1.plot(end, shouyi_huizong, color='blue', marker='o')
# 正收益概率图
fig2 = plt.subplot(212)
fig2.set_title("收益概率")
# 设置坐标轴范围
fig2.set_xlim(-1, 3)
fig2.set_ylim(0, 1.2)
# 设置坐标轴名称
fig2.set_xlabel('日期')
fig2.set_ylabel('收益概率')
# 设置坐标轴刻度
fig2.set_xticks = np.arange(-1, 4, 1)
for a, bin zip(end, gailv_huizong):
fig2.text(a, b +0.1, '%.2f' % b, ha='center', va='bottom', color='blue', fontsize=20)
fig2.plot(end, gailv_huizong, color='blue', marker='o')
plt.savefig(file_name, dpi=300)
print('end',time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))