本文是通过之前学习《Python量化入门》课程系列后,进行的学习研究,将思路和结果供大家分享一起讨论。
灵感:如果使用指数均线的优化模拟,我们的收益能否战胜股神呢(年化收益率>20%)?
详细请看我的实验报告~
关键词:Python, 量化研究,移动平均线。
实验目的:对多个股票,使用指数均线的多组参数,进行交易策略回测模拟,并对回测结果进行分析。尝试通过模拟找到最优组参数,然后使用最优组参数再测试实际收益结果。
关于单组测试的模拟,请回顾
刀客特鹿:量化学习笔记#股票的移动平均线的研究(1)单股单组均线zhuanlan.zhihu.com
重复部分,不会再记录。
原始数据:截止到2017年5月12日的A股股票数据和上证指数数据
多组参数:短期均线 ma_short range(20,121,5)
长期均线 ma_long range(20,121,5)
交易策略:最小交易周期:日
买入信号:当短期均线上穿长期均线时候 ma_short >= ma_long
卖出信号:当长期均线下穿长期均线时候 ma_short < ma_long
信号的判别价格:每日收盘价(历史除权价格)
交易计算价格:信号发生后的第二日开盘价(历史未除权价格)
交易手续费:按0.0005计算
印花税:按0.001计算,仅在卖出时发生
交易初始资金 1000000
最小交易数量:100股
测试的时间范围:20050101-20170512
Benchmark: 上证指数 sh000001
实验步骤:
第一步. 随机挑选股票进行模拟
模拟方式类似之前的:
量化学习笔记#股票的移动平均线的研究(3)多股多组均线
刀客特鹿:量化学习笔记#股票的移动平均线的研究(3)多股多组均线zhuanlan.zhihu.com
区别在于,使用指数的收盘价均线作为买卖交易信号
在初始化个股数据的时候就要在代码中加入如下
# 合并指数数据if index:# 获取指数数据index_data = Functions.import_index_data_xbx(index) stock_data = Functions.merge_with_index_data(stock_data, index_data)在计算交易信号的函数中,使用‘index_close’作为均线的价格参数df = get_sma_pos(df, ma_short=ma_short, ma_long=ma_long, price='index_close')
其他步骤与上一篇的实验类似
这些准备工作做好以后,就开始无差别攻击
这里还要介绍一个模块:
random随机模块
import randomcode = random.choice(stock_list) # 从一个列表中随机选取元素
源代码如下:
## 随机挑选股票然后多均线模拟# 随机挑选股票进行模拟stock_list = Functions.get_stock_code_list_in_one_dir_xbx() # 从原始数据存放的文件家中获取股票listexist_list = Functions.get_stock_code_list_in_one_dir_xbx(path) # 在存放结果数据的文件中获得已经有的股票listi = len(exist_list)while i < 1000: code = random.choice(stock_list) if code not in exist_list: stock_data = get_standardized_stock(code) # 判断股票的起始日是不是在2005年之前 if stock_data['date'].loc[0] < pd.to_datetime('20051231'): stock_data = stock_data[stock_data['date'].dt.year >= 2005] # 创建一张空表,用来存放所有的收益数据,为了之后的分析 output = pd.DataFrame() ma_short_list = range(1, 21, 1) ma_long_list = range(20, 121, 1) for ma_long in ma_long_list: for ma_short in ma_short_list: if ma_short < ma_long: print code, ma_short, ma_long df = stock_data.copy() df = get_sma_pos(df, ma_short=ma_short, ma_long=ma_long, price='close_adjust_back') df = get_stock_return(df) pf_result = get_pf_reult(df, ma_short=ma_short, ma_long=ma_long, printout=False) if output.empty: output = pf_result else: output = output.append(pf_result) output.to_csv(str(code) + '_sma_' + str(date_now) + '.csv', index=False) else: print(str(code) + ' data is too short.') else: print(str(code) + ' data existed.') print i
结果:
随机获取了111个股票数据进行模拟。
第二步:对结果进行分析,获取最优均线参数
2.1对以上所有结果中的内容进行统计分析
# 批量读取文件夹里的数据,然后提取所有结果
def get_top_output(path, column='final_ratio', n=10):""":param path: 获取文件的了路径:param column: 需要分析的列,默认'final_ratio',其他如‘total_return', 'annual_return':param n: 取出靠前的n行,默认是10:return: df"""# 建立空表outputoutput = pd.DataFrame()# 遍历所有数据取出分析for root, dir, files in os.walk(path):for file in files: result = pd.read_csv(file) result.dropna(inplace=True)# 按 年化收益/最大回撤 排序result.sort_values(by=column, ascending=False, inplace=True) result.reset_index(drop=True, inplace=True)if n: result = result.head(n)if output.empty: output = resultelse: output = output.append(result) output.reset_index(d
运行以上函数
output = get_top_output(path) # path是之前均线模拟结果存放数据的路径
输出output检查一下,获取每个股票'final_ratio'最高的10组内容
print output
2.2 统计均线参数(ma_long, ma_short)出现的次数
# 统计参数出现的次数,并作图def get_par_count(df, par, pic=True, output='df'): """ :param df: 拥有参数的df :param par: 要统计的参数 :param plt: 是否要绘图 :param output: 结果输出形式: list, df :return: """ df = df.copy() par_list = list(df[par]) # print ma_long_list par_set = set(par_list) par_count = [] for par1 in par_set: par_count.append(par_list.count(par1)) par_set = list(par_set) # 作图 if pic == True: fig = plt.figure(figsize=(12, 6)) plt.title(par) plt.show()par_set, par_count) plt.show() # 结果输出 if output == 'list': # 将结果以list合并输出 par_list = list(map(lambda x, y: [x, y], par_set, par_count)) return par_list elif output == 'df': # 将结果以df输出 df = pd.DataFrame({par: par_set, par + '_count': par_count}) df.sort_values(by=par + '_count', ascending=False, inplace=True) df.reset_index(drop=True, inplace=True) return df else: print "Not set output form."
运行函数:
get_par_count(output, 'ma_long', pic=True, output='df')
统计结果:
同样,我们也可以对ma_short的参数进行统计分析:
get_par_count(output, 'ma_short', pic=True, output='df')
然后我们提取final ratio最高的前5个ma_long和ma_short的参数
具体参数不告诉大家主要有两个原因,
1. 不想对各位的投资造成误导,因为这个实验方法,更多的是提供思路,而这些统计分析的结果往往会有过度拟合和鲁棒性差的问题存在,盲目投资,会造成巨大的损失。
2. 希望各位可以通过本笔记去学习,而不是简单的拿来主义。
第三步,在得到最优的均线参数后,进行反向验证。
再一次进行第一步的函数操作,不过这次ma_long_list 和 ma_short_list是直接统计分析后的结果。
模拟的原始数据,还是从原来的全量股票数据中随机获取。
# 随机挑选股票,对指定均线参数列表进行统计分析:get_sma_test(path, ma_long_list=ma_long_list, ma_short_list=ma_short_list, max_n=4000)
这次因为参数少,10多分钟就能获取160个股票的模拟结果
这次的模拟结果要比之前少得多
第四步,再对优化后的均线参数的模拟交易结果进行统计分析:
是不是绕晕了。。。各位,简单来说就是,模拟,优化,再模拟,再优化的逻辑,直到你觉得你自己满意为止。
4.1 看下这次所有的结果中年化收益大于0的占比
# 读取所有模拟的结果数据合并成大表output = get_top_output(path, n=False)# 设置目标收益率target = 0# 统计年化收益大于0的个数ar_positive = output.loc[output['annual_return'] >=target, 'annual_return'].count()print "There are %d returns greater than %f%%, occupying %.2f%%." % (ar_positive, target*100.0,(ar_positive/output.shape[0]) * 100)# 统计年化收益小于0的个数ar_positive = output.loc[output['annual_return']
结果:
胜率很高呀~
4.2 目标收益率改为年化4%,超过余额宝~
居然还有那么高得胜率~小惊喜~
4.3 目标收益率改为年化10%,挑战P2P~
结果依旧不错~ 胜率接近80%
4.4 目标收益率改为年化20%,挑战股神巴菲特
这次终于失败了。。。看来只是想靠均线参数的微调就想轻易挑战股神,还是比较难的。
实验总结:
以上实验属于纯技术性指标模拟,各位玩家即使通过这样的思路找到100%战胜股神的方法,还是要谨慎使用~毕竟投资不仅仅是数字游戏~但是数理统计分析能给你更多角度,更有效地去实现自己的假设推论~
以上交易策略的大量原始代码和框架,师从于《Python量化入门》课程系列,想获得所有的源代码,并建立自己的交易策略,建议购买课程后深入学习。
课程评价
优点:这套课程特别适合对Python,pandas都没掌握的同学,和“入门”二字很贴切。虽然价要收取一定的费用,但是对于零基础的同学还是墙裂推荐。当然我的笔记是完全免费的,但这个只能作为点心,毕竟正餐才是最有营养的~而且还有大量的源代码A股所有的股票历史交易数据。
缺点:目前只能用微信访问学习,PC端的话要通过微信客户端访问。
系列总课时约12多个小时,会获得课程讲解的所有源代码。如要深入熟练掌握,课外练习可能需要100小时+。
课程传送门:
网友评论