Home >  > VN.PY学习记录(源码)

VN.PY学习记录(源码)

0

VNPY的大概流程,实例化事件引擎,将事件引擎传到主引擎,再加载Gateway,再加载app中的实盘引擎(含策略)。

ee = EventEngine2()
le.info(u'事件引擎创建成功')

me = MainEngine(ee)
me.addGateway(ctpGateway)
me.addApp(ctaStrategy)
le.info(u'主引擎创建成功')

一、主引擎MainEngine

1.add_engine:

返回的是engine_class的一个实例对像,在vnpy-master\vnpy\trader\engine.py里面有他的用法:
比如
def init_engines(self):
"""
Init all engines.
"""
self.add_engine(LogEngine)
self.add_engine(OmsEngine)
self.add_engine(EmailEngine)

2、add_gateway:

返回的是gateway_class的一个实例对像,在vnpy-master\tests\trader\run.py里面有他的用法:
比如

main_engine.add_gateway(CtpGateway)
main_engine.add_gateway(IbGateway)

3、add_app:
在vnpy-master\tests\trader\run.py里有他的用法:

42: main_engine.add_app(CtaStrategyApp)
43: main_engine.add_app(CtaBacktesterApp)
44: main_engine.add_app(CsvLoaderApp)
45: main_engine.add_app(AlgoTradingApp)

4、主引擎中的cancel_order, send_order, subscribe, connect 都是来源于\vnpy\gateway\ctp\ctp_gateway.py:
里面有这些函数的定义。

比如connect,就是调用了CtpGateway类的connect方法。

def connect(self, setting: dict, gateway_name: str):
"""
Start connection of a specific gateway.
"""
gateway = self.get_gateway(gateway_name)
if gateway:
gateway.connect(setting)

二、Base.py

位于vnpy\app\cta_strategy下面,

本文件中包含了CTA模块中用到的一些基础设置、类和常量等。

其中的类StopOrder对应的委托类型为本地停止单,可以视作一种条件触发单:

当前分钟线收盘价格为100,希望在接下来当价格突破102时买入
发出一个102买入的StopOrder,该订单仅存在于本地引擎中,实时监控最新的价格
当引擎收到第一个价格超过102的Tick时,会立即发出市价委托(或者可立即成交的限价委托)买入
回测引擎采用特殊撮合模式来模拟实盘中StopOrder的效果,实现回测中的K线内成交


、实盘引擎CtaEngine(App里面的模块)

vnpy\app\cta_strategy里面有一个engine文件,定义了CtaEngine。

class CtaEngine(BaseEngine):

本文件中包含的是CTA策略模块的实盘策略引擎,由VnTrader加载管理,用户无需直接调用。

四、回测引擎

继承于BaseEngine,

最新的回测引擎用的是RQdate的数据。

一个指令生命历程:回测时,策略模块发出交易指令(对应指令的方法,如buy()、short()等),对应的指令方法将指令传给模板的senOrder()方法,该方法中先判断trading参数是否为True,若为True,则把指令传给回测引擎的sendOrder()方法,该方法会将相应的指令保存在workingLimitOrderDict或workingStopOrderDict字典中等待被撮合(实际上就是回测是newBar()方法中有相应的撮合方法代码),撮合后该指令会从字典中删除,该指令生命就此结束;若为False,则会返回一个空列表,不会传到回测引擎中,即该指令的生命在这里就结束了。

取前一根K线的价格,它这里用了-2?
https://blog.csdn.net/S_o_l_o_n/article/details/81366884

五、关于策略模板CtaTemplate

策略都是继承于CtaTemplate,那么这个CtaTemplate是来自哪里呢?
原来它是从from vnpy.app.cta_strategy import 这里引入的,在vnpy\app\cta_strategy下面有一个template.py文件,里面就有class CtaTemplate(ABC):
里面也定义了buy ,sell

总体上来看,上面的ctaTemplate的方法分四类:
1、策略管理类:初始化、启停,
2、k线管理类:ontick、onbar
3、指令管理类:委托类(sell、buy、short、cover),onOrder,Ontrader
4、历史数据管理类:insertTick、 insertBar、saveSyncData、getPriceTick

这个文件中还包含了另外一个TargetPosTemplate(目标仓位模板)

六、Event

所谓的事情驱动就是你要监听一些事件,当某些事件发生的时候,要分配相对应的方法进行处理。完成这个过程的东西我们抽象出来之后就叫做事件驱动引擎了。

事件引擎就是监听事件,并调用相对于的方法来对事件进行响应。那么很显然我们就需要一个放置事件的地方,当我们的引擎监听到一个,调用处理的函数的时候,可以去处理下一个。这里,self.__queue = Queue()就是初始化了一个FIFO,FIFO就是先进先出(First In First Out),也就是排队,先被监听到的事件先调用处理的函数,很公平。当然,从作者的这个FIFO中可以看出,其实事件是没有优先级的,相对来说简化处理了,其实如果比较复杂的话,应当考虑事件队列中事件的优先级问题。

首先在 ctaEngine 初始化时候,会分配eventEngine实例,再通过下面代码注册处理事件,当某类事件收到时候,调用对应的方法,比如事件类型EVENT_ORDER, 对应的方法是self.processOrderEvent。

class ctaEngine
def registerEvent(self):
"""注册事件监听"""
self.eventEngine.register(EVENT_TICK, self.processTickEvent)
self.eventEngine.register(EVENT_ORDER, self.processOrderEvent)
self.eventEngine.register(EVENT_TRADE, self.processTradeEvent)

class eventEngine
def register(self, type_, handler):
"""注册事件处理函数监听"""
# 尝试获取该事件类型对应的处理函数列表,若无defaultDict会自动创建新的list
handlerList = self.__handlers[type_]

# 若要注册的处理器不在该事件的处理器列表中,则注册该事件
if handler not in handlerList:
handlerList.append(handler)

在 eventEngine 中的 register函数就是处理的方法通过 __handlers字典来对应,__handlers是defaultdict(list),是一种特殊的字典,最大特点就是如果同一个 key 值插入不同 value ,他不会像就普通 dict 用新的替代,而且变成 {key:[value1,value2 , ……]} 这样存储。这样就可以让同一个 type ,可以有对应多个接收 handler 。

eventEngine还有一个 def _process(self, event: Event): 在一个内部队列__queue中不停抓起 event ,通过检索字典 __handlers来分配到对应的函数处理。那么谁放入新的event呢,就是一个调用put(event)函数向事件队列插入事件。这个时候发现一个特殊的 EVENT_TIMER ,看了半天,感觉可以理解为是一个节奏控制器,每一秒去做一次 process ;那么对于高频来说,可能换成 500 毫秒更合适。

def __process(self, event):
"""处理事件"""
# 检查是否存在对该事件进行监听的处理函数
if event.type_ in self.__handlers:
# 若存在,则按顺序将事件传递给处理函数执行
[handler(event) for handler in self.__handlers[event.type_]]

# 调用通用处理函数进行处理
if self.__generalHandlers:
[handler(event) for handler in self.__generalHandlers]

1.什么是defaultdict?

当我使用普通的字典时,用法一般是dict={},添加元素的只需要dict[element] =value即,调用的时候也是如此,dict[element] = xxx,但前提是element字典里,如果不在字典里就会报错

self.__handlers = defaultdict(list)

如果是空值的话,就会返回一个空的列表。

另外,因为 __handlers是一个字典,用来保存对应的事件调用关系, 其中每个键对应的值是一个列表,列表中保存了对该事件进行监听的函数功能。所以上面的self.__handlers[event.type_]就是取字典里面的值,取到的是一个列表,然后再用for循环显示出来。

好,那么到这里我们就明白了,我们事件处理现场thread的线程函数是run,这个函数的功能就是while循环从事件队列中获取事件,然后把事件交给处理函数precess去进行事件和事件对于的处理函数的匹配,并调用处理函数来处理。

如果要了解,这里有Demo:

https://zjcqoo.github.io/-----http://www.snailtoday.com/archives/11301

最终代码:

import datetime
def simpletest(x):
print ('处理每秒触发的计时器事件:%s' % str(datetime.datetime.now()))

a = EventEngine()
a.register(EVENT_TIMER, simpletest)
a.start()

注意:
def simpletest(x)必须加一个参数,不然会报错。
TypeError: simpletest() takes 0 positional arguments but 1 was given

这里又牵涉到多线程,可以看到这:

https://www.jb51.net/article/131583.htm
爬回co
https://www.cnblogs.com/python2016/p/5751096.html

七 、封装的源头

vnpy-master\vnpy\gateway\ctp\ctp_gateway.py里面有:

class CtpTdApi(TdApi):

class CtpMdApi(MdApi):
def __init__(self, gateway):
"""Constructor"""
super(CtpMdApi, self).__init__()

大家注意到,这两个类都各自继承了父类MdApi和TdApi,而这两个类的代码,大家是看不到的,是ctp编译之后的,只能import使用,而不知道其代码。我们看一下vnpy-master\vnpy\api\ctp\__init__.py下面的init的代码:

from .vnctpmd import MdApi
from .vnctptd import TdApi

而vnctpmd文件夹里面是没有MdApi这个py文件的。后面有机会讲ctp封装的时候给大家解释这个。

六、关于json文件

在class CtaEngine(BaseEngine):里面有data_filename = "cta_strategy_data.json"这样的,可是找不到json文件,难道是要在安装了vn.py的电脑上才能看到?

十、DataEngine
https://blog.csdn.net/qtlyx/article/details/84845093中提到的DataEngine,在新版本中已经变成了
class OmsEngine(BaseEngine):
也在vnpy-master\vnpy\trader\engine.py下面。

https://blog.csdn.net/qtlyx/article/details/84865972

这个六、和现在的架构是一样的:https://blog.csdn.net/qtlyx/article/details/84924990

关于CTA引擎的介绍:

https://www.jianshu.com/p/785fe39ced54

本文暂无标签

发表评论

*

*