量化交易是指以先进的数学模型替代人为的主观判断,利用计算机技术从庞大的历史数据中海选出能带来超额收益的多种“大概率”事件以制定策略。它极大地降低了市场波动给投资者情绪带来的影响,避免在市场极度狂热或悲观的情况下做出非理性的投资决策。
我们总是在不停的寻找各种问题的答案,如"今天中午吃什么?",“Excel 如何进行自动求和”,“520 该送女朋友什么样的礼物”等,搜索答案,最常用的方式是通过搜索引擎来获取答案,通过搜索我们能找到很多与上述问题相关的答案。
而如果想通过搜索引擎来知道“明天该买什么股票”,“这个股票明天能不能抄底”等诸如此类的问题。
搜索系统无法告诉你较为准确的答案。量化系统的主要功能就是类似于搜索系统,告知投资人,今天该买什么,或者今天该卖什么。
普通的散户往往会根据自身的偏好和以往的经验来进行策略的选择,也就是我们所说的“盘感”,通过每天对盘前,盘中,盘后的观察分析,逐渐总结出自己的一套交易逻辑。当然很多散户秉着”没有逻辑就是最好的逻辑“的玄学策略,经常就是一把梭哈,"all in"才是永恒的信仰!
这种毫无逻辑的赌徒,在孤注一掷后往往会产生莫名的自信感,总觉得自己一定能和幸运女神勾肩搭背。
倘若真被他们赌对了一两次,就觉得自己已经掌握了大 A 的脉搏,感受到了财富的律动,从而变得更加的贪婪,更加的愚蠢,到最后成批成批割肉跑路。
世人皆爱赌,那既然要赌,我们必须要学会如何选择盈面较大的一方。量化交易通过将数据输入“量化模型”之后,利用计算机及统计学的知识进行数据分析,从而找到数字背后所代表的规律(此处的规律并不是指客观存在的定律如 1+1=2,无论何时何地都不会改变,而是指有较大几率发生的情况,一个词概括就是“往往”)。
有目的性地学习能显著的提高学习的效率,长时间没有正反馈的学习会浪费我们宝贵的时间,让我们焦头烂额,难以为继。因此,在学习量化交易之前,我们必须要清楚的知道学习量化交易的目的究竟是什么,对我们的投资又会有怎样的帮助。
很多人一听说量化交易往往就会误以为是机器人炒股,AI 投资等等。但这其实并不是全面的。举个例子,大 A 股一直流传着“黑色星期四”的诅咒,即每到周四的时候 A 股有很大的概率会下跌。所以很多人的定投策略都会选择在周四尾盘买入,这样或许能为自己带来更大的收益。
而这种说法目前还停留在“江湖传言” 的阶段,没有人能对它的准确性作出保证,很多给散户们分享炒股策略的“股神”往往也会在最后“贴心”的提醒大家“本文只阐述个人观点,不构成任何投资建议!股市有风险,投资需谨慎!”。所以,我们散户迫切需要一种手段去验证,去优化这些“大师”的策略。
量化交易的目的之一就是通过历史的数据对我们选择的策略进行回测,例如,对于“中国平安”这只股票,从它的上市那天开始至今,如果我每周四尾盘全仓买入,下周三再以开盘价卖出(因为考虑到周四有可能会大跌),如此不停循环往复,扣除手续费和印花税,最后的资金曲线会是什么样的呢?如果这个策略在这只股票上表现的不理想,那会不会在别的股票上就会很有效?通过量化交易我们能够去验证各种股神的“战法”,分析他们的利弊,从而更好的总结出自己的一套交易策略。下面本文将通过编码的方式来验证流传已久的“黑色星期四”。通过以下代码,可以很直观的输出此策略在“中国平安”这只股票上的表现。
from datetime import datetime
import backtrader as bt
from backtrader_plotting import Bokeh
from backtrader_plotting.schemes import Tradimo
from learn.strategy1 import Strategy1
if __name__ == '__main__':
start = datetime(2020, 1, 1)
end = datetime(2021, 4, 1)
# 加载数据
data = bt.feeds.BacktraderCSVData(dataname='G:\\python\\stock\\feed\\601318_中国平安.csv', fromdate=start, todate=end)
# 初始化cerebro回测系统设置
cerebro = bt.Cerebro()
# 将数据传入回测系统
cerebro.adddata(data)
# 将交易策略加载到回测系统中
cerebro.addstrategy(Strategy1)
# cerebro.optstrategy(TestStrategy, maperiod=range(3, 30))
cerebro.addobserver(bt.observers.DrawDown)
# 设置初始资本为10,000
cerebro.broker.setcash(100000)
# 设置交易手续费为 0.2%
cerebro.broker.setcommission(commission=0.002)
# Add a FixedSize sizer according to the stake
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name="sharpe")
cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name="AR")
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="DD")
cerebro.addanalyzer(bt.analyzers.Returns, _name="RE")
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="TA")
start_money = cerebro.broker.getvalue()
# 运行回测系统
result = cerebro.run()
end_money = cerebro.broker.getvalue()
print('初始资产: %.2f' % start_money)
print('最后资产: %.2f' % end_money)
rate = round((end_money - start_money) / start_money * 100, 2)
print('收益率: %.2f' % rate, '%')
# 输出分析结果
for year, ret in result[0].analyzers.AR.get_analysis().items():
print("年化收益:\t {} {}%, ".format(year, round(ret * 100, 2)))
print("最大回撤:{},最大损失:{}".format(result[0].analyzers.DD.get_analysis()['max']['drawdown'],
result[0].analyzers.DD.get_analysis()['max']['moneydown']))
ta_data = result[0].analyzers.TA.get_analysis()
print("开仓:{}次,当前开仓的数据:{},平仓:{}次".format(ta_data['total']['total'], ta_data['total']['open'],
ta_data['total']['closed']))
print("开仓:{}次,当前开仓的数据:{},平仓:{}次".format(ta_data['total']['total'], ta_data['total']['open'],
ta_data['total']['closed']))
print("盈利:{}次,总共盈利:{},平均盈利:{},最大盈利:{}".format(ta_data['won']['total'], ta_data['won']['pnl']['total'],
ta_data['won']['pnl']['average'], ta_data['won']['pnl']['max']))
print("损失:{}次,总共损失:{},平均损失:{},最大损失:{}".format(ta_data['lost']['total'], ta_data['lost']['pnl']['total'],
ta_data['lost']['pnl']['average'], ta_data['lost']['pnl']['max']))
print("共有{}根k线,每{}根k线会发生一次交易,最大间隔{}根k线,最小间隔{}根k线".format(ta_data['len']['total'], ta_data['len']['average'],
ta_data['len']['max'], ta_data['len']['min'], ))
# 可视化
b = Bokeh(style='bar', plot_mode='single', scheme=Tradimo())
cerebro.plot(b)
import backtrader as bt
class Strategy1(bt.Strategy):
def next(self):
weekday = self.datas[0].datetime.date(0).weekday()
# 周三卖 1代表是周二,周二下单,周三以开盘价买
if weekday == 1 and self.position:
self.sell(size=1000)
# 周三卖 2代表是周三,周三下单,周四以收盘价卖
if weekday == 2 and not self.position:
self.buy(size=1000)
运行后得到结果:
以两年的收益率来说,这简直是个惨不忍睹的策略。现在我们来换一种策略,模拟每周四定投一手(100 股=1 手),然后年底卖出,看看最后的收益率是什么样的。
class Strategy1(bt.Strategy):
def __init__(self):
self.num = 0
def next(self):
weekday = self.datas[0].datetime.date(0).weekday()
# 周三卖 1代表是周二,周二下单,周三以开盘价买
if weekday == 1 and self.num >= 52:
self.close()
self.num = 0
# 周三卖 2代表是周三,周三下单,周四以收盘价卖
if weekday == 2 and self.num < 52:
self.buy(size=100)
self.num = self.num + 1
从结果上来看,这种坚定的投资策略比我们刚刚的那种“自作聪明”的策略有更大的利润空间。
对于大部分的策略我们都可以去编写代码用历史数据进行回测,从而验证这些策略的优劣。
自动化交易是量化交易的重要组成部分,当我们的策略产生一个买入或者卖出的交易信号后,自动化交易模块便会去响应此信号,从而调用券商接口或者其他途径进行自动下单。如上述案例中的 self.sell()和 self.buy(),self.close()都是交易型号。自动化交易分为实盘交易和模拟盘交易,一般有新的策略产生先会去模拟盘上跑个几周或者几个月,稳定下来后才会去真正实盘操作。自 2015 年后官方已经关闭了所有自动交易的接口,要想实现自动交易我要我们自己是尝试别的方法。对于散户来说,每个月不会太过频繁的交易,因此,散户注重的应该是策略的好坏,自动化交易只是一个锦上添花的东西。
量化交易中大多数策略是基于对历史规律的总结,在规律的基础上发现概率优势,它的最大理论依据是人性的相似性以及人性很难改变的事实。如果每一个瞬间的股票价格都是全体交易者对价值所达成的一种瞬间共识,那么历史的规律在今后的交易中同样具有指导意义。历史是有惯性的,无论人,社会,国家,市场等等,惯性作用于方方面面,在股市中常被称为“趋势”。量化的最大价值是能在趋势产生之初就发现它,我们并非一定要买在股价的最低点,卖在股价的最高点,我们需要的是量化模型能帮我们判断出一个区域。
量化模型很像我们小时候常常玩的找规律的游戏,因此不可避免的很多人都会将机器学习,人工智能的技术用在量化交易上。然而机器学习和深度学习产生的模型对股票规律的可解释性太差,数据太过拟合,举个例子,
股票代码 | 2021 | 2020 |
000001 | 涨 | 涨 |
000002 | 跌 | 跌 |
000003 | 涨 | 涨 |
000004 | 跌 | 跌 |
000001 和 000003 在这两年中都是涨的,000002 和 000004 在这两年中都是跌的,如果用这些数据去训练模型,很有可能得出来“偶数位的股票代码会跌,奇数位的股票代码会涨。”的结论,这显然是一个比较扯淡的结论,但是对于 AI 来说,它觉得这就是它的世界中的真理。因此,我们在发现规律的同时也要考虑规律背后所代表的含义。
很多交易者常常是“交易瘾”患者,他们做交易时会有快感,其交易的主要目的是寻找刺激,他们企图抓住市场中每一次价格波动,贪婪地不想放过任何一次机会,如果买入后的走势符合他们的预期则表现为自负得意,如果走势背离预期则表现为懊恼,需寻找“复仇”机会。其次,很多交易者都喜欢逆势操作,将“别人贪婪的时候我恐惧,别人恐惧的时候我贪婪”奉为至高信条。
投资者在下跌时买入股票会有一种错觉:反正别人买的比我高,我不害怕,要死一起死,相反投资者在股票持续突破高位的时候,往往会变的畏畏缩缩,不敢冒进,天天想着明天大跌然后上车。量化交易就是来规避这些人性的弱点,它没有感情,具有很强的执行力,确保策略的正确执行。
随着技术的不断发展,量化交易的各种技术框架层出不穷,下面分享几个常用的优秀技术和框架。
股票的数据一般需要编写爬虫从各大财经网站爬取,也可以使用Tushare[1]框架去获取每日的股票数据。
import tushare as ts
ts.get_hist_data('600848') #一次性获取全部日k线数据
结果显示:
open high close low volume p_change ma5 \
date
2012-01-11 6.880 7.380 7.060 6.880 14129.96 2.62 7.060
2012-01-12 7.050 7.100 6.980 6.900 7895.19 -1.13 7.020
2012-01-13 6.950 7.000 6.700 6.690 6611.87 -4.01 6.913
2012-01-16 6.680 6.750 6.510 6.480 2941.63 -2.84 6.813
2012-01-17 6.660 6.880 6.860 6.460 8642.57 5.38 6.822
2012-01-18 7.000 7.300 6.890 6.880 13075.40 0.44 6.788
2012-01-19 6.690 6.950 6.890 6.680 6117.32 0.00 6.770
2012-01-20 6.870 7.080 7.010 6.870 6813.09 1.74 6.832
ma10 ma20 v_ma5 v_ma10 v_ma20 turnover
date
2012-01-11 7.060 7.060 14129.96 14129.96 14129.96 0.48
2012-01-12 7.020 7.020 11012.58 11012.58 11012.58 0.27
2012-01-13 6.913 6.913 9545.67 9545.67 9545.67 0.23
2012-01-16 6.813 6.813 7894.66 7894.66 7894.66 0.10
2012-01-17 6.822 6.822 8044.24 8044.24 8044.24 0.30
2012-01-18 6.833 6.833 7833.33 8882.77 8882.77 0.45
2012-01-19 6.841 6.841 7477.76 8487.71 8487.71 0.21
2012-01-20 6.863 6.863 7518.00 8278.38 8278.38 0.23
将数据抓取下来后,可以保存到关系型数据库如 mysql,非关系数据库如 MongoDB,或者直接以 csv 格式和 hdf 文件的方式保存,也可以考虑去研究一下时序数据库,如 InfluxDB、TimescaleDB 等。
回测框架一般包含以下几种功能:
•编写策略;•对策略进行回测;•对策略的结果进行分析;•支持多种股票的对比;
其中,我们主要的工作是在编写合理的策略,剩下的这些功能,都是框架已经封装好的,使得用户能更好的集中精力进行策略的开发。
上述“黑色星期四”的例子使用的是BackTrader[2]框架,Backtrader 是一个基于 Python 的自动化回溯测试框架,作者是德国人 Daniel Rodriguez,是一个易懂、易上手的量化投资框架。相似的框架也有很多如 PyAlgoTrade,Zipline,pysystemtrade 等。
有很多人会觉得自动化交易很酷,可是遗憾的是,目前官方已经完全关闭了对散户的交易接口。要想实现自动化交易最常用的方式是编写如 selenium、按键精灵等,模拟用户行为的方式去实现。
PyAutoGUI 是一个纯 Python 的 GUI 自动化工具,其目的是可以用程序自动控制鼠标和键盘操作,多平台支持(Windows,OS X,Linux)。
在电脑上安装同花顺的交易软件
通过 PyAutoGUI 编写自动打开此软件然后买卖股票的代码便能够实现股票的自动化交易。
[1]
Tushare: http://tushare.org/index.html[2]
BackTrader: https://www.backtrader.com/docu/quickstart/quickstart/