上次利用Backtrader对实盘交易记录进行了回测。现在打算对程序进行重构,参考《重构——改善既有代码的设计》。
先用git branch refactoring开一个分支,checkout到该分支开始干活。
先是一些基本理论。
如果发现由于代码结构无法方便的为程序添加特性,就先重构程序,使特性添加比较容易,再添加特性。
重构的第一步:建立可靠的测试环境。
重构步骤的本质:由于每次修改的幅度都很小,所以任何错误都很容易发现。
任何一个傻瓜都能写出计算机可以理解的代码。惟有写出人类容易理解的代码,才是优秀的程序员。
重构是在不改变软件可见行为的前提下,提高其可读性,降低修改成本。
增添功能与重构分开。
重构改进软件设计,使软件更容易被理解,帮助调试,提高编程速度。
何时重构?事不过三,三则重构。添加功能,修补错误,代码复审时重构。
下面就来看看我上次写的代码有什么”坏味道”
1 | # coding:utf-8 |
1.重复的代码
notify_order成员函数里,以及数据初始化里都有重复的代码。
2.过长函数
main函数,next成员函数都太长了。
每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。我们
可以对一组或甚至短短一行代码做这件事。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地那么做。
3.过大的类
单一类不要做太多事,python这个问题貌似不严重。
4.过长参数列表
面向对象,函数需要的某些参数可以设为类成员变量,而不必作为函数参数。问题是很多python库都有长参数列的问题。
5.发散式变化
针对某一变化需要修改多个类的情况,最好将类拆分为数个,使每个变化只需修改一个类。
策略类貌似还可以改。
6.散弹式修改
一个变化要修改多个类,可将这些类合并,使得每个变化只修改一个类。
7.依恋情结
成员函数对某个对象的兴趣高于对自己的类的兴趣。
8.数据泥团
几个类中有相同的数据项。
9.基本类型偏执
用基本类型组成一些类型。
10.switch问题
少用,考虑用多态替代。python貌似没有。
11.平行继承体系
为某个类增加之类时需要为另一个类也增加子类。
12.冗余类
没啥用的类,删!
13.为未来设计
函数和类的唯一用户是测试程序。
14.临时变量
仅为某种特定情况设置的临时变量。
15.过度耦合的消息链
一个对象要求另一个对象,另一个对象在要求别的对象……
16.中间人
一个类需要太多调用另一个类完成其功能。直接调用实际工作的类。
17.亲密关系
两个类关系密切。分开或合并。
18.异曲同工的类
做类似的工作却有不同名称的类。合并。
19.不完美的程序库
自己改吧。
20.纯数据类
将使用该类的地方移入类中。
21.被拒绝的遗赠
子类不想继承父类某些内容。设计错误,重新设计。
22.过多的注释
多余的注释,说明代码需要重构。
进行重构,首要前提是有一个可靠的测试环境。就是要先有测试再重构啦,搜了一下,python标准库自带了unittest,但不太好用。还有个pytest,试试这个。
花了几天看了一下pytest,会基本操作了。主要就是以test_开头或_test结尾命名测试函数或类,可以保存到以test_xxx为文件名的单独文件里,然后在命令行里用pytest执行测试。测试里主要用assert语句进行测试。其它用法参见文档吧。继续重构。本来想先为原来的程序写单元测试,但是原来的程序都搅和到一起了,很难测试。这也是我想要重构的原因。
提炼函数(extract method)
将长的函数提炼成几个短的函数,或者类。main函数部分太长了,分成几个部分吧。
1 | if __name__ == "__main__": |
分成这么几个函数,主函数短多了。
TradeStrategy类里的next函数也太长了,把交易功能提成函数吧。
1 | def __doTrade(self, data, name, price, stock, commit, orderType): |
有几种情况:没有使用变量的,直接提出去即可。使用了变量但是没有改变的,只在提炼区里使用的,在提炼函数里声明使用;在提炼区外也使用的,作为参数传入。提炼区改变了并且提炼区外要使用的变量,提炼函数返回值返回。
再把交易过程从next()函数中完全提取出来吧
1 | # 具体交易逻辑,可以改的。 |
再改策略的时候直接改doTrade就行了。
再改一下notify_order成员函数,把重复的地方提炼成函数。
1 | # 输出交易过程 |
再把整个数据准备,建立回测对象的过程封装到类里吧。
放到一个新的文件backtest.py里。
1 | # coding:utf-8 |
再调用
1 | if __name__ == "__main__": |
跟重构以前的运行结果一致。merge到主分支上,删除refactoring分支,提交。
本文代码: https://github.com/zwdnet/MyQuant/tree/master/46 trade.py和backtest.py两个文件。
总结一下,重构主要目的是在不改变程序功能的前提下消除代码的“坏味道”,让代码更加可读,bug更少。我也尝试了一下用pytest进行测试驱动开发,感觉完全先写测试再写代码还是比较困难。尤其是一些读取数据,文件操作等地方,测试很难写。还是先写出个能干活的程序,再用重构的原则,一点一点改,改一点就运行吧。接下来打算用这些代码实现一些经典的交易策略吧。
我发文章的三个地方,欢迎大家在朋友圈等地方分享,欢迎点“在看”。
我的个人博客地址:https://zwdnet.github.io
我的知乎文章地址: https://www.zhihu.com/people/zhao-you-min/posts
我的微信个人订阅号:赵瑜敏的口腔医学学习园地