干货 | 用python从零实现基于事件驱动的量化回测(Back
为何写类似的文章。最近有朋友问我,还能写代码么,颇有“廉颇老矣,尚能饭否?”的意味。作为一直信奉"talk is cheap, show me the code"理念的资深码农,深感利用业余时间奉献出来实现这个系统的必要性。
一个题外话,“talk is cheap, show me the code”在知乎上有经典中文翻译:
“屁话少说,放码过来”。
“代码胜于雄辩”。
“空谈误国,写代码”。
包括现在火热的深度学习,大家都在谈,谈理想,谈恐惧。最理想的方式,实现它,后续计划结合深度学习在金融文本领域的挖掘,输出一系列的文章,特点就是都是代码实现,不空谈。
前一篇文章提到过“量化回测系统”是AI量化的基础必要组成。尽管通过回测,取得良好结果,不代表未来有好的收益。但反向是可以证明的,也就是使用回测过程显示结果不好的策略,是一种冒险。
那“量化回测系统”是什么呢?
量化交易,就是计算机按指定的策略,产生特定的交易信号。因为二级市场所有历史数据、财务数据都是可获得的,那么既然是计算机进行操作,就很容易对整个交易进行复盘。好比我们玩扑克牌或者“杀人游戏”,在一局结束,总有自认逻辑严密的人喜欢对游戏过程进行复盘,刚哪里可以做得更好,分析出了什么问题,确保后续如何优先,调整和提高。这就是“回测”系统的价值。
回测系统几乎是无代价的,我们可以调整参数,或者各种创造性的想法,这个系统会告诉你,在过往一段时间(比如一年),假如你真的这么做了,收益率,最大回撤是多少。这就避免我们花大量真金白银去市场上买教训,更重要的事,节省时间,你可以在几分钟内模拟几个股市周期,穿越牛熊。你如果要想经历几个股市周期,那估计是以10年甚至更长的时间维度来计算,但对于量化回测引擎,几秒钟就可以告诉你结果,且没有产生实质的损失。
量化回测系统对于初学者是神秘的,但其原理并不复杂。此系列文章的目的,就是带着大家,用python从零实现一个基于事件驱动的量化回测系统。
为什么是python?这个在很多文章里都有论述,对比java/c++以及同为数据领域的matlab或R,python都有非常大的优势,总之作者认为它是大数据,机器学习,人工智能时代最好的选择,没有之一。
如下图python的增长速度是无与伦比的。
按行业的常规做法我们同样选择使用“事件驱动(Event-driven)”的方式。选择事件驱动的好处,相对于直接用pandas来运算,有很多好处,当然也有复杂性,运算相对慢的缺点。这里不展开论述,后续有机会用pandas直接实现一个直观的回测运算。
先亮一下总体结构,aiquant是顶层包,engine是引擎主体。下面是几个主模块。test目录下是自动化测试用例,demo目录用于写一些策略。
一个事件驱动的系统,通常包含如下组件:
Backtest,主系统入口,在run函数下对市场数据进行循环,并产生各种事件。
事件 -Event类,是事件驱动系统的基础事件类,它包含一个type(本系统我们实现 ‘BAR’,'SIGNAL','ORDER','FILL'),这个type代表事件类型,然后这些事件,会在事件循环里被一一处理。
事件队列 -events_queue就是一个内存队列,新产生未处理的event都在这里排队,等待处理。这里直接用python内置的Queue即可。
数据处理器 -DataHandler类,负责处理数据,包含模拟历史数据或实盘的市场真实数据到达。
策略类 -Strategy类,这是一个抽象基类,我们想表达的策略,算法,都在这个策略类里实现,每次bar事件,即一个新的(tick)k线到达,就会触发这个strategy.onbar函数,在onbar里实现算法即可。
投资组合类 -Portfolio抽象基类,负责头寸跟踪、订单管理,投资组合管理,可以进一步做收益分析、风险管控,结果绘图等交易执行单元 -ExecutionHandler,负责模拟交易所交易或实盘订单执行。
importqueue
fromaiquant.engine.dataimportCSVDataHandler
fromaiquant.engine.portfolioimportSimplePortfolio
fromaiquant.engine.executionimportSimulatedExecutionHandler
fromaiquant.engineimportvisual
classBacktest(object):
def__init__(self,strategy_cls):
self.events = queue.Queue()
self._data_handler = CSVDataHandler(self.events,'../demo/testdata',['600008','600018'])
self._portfolio = SimplePortfolio(self._data_handler,self.events)
self._execution = SimulatedExecutionHandler(self.events)
self._strategy = strategy_cls(self._data_handler,self.events)
self._init_event_handlers()
#挂载事件,每个事件类型对应一个处理函数
def_init_event_handlers(self):
self._event_handler = {}
self._event_handler['BAR'] =self._handle_event_bar
self._event_handler['SIGNAL'] =self._handle_event_signal
self._event_handler['ORDER'] =self._handle_event_order
self._event_handler['FILL'] =self._handle_event_fill
#主系统入口
defrun(self):
while True:
#每次loop,都会让data_handler触发新的bar事件,即有一个新bar到达
#b_continue_backtest是数据管理器的标志,置False则回测停止
ifself._data_handler.b_continue_backtest:
self._data_handler.update_bars()
else:
break
#可能有多事件,要处理完,如果队列暂时是空的,不处理
while True:
#队列为空则消息循环结束
try:
event =self.events.get(False)
exceptqueue.Empty:
break
else:
self._handle_event(event)
defshow_results(self):
df_all,idicators =self._portfolio.calc_performance()
print(df_all.tail())
visual.show_draw_result('回测净值曲线图',df_all,idicators)
def_handle_event(self,event):
handler =self._event_handler.get(event.type,None)
ifhandleris None:
print('type:%s,handler is None'%event.type)
else:
handler(event)
def_handle_event_bar(self,event):
print('OnBar Event',event.type)
self._strategy.onbar()
self._portfolio.update_timeindex()
#处理策略产生的交易信号
def_handle_event_signal(self,event):
print('OnSignal Event', event.type)
self._portfolio.on_signal(event)
#处理ORDER
def_handle_event_order(self, event):
print('OnOrder Event', event.type)
self._execution.execute_order(event)
#处理FILL
def_handle_event_fill(self, event):
print('OnFill Event', event.type)
self._portfolio.on_fill(event)
如上部分,实现了基于事件驱动的量化回测系统的核心组件和事件驱动的流程,后续系列文章,我们再对各个组件的原理,代码实现详细展开。
关于作者:魏佳斌,互联网产品/技术总监,北京大学光华管理学院(MBA),特许金融分析师(CFA)。深度关注互联网发展趋势,AI金融量化。致力于使用最新的人工智能技术去理解经济、金融,实现信息增值。
扫描下方二维码,关注:AI量化实验室,了解AI量化最前沿技术、资讯。