看着别人炒股挣钱,心里总是心痒痒,但是每次一入市,总能被当韭菜收割,沉不住气。近期看了《海龟交易法则》,里面提到一些说法,觉得有点意思,所以拿历史数据试一试,探探究竟,不作为投资建议,仅供娱乐。
抛出书里的几个说法: 1、投资可以标准化,但是每几个人能坚持; 2、找到一个期望值为正的操作系统,不如有55%的概率是挣钱的,那么只要不断的重复,最终总可以赢钱,除非你是45%*45%...*45%的倒霉蛋; 3、风险空值,逐步试行; 4、简单就可以了,复杂的系统反而会出现过度拟合;
使用python进行数据收集和处理,最后进行结果分析和展示。使用到pandas库、金融数据库使用了akshare(免费,tushare也可以,但是要有积分),但是
akshare收集到的数据,与不同平台的进行比较(wind,东方财富)有微小偏差,暂忽略不计。
akshare:https://www.akshare.xyz/zh_CN/latest/data/stock/stock.html#id5
***本次回测操作方法为趋势交易,采用60日均线上穿250日均线时买入(认为开启往上涨的趋势),下穿时卖出(认为上涨结束开始下跌)
使用当前A股从2000年1月1日到2020年11月26日数据进行测试。
以当前数据的结论是:截止到2020年11月26日,包含持有的收益是(6000倍),但是需要操作的资金量大,操作股票数量较多,且存在连续亏损的年份,虽然总体为
正收益,但是执行起来信心难以保障。
1、获取机构名单,这个通过wind查询了机构的名单;
2、获取数据,通过akshare库获取每只股票从2000年开始,每一天的数据,采用后复权。
后复权:保证历史价格不变,在每次股票权益事件发生后,调整当前的股票价格。 后复权价格和真实股票价格可能差别较大,不适合用来看盘。 其优点在于,可以被看作投资者的长期财富增长曲线,反映投资者的真实收益率情况。
%%time # 读取全部A股数据 for i, row in org.iterrows(): code = row[\'akcode\'] t1 = row[\'上市日期\'] if t1 < datetime.datetime(2000,1,1): t1 = datetime.datetime(2000,1,1) t2 = datetime.datetime.now() print(str(i),code,\'开始读取。\') berr = True try: #调用API接口获取数据 df = ak.stock_zh_a_daily(symbol=code, start_date=t1.strftime("%Y%m%d"), end_date=t2.strftime("%Y%m%d"), adjust="hfq") except: print(code + \'读取\' + t1.strftime("%Y-%m-%d") + \' -- \' + t2.strftime("%Y-%m-%d") + \'有问题。\') berr = False l = [] # 合并 if berr: df = df.reset_index() # 修改名字 df.columns = [ x.upper() for x in df.columns] df.rename(columns={"DATE": "index"},inplace=True) df = df[df[\'OPEN\'].notnull()] # 通过rolling滚动计算均线和BOLL通道 df[\'MA_5\'] = df[\'CLOSE\'].rolling(window=5).mean() df[\'MA_10\'] = df[\'CLOSE\'].rolling(window=10).mean() df[\'MA_20\'] = df[\'CLOSE\'].rolling(window=20).mean() df[\'MA_30\'] = df[\'CLOSE\'].rolling(window=30).mean() df[\'MA_60\'] = df[\'CLOSE\'].rolling(window=60).mean() df[\'MA_250\'] = df[\'CLOSE\'].rolling(window=250).mean() df[\'BOLL_STD\'] = df[\'CLOSE\'].rolling(window=250).std() df[\'BOLL_UPPER\'] = df[\'MA_250\'] + 2 * df[\'BOLL_STD\'] df[\'BOLL_LOWER\'] = df[\'MA_250\'] - 2 * df[\'BOLL_STD\'] with pd.ExcelWriter(r\'../data/huice/aklishi/\' + code + \'.xlsx\') as writer: df.to_excel(writer,index=False,sheet_name = \'Sheet1\')
3、开始进行回测;
# 判断买卖结点 # 采用短线均线上穿长线均线为买点 # 采用短线均线下穿长线均线为卖点 def is_dbljx(data,i,mode = \'buy\',j1=\'MA_60\',j2=\'MA_250\'): if i < 250: return False if mode == \'buy\': if (data.loc[i,j1] > data.loc[i,j2]) & (data.loc[i-1,j1] < data.loc[i-1,j2]): return True else: if (data.loc[i,j1] < data.loc[i,j2]) & (data.loc[i-1,j1] > data.loc[i-1,j2]): return True return False # data总数据,l操作记录列表(买、卖形成一条记录) # 计算出每一次持有股票的盈利金额、盈利幅度、持有天数、期间最高收益、区间最低收益 def huizong_one(data,l): # 输出结果 dfout = pd.DataFrame(l, columns=[\'证券代码\',\'买入索引\',\'卖出索引\', \'买入日期\',\'卖出日期\', \'买入价格\', \'卖出价格\',\'状态\']) dfout = dfout.reindex(columns=[\'证券代码\', \'买入索引\',\'卖出索引\', \'买入日期\',\'卖出日期\', \'买入价格\', \'卖出价格\',\'状态\',\'期间最高收益\',\'期间最低收益\',\'盈利金额\',\'盈利幅度\',\'持有天数\']) # ,\'盈利金额\',\'盈利幅度\',\'持有天数\' for i, row in dfout.iterrows(): dfout.loc[i,\'盈利金额\'] = dfout.loc[i,\'卖出价格\'] - dfout.loc[i,\'买入价格\'] dfout.loc[i,\'盈利幅度\'] = (dfout.loc[i,\'卖出价格\'] - dfout.loc[i,\'买入价格\']) / dfout.loc[i,\'买入价格\'] * 100 dfout.loc[i,\'持有天数\'] = dfout.loc[i,\'卖出日期\'] - dfout.loc[i,\'买入日期\'] m = data.loc[dfout.loc[i,\'买入索引\']:dfout.loc[i,\'卖出索引\'],\'CLOSE\'].max() if m < dfout.loc[i,\'卖出价格\']: m = dfout.loc[i,\'卖出价格\'] dfout.loc[i,\'期间最高收益\'] = (m - dfout.loc[i,\'买入价格\'])/dfout.loc[i,\'买入价格\'] * 100 m = data.loc[dfout.loc[i,\'买入索引\']:dfout.loc[i,\'卖出索引\'],\'CLOSE\'].min() if m > dfout.loc[i,\'卖出价格\']: m = dfout.loc[i,\'卖出价格\'] dfout.loc[i,\'期间最低收益\']= (m - dfout.loc[i,\'买入价格\'])/dfout.loc[i,\'买入价格\'] * 100 return dfout
回测主体:
%%time # 遍历回测所有采集的股票数据 folder_name = r\'../data/huice/aklishi/\' file_list = os.listdir(folder_name) ldf = [] if len(file_list) > 0 : # 遍历文件夹下所有文件 ldf = [] for f in range(len(file_list)): data = pd.read_excel(folder_name + str(file_list[f]),dtype=object) data[\'bs\'] = \'\' code = str(file_list[f])[:-5] l = [] buy_index = 0 # 因为比较的是60日线和250日线,所以从第251个开始 for i in range(250,len(data)): # 判断买卖信号 if is_dbljx(data,i,\'buy\'): # 记录买点 buy_index = i data.loc[i,\'bs\'] = \'b\' # 卖出信号 elif (buy_index != 0) & is_dbljx(data,i,\'sell\'): # 证券代码 买入索引 卖出索引 买入日期 卖出日期 买入价格 卖出价格 # 记录下买卖点,清除买点 l.append([code,buy_index,i,data.loc[buy_index,\'index\'],data.loc[i,\'index\'],data.loc[buy_index,\'CLOSE\'],data.loc[i,\'CLOSE\'],\'终止\']) buy_index = 0 data.loc[i,\'bs\'] = \'s\' # 结束以后保存结果 with pd.ExcelWriter(folder_name + str(file_list[f])) as writer: data.to_excel(writer,index=False,sheet_name = \'Sheet1\') # 汇总单次结果 if buy_index != 0: # 记录一个当前 l.append([code,buy_index,i-1,data.loc[buy_index,\'index\'],data.loc[i-1,\'index\'],data.loc[buy_index,\'CLOSE\'],data.loc[i-1,\'CLOSE\'],\'持有\']) ldf.append(huizong_one(data,l)) # 存储每只股票的操作结果 # 最后汇总输出 dfout = pd.concat(ldf,ignore_index=True) with pd.ExcelWriter(r\'../data/huice/回测结果2.xlsx\') as writer: dfout.to_excel(writer,index=False,sheet_name = \'Sheet1\',float_format="%.2f")
最终形成的数据:
4、结果分析
4.1 总体情况来看
从2000-01-01 -- 2020-11-26 以来,按照收盘价,60日均线上穿250日均线时进行买入,60日均线下穿250日均线时进行卖出,共进行操作21948次(每一次都买入一样的价钱),平均持有天数是293天,亏损次数13331次,盈利次数8568次,只有大概4成的操作是盈利的,但是盈利幅度总和达到605232.1%(6052倍),总共是盈利的。
最大一次亏损达到-68.5%,相当于1万元,亏损剩下3000来,最大一次盈利是24倍!
4.2 从过程来看
持有期间,最高一次收益是52倍,最低持有收益是-72.22%,意味着你某次操作得承受亏损72%,持有天数达223天,最大一次利润回吐是84.15%,经历了从盈利3倍到亏损27%的痛。
4.3 分时间操作来看
买入来看,在2007年仅操作了84次,在2019年3401次。假设每次操作都投入1万元,那么- -||在不考虑存量股票的情况下,你需要3401万资金,贫穷限制了我的操作。
卖出来看,最多卖出时在2019年2242次(2020年卖出1671次,还在持有的有(2515,前面的计算是计算到截至11月26日的收益),最少卖出时间是在2009年。
分年度收益来看,20年来,仅有11年是正收益,在2008年的时候,产生了3052倍收益,在2019年时造成了321倍的亏损....这相当于,每次投入1万块,在2019年会亏个几百万。从这个系统运行起来看,2001年-2006年经历了长达5年的亏损,正常人相比都会怀疑自己的系统不行的了。难!
分个股收益来看。 总操作股票3570个,其中亏损的有915个,越大概四分之一的股票会亏损,这么一看,选择单个股票执行这个策略有25%的概率会失败,其中失败最惨的一次是亏损1.39倍。盈利来说有2655个,总收益是6342倍,最小的一次收益是30%,极端情况下,我选两次股票进行操作,不排除出现(-1.39,0.30)的组合,最终亏损一倍。下一步因对标记了的亏损股票进行一次数据挖掘分析,得出亏损的股票都有些什么性质,进而进行排除。
5、结论
从当前的操作来看这一次回测,第一,虽然能挣钱,资金量需要有点多;
第二,操作的股票太多,2020年持有2515只股票,等价于操作了股票市场一半多的股票。
第三,根据交易者效应,你发现能够赚钱的策略被别人知道以后开始跟风就会变得不赚钱,然而系统的正确性又需要时间来验证,如果出现了交易者效应或其他因素导致系统连续亏钱如2001年 -- 2006年,那么就会导致对系统变得没有信心,最终就会错过2007年、2008年以致于后面丰收的年份。
第四,需要进行分散投资;
总体来说,有点跳大神的感觉,以为自己在某个时间段合适的理论,结果只是巧合?三体转。
回头再考虑结合一下,大盘的情况进行回测看看。