python-股票量化-双均线策略(如何读取数据?,股票涨跌幅/涨跌停/复权计算,绘制股票资金曲线k线如何标记双均线策略信号,持仓分组标记及涨跌幅分析,绩效指标计算及调参方法,用循环调整周期参数)

本文主要是介绍python-股票量化-双均线策略(如何读取数据?,股票涨跌幅/涨跌停/复权计算,绘制股票资金曲线k线如何标记双均线策略信号,持仓分组标记及涨跌幅分析,绩效指标计算及调参方法,用循环调整周期参数),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

炒股的人通常都喜欢各种技术指标,来指定买卖策略,然而我们却经常看到股民们亏钱,难道是这些指标有问题吗?还是专家在忽悠股民朋友们?将以非常简单的均线指标为例,来挖掘其在一只股票上的绩效潜力。

实现流程的简单梳理

1.读取数据,了解数据的结构
2.对读取的数据进行清洗,使数据符合进一步分析
3.计算相应的股票日涨跌幅度
4.通过画图来展示:
a.基于回测资金曲线
b.股票k线图
c.参数优化的热力图

读取数据

注意事项:

pd.read_excel()有两个设置参数,分别是:
1.parse_dates:是指将指定列转化为时间类型格式,注意是列表形式指定列[“交易日期”]
2.index_cel:是指将指定列变为行索引index

# 导包
import pandas as pd
df = pd.read_excel("sz002115",parse_dates=["加油日期"],index_cel="交易日期")

在这里插入图片描述

为什么要设置"交易日期"列为datetime数据类型?

主要是后面的作图,直接传入时间数据为y轴,用str数据类型,有可能不会被识别.

最后我们查看一下数据的基本构造,看数据是否有空值,是否需要填充

df.info()

在这里插入图片描述

股票涨跌幅/涨跌停/复权计算

概念解析

1.股票涨跌幅:股票每天相对的昨日收盘价的涨跌幅
2.股票涨跌停价格:股票每一天的涨停价,跌停价
3.复权:把复权简单理解为就是排除送股,配股,分红等影响因素。

计算股票涨跌幅

df["涨跌幅"] = df["收盘价"]/df["前收盘价"]-1

注意事项

使用前收盘价,而非用开盘价,是为了避免因为送股,配股,分红等影响因素的股价不合理变化.

计算股票涨跌停

df["涨停价"] = df["前收盘价"]*1.1
df["跌停价"] = df["前收盘价"]*0.8

计算股票后复权价格

概念解析

前复权处理:股票最近一天价格不变,之前数据按照真实日涨跌幅做调整;

后复权处理:股票上市第一天价格不变,之后数据按照真实日涨跌幅做调整;(后复权类似于基金净值的统计)

计算股票资金曲线

所谓资金曲线,即把股票上市第一天的净值设为1,之后每天净值随真实日涨跌幅波动的曲线
1+涨跌幅 我们的到的是涨跌比例,我们把每一天的涨跌比例累程起来.我们的到一个1+涨跌幅的累乘数据,在做归一化
在这里插入图片描述
这个是比较难理解的,看图你应该就明白了!!!

df["资金曲线"] = (1+df["涨跌幅"].cumprod()  
df.loc[:,"资金曲线"] = df["资金曲线"]/df["资金曲线"].iat[o]  #进行数据归一化

iat只能访问单个元素

df[“xxx”].iat[x]
当前"xxx"这一列的第x值

loc与iloc

loc是标签名,不带i的是名字
iloc是位置号,带i的就是index位置
取数据都遵循:左行右列loc[左行,有列] iloc[左行,右列]

计算后复权价格

格式:
收盘后复权 = 第一天的当日收盘价(固定值) * 资金曲线的值 (一直在变动反应股票涨跌变化情况的值)
其他后复权,求出对于收盘价的涨跌幅后乘上收盘价后复权
涨跌幅 * 收盘价后复权

df["收盘价_后复权"] = df["资金曲线"] * df["收盘价"].iat[0] # 股票第一天的价格不变作为基准价df["开盘价_后复权"] = df["开盘价"]/df["收盘价"]*df["收盘价_后复权"]
df["最高价_后复权"] = df["最高价"]/df["收盘价"]*df["收盘_后复权"]
df["最低价_后复权"] = df["最低价"]/df["收盘价"]*df["收盘_后复权"]# 生成新的4个字段
df[["开盘价_后复权","最高价_后复权","最低价_后复权","收盘价_后复权"]]

在这里插入图片描述

绘制股票历史价格变动图、k线图

绘制股票历史价格变动图

df["资金曲线"].plot(figsize=(20,8),grid=True,alpha=0.8,rot=45,title="股票历史价格变动图")

在这里插入图片描述

绘制不复权k线图

# 导入pyecharts工具包,用pe作为简写
import pyecharts as pe# 提取x轴数据,即交易日期,同时转换为字符串形式
x = df.index.astype('str')# 提取y轴数据,即绘制股票k线所必须的4个价格字段
y = df[['开盘价','收盘价','最低价','最高价']].values# 创建k线图对象,并设置大标题
kline = pe.Kline('不复权K线图_三维通信')# 绘制图表
kline.add('日K', x, y,                          #图例名称,x轴数据,y轴数据is_datazoom_show=True,                #使用区域缩放组件datazoom_range=[97,100],              #设置区域缩放的范围,这里设置为末尾3%的k线数据mark_point=['max','min'],             #标记可视范围内开盘价最大值,最小值is_xaxislabel_align = True,           #设置x轴刻度与标签对齐tooltip_trigger ='axis',              #坐标轴触发弹窗提示tooltip_axispointer_type ='shadow')   #设置tooltip指示器

在这里插入图片描述

标记双均线策略信号

计算均线

df["MA50"] = df["收盘价_后复权"].rolling(50).mean()
df["MA200"] = df["收盘价_后复权"].rolling(200).mean()

注意事项

pandasSeries.rolling(n)是表示对一列的n个数据做滚动处理,rolling(3).mean()表示
对滚动的数据做均值计算,并且记录在最后一行
在这里插入图片描述

双均线策略信号标记

在这里插入图片描述
在这里插入图片描述

总结一句话就是:

对于短周期均线前天小后天大是上传买入信号,前天大后天小是下穿卖出信号

## 1位买入信号
df.loc[(df["MA50"]<df["MA200")&(df["MA50"].shift(1)>df["MA200"].shift(1)),"交易信号"]=1
# 0卖出信号
df.loc[(df["MA50"]>df["MA200"])&(df["MA50"].shift(1)<df["MA200"].shift(1)),"交易信号"]=0
# 去除掉空值
df["交易信号"].dropna()

注意事项:

df.loc[(条件1)&(条件2), ‘交易信号’] = 1找到了同时满足2个条件的所有行,并且索引到交易信号这一列,当这一列最开始不存在的时候,pandas会帮我们自动新建一列空值的交易信号,然后对这一列满足2个条件的所有行赋值为1,这就完成了买入信号的标记,卖出信号标记则同理

涨跌停信号屏蔽

当股票涨停是无法买入的,跌停也是无法卖出的,如果我们策略中有这样的情况,则需要把交易信号屏幕掉(即设置为空值)

df.loc[(df["收盘价"]>=df["涨停价"])&(df["交易信号"]==1),"交易信号"]=None
df.loc[(df["收盘价"]<=df["跌停价"])&(df["交易信号"]==0),"交易信号"]=None
df["交易信号"].dropna()

持仓信号标记

我们需要根据 交易信号 来标记出,在股票历史中,我们买入持仓的期间,以及卖出空仓状态的期间。这是为了之后能回测双均线策略的优劣所必须的步骤

df["持仓信息"] = df["持仓状态"].fillna(method="ffill").shift().fillna(0)

df[‘交易信号’].fillna(method=‘ffill’) 函数会填充 “交易信号” 字段的空值,这里用到的参数 method=‘ffill’ 代表向前填充。假设交易信号只有4行 [NaN , 1 , NaN , NaN],每一行空值都会按之前最近的一个非空值来填充自己,ffill填充结果为 [NaN , 1 , 1 , 1],首行找不到之前的非空数据就不会被填充
pandas里的方法是可以连续调用的,在填充空值后,连续调用 .shift() 移位,沿用上面例子的假设,[NaN , 1 , 1 , 1] 将变为 [NaN , NaN , 1 , 1, 1],最后调用 .fillna(0) 将剩余空值填充为 0 [0 , 0 , 1 , 1]
在这里插入图片描述
这样子做的目的是为了实现,当日建仓因为是T+1的,当日是不可以买卖的持仓数量应为0,次日才应该有持仓
在这里插入图片描述

构建函数分析

持仓的计算,都是基于50日均线,200日均线来标记生成的。如果我想测试其他的均线组合呢?我要一行行去改代码中出现MA50,MA200的位置嘛?–这里我们总结上述信息封装一个函数

def Ma_mark(dfr,n,m):
""
dfr股票原始数据
n短周期
m长周期
""
assert n<m,"短周期数值不能大于长周期数组"
assert m<dfr.shape[0],"长周期数值不能大数提供的股票数据的长度"
# 制作数据备份
df = dfr.copy()
# 计算双均线
df["{}日均线".format(n)] = df["收盘价_后复权"].rolling(n).mean()
df["{}日均线".format(m)] = df["收盘价_后复权"].rolling(n).mean()# 计算交易信号
#买入
df.loc[df["{}日均线".format(n)]<df["{}日均线".format(m)]&df["{}日均线".format(n)].shift()>df["{}日均线".format(m)].shift(),"交易信号"]=1
# 卖出
df.loc[df["{}日均线".format(n)]>df["{}日均线".format(m)]&df["{}日均线".format(n)].shift()<df["{}日均线".format(m)].shift(),"交易信号"]=0
# 涨停无法买入标记
df.loc[df["收盘价"]>=df["涨停价"]&df["交易信号"]==1,"交易信号"]=None
# 跌停无法卖出标记
df.loc[df["收盘价"<=df["跌停价"]]&df["交易信号"]==0,"交易信号"]=None
# 计算持仓状态信号
# ffill向上填充
df["持仓状态"] = df["交易信号"].fillna(method="ffill").shift().fillna(0)# 调用函数
df_ma= Ma_mark(df,50,20)# 画图展示
df_ma[["收盘价_后复权","50日均线","200日均线"]]

在这里插入图片描述

注意事项

1.DataFrame.shape可以拿到数据的(行数,列数),索引取0即取数据的行数
2.assert语句包含两部分,assert 条件,不满足条件时报错并弹出对应提示 ,用来限制大家对函数参数的传入

持仓分组标记及涨跌幅分析

我们持仓状态要么是1,要么是0,类似[0 0 0 1 1 1 1 1 1 0 0 …]这样循环往复,我们希望对这些持仓状态都标记为不同的组,比如[0 0 1 1 0 0] 我们希望标记为 [组1 组1 组2 组2 组3 组3]

我么怎么实现这种持仓分组标记?

1.直接赋值index(交易日期)为持仓分组
2.我们看当日持仓状态是否等于前一日持仓状态.如果等于我们就把相等的数据对应的持仓分组行赋值为空值(None)
3.我们对持仓分组进行向上填充,就可以达到 [组1 组1 组2 组2 组3 组3]
method=‘ffill’ 代表向前填充。假设交易信号只有4行 [NaN , 1 , NaN , NaN],每一行空值都会按之前最近的一个非空值来填充自己,ffill填充结果为 [NaN , 1 , 1 , 1]
在这里插入图片描述

策略涨跌幅及手续费计算

策略持仓股票时,策略的涨跌和股票同步,策略空仓时,策略不涨不跌

  1. 开仓买入手续费计算
    在持仓状态为1的每一组,首行即开仓买入,持仓状态刚刚从0到1,此时策略就要开始产生涨跌幅了。假设我们开仓支付了0.1%的手续费,那么实际持仓的价值就只有99.9%,也就是只有99.9%的仓位发生了涨跌, 首行策略实际涨跌幅 = (1 + 股票涨跌幅)[1是股票本身(基准)+涨幅]*(1 - 开仓手续费)[股票买入成本是100%-手续费0.1%]-1
  2. 平仓卖出手续费计算
    在持仓状态为1的每一组,最后一行收盘时平仓卖出,再下一行持仓状态就要从1变为0了,此时策略涨跌幅就要被屏蔽掉了。在此之前,假设平仓也是0.1%手续费,那么最终变现的价值也就只有99.9%,公式同样是 最后一行策略实际涨跌幅 = (1 + 股票涨跌幅)*(1 - 平仓手续费)- 1
# 如果持仓状态为0,乘以涨跌幅,就是0.而如果为1,乘以涨跌幅,就涨跌幅本身
df["涨跌幅_策略"] = df_ma["涨跌幅"] * df_ma["持仓状态"]
# 找到第一次开仓点(持仓状态为1,上一行是0),扣除手续费.上一行=本行的shift()平移
df_ma.loc[df["涨跌幅_策略"]!=0 &df["涨跌幅_策略"]!=df["涨跌幅_策略"].shift(),"涨跌幅_策略"]=(1+"涨跌幅_策略")*(1-0.0008)-1
# 找出开仓组的最后一行(最后一行持仓状态为1,下一行是0),扣除手续费
df_ma.loc[df["涨跌幅_策略"]!=0 &df["涨跌幅_策略"]!=df["涨跌幅_策略"].shift(-1),"涨跌幅_策略"]=(1+"涨跌幅_策略")*(1-0.0008)-1

注意事项:

当出现df[“涨跌幅_策略”]!=df[“涨跌幅_策略”].shift(-1)
df[“涨跌幅_策略”]!=df[“涨跌幅_策略”].shift()说明到了组与组的分界点了
在这里插入图片描述
在这里插入图片描述

绩效指标计算及调参方法

策略资金曲线,绩效指标计算

# 计算并更新策略资金曲线
df_ma.loc[:,"资金曲线_策略"] = (1+ df_ma["涨跌幅_策略"]).cumprod()
# 归一化
df_ma.loc[:,"资金曲线_策略"] = df_ma["资金曲线_策略"]/df_ma["资金曲线_策略"].iat[0]# 计算最大回撤(策略在每个交易日,相对之前最高点跌幅是多大)
df_ma["最大回撤"]= df_ma["资金曲线_策略"].cummax()
df_ma.loc[:,"最大回撤"]= 1-df_ma["资金曲线_策略"]/df_ma["最大回撤"]# 新建一个字典来存储各绩效指标
# 股票资金曲线终值:即股票资金曲线在最近一个交易日的数值,展示了开盘以来累计涨跌的收益情况
# 策略资金曲线终值:即策略资金曲线在最近一个交易日的数值,展示了开盘以来累计涨跌的收益情况
# 相对收益:最近一个交易日,策略资金曲线与股票资金曲线的比值
# 总交易次数:所有的买入卖出次数总计
# 平均持仓时间:持仓一次平均的时间长度
# 历史最大回撤:策略历史上,从最高点到最低点的跌幅
# 股票夏普率:股票平均涨跌幅/涨跌幅的波动,课程里我们简化夏普率计算,假设无风险收益率为0
# 策略夏普率:策略平均涨跌幅/涨跌幅的波动
dic={"股票资金曲线终值":df_ma["资金曲线"].iat[-1],"策略资金曲线终值":df_ma["资金曲线_策略"].iat[-1],"相对收益":df_ma["资金曲线_策略"].iat[-1]/df_ma["资金曲线"].iat[-1],"总交易次数":df_ma["持仓分组"].unique().shape[0],"平均持仓时间":(df_ma.index[-1]-df_ma.index[0])/(df_ma["持仓分组"].unique().shape[0]),"历史最大回撤":df_ma["最大回撤"].max(),"股票夏普率":df_ma["涨跌幅"].mean()/df_ma["涨跌幅"].std(),"策略夏普率":df_ma["涨跌幅_策略"].mean()/df_ma["涨跌幅_策略"].std()}
print(dic)
df_ma[["资金曲线","资金曲线_策略"]].plot(figsize=(20,8))

在这里插入图片描述
在这里插入图片描述

注意事项:

1.对策略资金曲线运用cummax函数,它能帮助我们计算出,资金曲线每一行,累计的最大值。比如我对一个列表 [1,3,5,4,2] 计算 cummax 结果为 [1,3,5,5,5]
2.df.unqiue() 去重获取到唯一值
3.shape():读取矩阵长度,如shape[0]是读取矩阵第一维的长度。

上面做了一堆很是凌乱,不方便我们后期复用,所以我们在这里封装一个函数,在实现上面的内容,方便后的的策略优化调用

# 重置一下df_ma,看看其他周期的效果
df_ma = MA_mark(df,100,300)def fund_curve(dfr,tr=.0008):'''【回测:资金曲线及绩效指标计算】dfr:标记交易信号及持仓状态后的股票数据tr:单次交易综合手续费,默认万分之8'''# 拷贝一份数据df = dfr.copy()# 标记多空持仓组别df['持仓分组'] = df.indexdf.loc[df['持仓状态']==df['持仓状态'].shift(),'持仓分组'] = Nonedf['持仓分组'].fillna(method='ffill',inplace=True)    # 计算策略基本涨跌幅,持仓为0则当日策略基本收益率也为0df['涨跌幅_策略'] = df['涨跌幅'] * df['持仓状态']# 筛选所有非空持仓并扣除开平仓手续费df.loc[(df['持仓状态'] != 0) &(df['持仓状态'] != df['持仓状态'].shift()),'涨跌幅_策略'] = (1 + df['涨跌幅_策略']) * (1 - tr) - 1df.loc[(df['持仓状态'] != 0) &(df['持仓状态'] != df['持仓状态'].shift(-1)),'涨跌幅_策略'] = (1 + df['涨跌幅_策略']) * (1 - tr) - 1# 计算并更新策略资金曲线    df.loc[:,'资金曲线_策略'] = (1 + df['涨跌幅_策略']).cumprod()df.loc[:,'资金曲线_策略'] = df['资金曲线_策略'] / df['资金曲线_策略'].iat[0]# 计算最大回撤df['最大回撤'] = df['资金曲线_策略'].cummax()df.loc[:,'最大回撤'] = 1 - df['资金曲线_策略'] / df['最大回撤']return df,{'股票资金曲线终值':df['资金曲线'].iat[-1],'策略资金曲线终值':df['资金曲线_策略'].iat[-1],'相对收益':df['资金曲线_策略'].iat[-1]/df['资金曲线'].iat[-1],'总交易次数':df['持仓分组'].unique().shape[0],'平均持仓时间':(df.index[-1]-df.index[0])/df['持仓分组'].unique().shape[0],'历史最大回撤':df['最大回撤'].max(),'股票夏普率':df['涨跌幅'].mean()/df['涨跌幅'].std(ddof=0),'策略夏普率':df['涨跌幅_策略'].mean()/df['涨跌幅_策略'].std(ddof=0)}# 调用函数,把原始的df_ma数据传入资金曲线计算函数
result = fund_curve(df_ma)# 查看绩效指标,并绘制资金曲线对比可视化图表
print(result[1]) 
result[0][['资金曲线','资金曲线_策略']].plot(figsize=(20,8),grid=True)

在这里插入图片描述
在这里插入图片描述

用循环调整周期参数

绘制各绩效指标参数热力图

最优参数绩效及资金曲线

这篇关于python-股票量化-双均线策略(如何读取数据?,股票涨跌幅/涨跌停/复权计算,绘制股票资金曲线k线如何标记双均线策略信号,持仓分组标记及涨跌幅分析,绩效指标计算及调参方法,用循环调整周期参数)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/357177

相关文章

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

4B参数秒杀GPT-3.5:MiniCPM 3.0惊艳登场!

​ 面壁智能 在 AI 的世界里,总有那么几个时刻让人惊叹不已。面壁智能推出的 MiniCPM 3.0,这个仅有4B参数的"小钢炮",正在以惊人的实力挑战着 GPT-3.5 这个曾经的AI巨人。 MiniCPM 3.0 MiniCPM 3.0 MiniCPM 3.0 目前的主要功能有: 长上下文功能:原生支持 32k 上下文长度,性能完美。我们引入了