在上一次https://www.cnblogs.com/webor2006/p/15164775.html学习了使用JQData来查询股票相关数据, 这次则开始一点点构建咱们的量化交易系统了。
量化交易平台功能模块了解:
对于一个量化交易平台,它主要包含如下功能模块:
而整个模块基本都是基于后端来开发的,只有图表可视化是用一个可视化库来打造,并未涉及到前端的功能,核心主要是来学习业务逻辑:
获取股票数据:
新建readme:
在咱们的工程中新建一个Readme文件用来进行功能的描述:
#DeltaTrader ## 功能模块 ### 行情记录 (data) #### stock.py - 获取所有A股股票列表 - 获取单个股票行情数据 - 导出股票行情数据 - 转换股票行情周期 - 获得单个股票财务指标 - 获取单个股票估值指标 ### 策略开发 ### 自动交易
三大块,其中目前主要关注获取行情记录数据这块,所以咱们的包名是data:
而其它模块会随着学习的不断深入再慢慢填充,其实对于这些数据的获取在上一次咱们已经都试验过了,只是没有将其封装成一个通用的API,所以接下来则会基于咱们上次实现的代码进行一个抽取,来实现这里所列出的具体API。
获取所有A股股票列表:
这个在之前咱们实现的代码为:
咱们直接基于它来改一下:
获取单个股票行情数据:
先看一下咱们之前实现的:
定义一下:
导出股票相关数据:
原来咱们在获取股票财务指标时用过:
咱们来封装一下:
其中这里新建一个price目录,专门用来存储股票价格相关的文件:
如果要导出其它类别的数据,新建相关目录,在调用此函数时传相关的type名称既可。
转换股票行情周期:
先来看一下之前咱们使用的:
下面基于它来封装一下:
获取单个股票财务指标:
之前的代码:
封装一下:
获取单个股票估值指标:
封装为:
调用stock:
接下来咱们则来调用一下咱们重新封装的stock。
1、新建一个测试模块:
为了规范,这里将测试相关的代码都放到另一个模块中:
2、获取股票行情数据并导入csv:
控制台输出:
看一下表格文件有木有生成?
但是!!!你会发现表格中表头部分少了一个日期:
这个时候是需要咱们来处理一下的,如果不处理在未来的cvs的读取数据时是会有问题的,这个问题是啥呢?下面在stock中先增加一个cvs的读取函数,读出来自然就明白了:
咱们来调用读取一下:
接下来就来修复它,需要在导出函数那块进行处理了,如何处理呢,此时则需要对数据的索引进行重命名了,如下:
再重新导出,再获取:
计算交易指标:
接下来就来学习跟咱们炒股操作息息相关的一些东东啦~~
使用shift函数计算涨跌幅:
每日涨跌幅:
先来计算一下每日的涨跌幅,看是否准确,先来明确一下涨跌幅的计算公式:“(当期收盘价-前期收盘价)/ 前期收盘价” ,
其中的当期可以是当天、当分钟、当秒钟、当周、当月,具体实现如下:
其中shift(1)表示上一行的数据,而shift(2)表示上两行的数据,shift(-1)表示下一行的数据。
下面来调用验证一下是否准确,这里还是以平安银行的日K数据为例:
运行看一下:
/Users/xiongwei/opt/anaconda3/bin/python3.8 /Users/xiongwei/Documents/workspace/python/QuantitativeTrading/studycode/example/stock.py auth success open close high low volume money 2021-09-01 17.48 17.88 17.92 17.01 231689409.0 4.046284e+09 2021-09-02 18.00 18.40 18.78 17.80 242260354.0 4.454545e+09 2021-09-03 18.50 18.04 18.50 17.70 139481871.0 2.523273e+09 2021-09-06 17.93 18.45 18.60 17.78 151522556.0 2.780281e+09 2021-09-07 18.60 19.24 19.56 18.35 162234416.0 3.067366e+09 open close high low volume money close_pct 2021-09-01 17.48 17.88 17.92 17.01 231689409.0 4.046284e+09 NaN 2021-09-02 18.00 18.40 18.78 17.80 242260354.0 4.454545e+09 0.029083 2021-09-03 18.50 18.04 18.50 17.70 139481871.0 2.523273e+09 -0.019565 2021-09-06 17.93 18.45 18.60 17.78 151522556.0 2.780281e+09 0.022727 2021-09-07 18.60 19.24 19.56 18.35 162234416.0 3.067366e+09 0.042818 Process finished with exit code 0
接下来就要来验证咱们计算涨幅的正确性了,咱们这里来挑两天的验证一下既可,比如我们挑这两天的:
回到交易平台中咱们来看一下:
完成正确。
每周涨跌幅:
为了进一步验证准确性,这里再来看一下周K的涨跌幅,那首先咱们需要将日K的数据转换为周K的数据对吧,该功能我们已经封装好了,直接调用既可:
好,获取了三周的数据,回到同花顺软件里确认一下准确性:
为啥咱们打印的日期是显示的2021-09-05呢?
其实我们打印的是一周的最后一天:
而股票软件里是算到工作日的最后一天,关于这个细节就不过多较真的,总之涨幅结果对相同的,再来看一天的:
模拟股票交易:
买入、卖出信号:
对于股票操作最频繁的就是买入和卖出操作对吧,所以接下来会以一个简单的策略来对股票生成买卖信息,注意:由于这阶段还在打基础,还没有学习各种选股策略的应用这块,所以这里的策略是很呆板的,比如可能就是定就是周一卖,周四买之类的,重点是能让我们程序按预期来生成买卖信号。
1、新建Strategy模块:
由于未来会有很多的一些策略,所以这里新建一个包名:
2、创建周期选股策略:
好,接下来则来创建一个非常简单按周期选股的策略:周四买入、周一卖出【有过炒股经历的应该也能感受到这个规则,也有点用吧,因为基本上到了周四就开始阴了,而周一作为一周的开始往往势头比较旺~~】
""" 用来创建交易策略、生成交易信号 """ import data.stock as st import numpy as np def week_peroid_strategy(code, time_freq, start_date, end_date): data = st.get_single_price(code, time_freq, start_date, end_date) # 新建周期字段,周一是从0开始 data['weekday'] = data['date'].weekday # 周四买入,下面代码的意思是如果是周四,则是1表示买,0表示不买 data['buy_signal'] = np.where((data['weekday'] == 3), 1, 0) # 周一卖出,下面代码的意思是如果是周一,则是-1表示卖,0表示不卖 data['sell_signal'] = np.where((data['weekday'] == 0), -1, 0) return data if __name__ == '__main__': data = week_peroid_strategy(code='000001.XSHE', time_freq='daily', start_date='2021-08-25', end_date='2021-09-08') print(data)
下面运行看一下,报错了:
原因是咱们的索引列木有给它重命名,没有date这一列,当时我们重命名只是在导出函数中加了:
好,那修改一下程序:
为了打印看得更加清楚,这里将字段过滤一下:
3、信号整合:
接下来想一个场景,就是有可能周四你买入,周五又有可能买入对吧,那连续两天买入,在实际做策略时会经常碰到这种重复的信号的,所以这里针对这样的场景模拟一下:
那针对这样的数据,咱们得想办法只让第一天买入为1,其它的买入都变为0,其实也很简单,如下:
此时又用到了shift()函数了对吧,不过这里还是会有些没覆盖的场景,这里将获取数据的时间改一下就能看到问题之所在了:
好,此时咱们把整合的那句代码又打开注释,再运行,你会发现:
这里需要改一下条件了,如下:
当然对于卖出信号也需要整合一下,因为也有可能出现重复卖出的情况,如下:
4、最终生成买卖信号:
目前buy_signal和sell_singal,在同一天只可能有一种signal,要么是买,要么是卖对么?所以这里再加一个字段,用来做买卖的直观判断,不然我看两个字段有点晕,如下:
计算持仓收益:
了解:
先来看一下股票软件中持仓收益一般包含的数据内容:
其中咱们需要了解的有如下几个计算公式:
1、总盈亏= (市价 - 成本价) * 股数;
也就是表格中这一列的值:
2、 浮动盈亏比 = (市价 - 成本价) / 成本价;
也就是表格中的这一列的值,我们平常最关心的收益率:
比如7.377,通常也叫挣了7.3个点,其实还有一个更加简便的计算方法,比如盈利的情况下,直接拿市价/成本价,然后肉眼就可以看到收益率了,比如表格中的新城股份:
咱们来除一下:
然后用它再减去1,此时就可以看到百分收益率就是7.377啦,如果对于亏损的情况也类似,用价格高的除以价格低的就成。
3、成本价 = 买入金额 / 持有股数
因为有可能在实际会分仓进行买入,所以成本价计算就是如上公式了。
4、股数 = 累计买入股数
这个就比较简单了,通常一手是100股。
以上数据不用太过关心,因为股票软件里持仓记录那块都能一眼看到,对于我们来说只要知道如何查询出这些数据就可以了, 其中重点是关注如何计算持仓收益率,这个是评价一个策略是否挣钱最直接的一个指标。
实现:
1、收益率计算:
接下来咱们基于上面的买卖信号数据来计算一下每次的收益率,先分析一下咱们的数据:
发现在买和卖之间的天数肯定是不一定的,短线的周期短,中长线的周期长,而对于计算收益率来说,只关心买入和卖出那两天的价格对吧?所以,我们可以为了方便计算的收益率,把买和卖中间持股的记录都给删掉,只保留买和卖那两天的数据,这样计算的话就比较方便了,所以基于这样的思路咱们来写一下逻辑:
接下来计算收益率为:
其中发现,对于买的情况很显然是不需要显示收益率的,所以这里再过滤一下:
2、获取上市以来所有数据:
目前咱们只获取了二个月的数据,那如果我想获取单支股票自上市以来的所有数据来进一步观察咱们策略的有效性呢?此时可以回到JQData的官网找一下如何获得上市以来的数据,其实就是在获取股票单个行情数据函数中的start_date传上市的时间既可:
那上官网查一下:
好,咱们来改一下:
此时咱们在调用时就可以传None了:
其中需要导一下datetime:
3、重构week_peroid_strategy函数:
目前对于week_peroid_strategy可以稍加优化一下代码,因为看着有点乱,就是把这俩逻辑抽离出去:
说不定未来还能在其它的功能上使用上,如下:
这样,整个逻辑就变得清爽了:
这个思想不管是用啥语言写程序都是应该我们来进行考虑的,因为可以大大增加代码的可读性。
另外目前咱们代码这有个报警:
是因为在main中也是用的data:
这里将它改一下名称既可:
4、打印一下数据的均值:describe()
对于平安银行自上市至今的数据太多了,可以用下面来查看一下均值,对整体数据有一个大概的了解:
关于各参数的函数可以参考:
3、将其可视化输出:plot()
接下来咱们也可以使用plot()函数来将整个的收益率情况可视化一下,如下:
是不是似曾相识,是的,在之前线性代数的学习https://www.cnblogs.com/webor2006/p/14271706.html中也使用到了该可视化库了,这也就是知识的关联系,学习的任何一个知识在未来的某一个时刻一定还能出现它的身影,并不是完全孤立的,好,运行,一个可视化的界面就出现了:
计算累计收益率:
概述:
现在我们已经能计算出一支股票的单次的收益率对吧?那如何根据单次收益率来计算出一个累计收益率呢?比如你支付宝中的基金【关于基金的学习,也是今年的一个计划,将来得实际行动起来】,通常在这个页面会展示累计收益率:
那你有没有搞清楚这个值是如何计算出来的呢?这里正好可以弥补一下这个知识,假如你买的一个基金,整体的收益情况如下:
那累计收益的计算,咱们口算一下,算完之后你就会发现公式出来了,公式有了那们咱们的程序就有戏了:
这个比较简单对吧,也就是原来100块钱,经过一天之后的收益利息,挣了3块钱,总金额变成了103了,好,接着算第二天的累计收益了:
接下来再把剩下的两天算出来,你就会发现公式了:
其中“(1 + 当天收益率)的累积乘积 ”,其实就是一个通用计算累计收益率的公式【一定要注意累计收益和累计收益率,累计收益是有本金在里面,而累计收益率是不包含本金的,就是一个比例嘛,这一点必须要搞清楚】,但是!!!它还是有问题的,为啥呢?那咱们以这个公式来算一下图中第一天的累积收益就知道了,很明显第一天的累计收益率就是3%,因为只有一天嘛,但是如果以"(1 + 3%)"来算,很明显它的收益率就为103%,需要将本金去掉,也就是累计收益率的公式为“(1 + 当天收益率)的累积乘积 - 1”,(1 + 3%) - 1 是不是收益率就是3%了?
实现:
接下来则回到python的世界,来计算一下平安银行的累计收益率:
接下来咱们来调用一下:
而看一下可视化的累积收益率的图:
从这个收益曲线图来看,貌似周四买入,周一卖出的简单策略,收益率还是蛮不错的嘛,当然啦,不可能按这么简单的规则来炒股的,纯学习。
最后在写python时有一个小的细节这里提一下,就是在计算累计收益率不是用到了这个函数嘛:
但是!!!你这个函数完全得要你手动敲全,不能智能的提示对吧?其实不提示的原因也很简单,原因是由于:
不知道它加出来的是DataFrame,所以当然也就不知道给你提示它里面的函数喽,要想解决这个提示问题,咱们可以显示的指定这数据是DataFrame既可,如下:
此时再输入时就可以看到提示了: