发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2020-07-20
上一篇文末的抽奖福利中
获奖的朋友是:
Shirley S
请在48小时内通过公众号留言联系领取奖品~
讲到中国金融市场的量化交易发展历史,绕不开的一个主题就是CTA策略。CTA这个词本身是Commodity Trading Advisor的缩写,在定义上是欧美一种交易期货衍生品为主的投资顾问,或者也可以理解为一种对冲基金的类型。
但在国内的CTA这个词,更多指的是一种以趋势跟踪(Trend Following)为主的量化交易策略,交易品种也同样是期货。因为其门槛较低(相对于多因子、期权波动率等策略),CTA策略大概2010年前后就开始在国内市场上流行起来。
虽然没有做过特别正式的统计,但CtaStrategy无疑一直是vn.py平台内用户量最大的量化应用模块。所以在这第二篇文章中,就来讲讲和CtaStrategy相关的两个项目:CTP API和MultiCharts。
类型:交易接口
国家:中国
语言:C++
启发:数据定义
CTP是上期技术公司推出的一套期货交易柜台系统,目前大概70-80%的期货公司都已经上线,其整体架构如下图所示:
用今天2020年的眼光来看,CTP柜台在交易方面的技术优势可能已经不那么明显,但是它的交易接口CTP API却走得远得多,甚至在一定程度上成为了国内金融市场的量化交易接口设计标准,类似FIX协议在欧美的地位。
究其原因,个人观点可以总结为代码精简和功能完善两个方面。
首先,不管行情还是交易,接口中都只包含两个大类:
API类
SPI类
没有麻烦的数据打包器、解包器,也没有消息客户端、主推订阅者,更没有一大堆的配置选项(授权文件、加密签名、缓冲区大小......),只有简简单单的两个类,不管哪家期货公司几乎都是完全一致的配置,做到了真正的标准化接入。
其次,在业务数据的定义上,不像恒生T2SDK等消息收发类接口需要查看Excel文档,才能知道代码要怎么写(还特别容易掉坑),CTP API则是采用了Struct结构体的定义形式,开发者只需看着.h头文件中的内容就能快速完成代码开发。其中最核心的业务数据结构,甚至两只手都能数的过来:
主动请求
被动回报
vn.py的底层接口设计,整体上借鉴了CTP API的风格。通过vnpy.trader.object定义了所有的标准数据结构,如ContractData、TickData、OrderRequest等,通过vnpy.trader.gateway定义了标准对接基类BaseGateway,从而实现将所有不同类型的接口(C++、Python、REST、FIX等)统一对接到vn.py系统上层的量化策略应用。
类型:商业软件
国家:美国
语言:C#
启发:CTA策略
MultiCharts(缩写MC)是一款主要针对CTA类策略的量化交易平台,其创始人Dennis Globa曾经也是TradeStation(缩写TS,另一款起源于1991年的量化交易平台)的忠实用户。但因为觉得TS的功能有所不足(数据源限制、没有组合回测等),所以在2005年决定自立门户推出了MC,并且在策略开发语言上选择和TS保持兼容,使用经典的EasyLanguage语言。
EasyLanguage语言的特点,用一句话概括叫做:用普通英语来写量化策略。什么意思呢,看一行代码:
buy 2 contracts this bar close;
几乎不需要去学什么编程语法,你也能看明白这是要在当前K线收盘点买入2手合约。
简单易用的EasyLanguage让用户能够很快把策略代码给实现出来,同时MC也提供了许多强大的历史回测和参数优化功能:
逐日盯市的回测曲线
200个多个回测统计指标
参数优化结果的3D绘制
准确的历史数据回测,是CTA策略开发过程中最为重要的环节。所以vn.py的CtaStrategy模块在历史数据回测上,采用了和MC一致的计算逻辑,列举一些关键点:
委托类型
成交规则
同时也结合Python语言的优势做了额外改进:
同样,有什么想问的问题或者分享的内容,欢迎在文章下方留言。本期我们将会随机抽取一位留言者赠送《vn.py全实战进阶 - 期权零基础入门》课程的5折优惠券一张。
发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2020-07-20
和其他软件项目一样,vn.py同样也是建立在诸多前人工作的成果之上,这些项目中凝结的知识和经验给了vn.py许多启发,有些是技术选型的方向,有些是新功能的灵感,也有些是社区运营的思路。
最近几个月在vn.py社区中,开始出现越来越多围绕系统架构和技术体系方面的讨论,而不仅仅是过去围绕策略和交易相关的话题,可能也意味着我们社区成员的整体水平进入了一个更高的阶段?知其然后,更希望知其所以然。
满足用户需求一向是我们的主要工作目标,所以接下来打算做一个围绕vn.py技术架构相关的系列文章,希望能帮助大家去了解其中的诸多技术细节。作为第一篇文章,本文中我们先来看看两个影响了vn.py主要技术选型的项目:Matlab和AlgoTrader。
类型:商业软件
国家:美国
语言:Java/C++/Fortran
启发:编程语言
Matlab本身相信不用我过多介绍了,我本人在大学金融工程课程中唯一接触过的编程语言就是Matlab。几乎所有的作业论文,不管是数据建模、期权定价还是策略回测,基于Matlab的矩阵运算功能以及内置金融模块,都能够相当方便的完成出来,多花点心思的话还能进一步开发出定制化的UI界面工具(如下图所示):
2012年毕业进入工作后,却很快发现了Matlab的一个重要短板:针对数值运算设计的Matlab在事件驱动编程方面相当薄弱,无法作为实盘交易程序的核心。搞定了数据分析,搞定了策略回测,但却倒在了最终的实盘交易。
无奈之下只能去寻找新的编程语言,筛选标准其实还是围绕着Matlab的特点:
筛了一圈,最后找到得答案就是Python。
首先各方面功能足够强大:矩阵运算有numpy,时序分析有pandas,图形界面有PyQt,API封装有boost.python(现在是pybind11了)。总之只要学一种编程语言,就能搞定量化交易种的所有环节。
其次,Python在基础语法上和Matlab近似度很高,对于我本人这种非专业IT出身的量化交易员,只要不怕麻烦不怕累,花点时间硬着头皮学一两个月,很快就能入门上手开始出点成果。不必像C++学个半年,还在和各种野指针、内存泄露的问题上来回折腾不可自拔。
当然Matlab后来也推出了专门针对量化交易的工具库Trading Toolbox,对接了一系列主流的数据和交易系统,例如下图中对接CQG系统的交易流程图:
但可惜还是没有对接国内的CTP柜台。
类型:商业软件(曾是开源软件)
国家:瑞士
语言:Java
启发:系统架构
差不多10年前,欧美量化交易领域曾经有三个非常知名的开源项目,分别是Marketcetera、AlgoTrader、TradeLink(详细介绍可以参考《Quantitative Trading》这本书,同一时间段出版的,也是我本人的量化启蒙书)。随着时间的发展,Marketcetera逐渐没落(已经很久没再看到有什么版本更新或者用户交流了),而AlgoTrader和TradeLink这两家则选择了闭源路线,转型成了商业软件。
AlgoTrader的开发团队主要来自于瑞士,最初也是为了期权波动率交易策略而专门开发。其核心架构如下图所示:
基于Java语言开发的AlgoTrader,在系统层面做了非常好的解耦设计:
三层架构
数据通讯
事件驱动
第一次看到AlgoTrader的时候,感觉只有一个:惊艳,然后立马开始学习Java。无奈几个月的学习后,还是只会调用非常基础的库,量化策略相关的代码一点写不出来。尽管主要原因肯定是我的编程水平太差,但长时间投入出不了成果,兴趣和热情逐渐就磨没了,所以AlgoTrader的学习计划,卒。
虽然没能学得会,倒是把AlgoTrader的系统架构深深印在了脑子里,后面在开发vn.py的过程中也采用了同样的三层架构:
不过由于一些Python语言自身和Java的特性区别,vn.py在细节方面还是做了许多额外修改,比较主要的两个:
最后,有什么想问的问题或者分享的内容,欢迎在文章下方留言,我们将会随机抽取一位留言者赠送《vn.py全实战进阶 - CTA策略》课程的5折优惠券一张。
vn.py社区活动公告
上海地区这边写字楼已经允许举办会议活动了,所以这个月底7月26号的社区活动改为线上线下同时进行,这次的主题是vn.py在外盘市场量化策略方面的分享,活动大纲:
认识外盘市场
a. 全球期货市场
b. 港股美股市场
vn.py的解决方案
a. 交易通道选择
b. 历史数据服务
c. CTA趋势策略
d. 境内外套利交易
直达期货分享
a. 外盘经纪业务
b. 对比IB的优势
报名请扫描图片中的二维码(99元,Elite会员免费参与):
发布于vn.py社区公众号【vnpy-community】
《30天解锁Python量化开发》课程已经更新过半(至25集),计划在9月底全部更新完毕,通过概念讲解和实践操作结合的方式,加上vn.py框架内部代码细节的梳理学习,来帮助你快速掌握Python量化开发能力,详情请戳。
原文作者:KeKe | 发布时间:2020-09-15
不管是研究价差套利策略还是多因子组合策略,都需要同时用到诸多合约的历史数据。
而vn.py目前提供的图形界面数据下载工具(CtaBacktester和DataManager两个模块),每次操作只能下载一个合约的数据,当要下载的合约数量较多时,逐个点过去既浪费时间也容易误操作。
本文中,我们将来介绍如何通过Python脚本执行的形式,直接调用vn.py框架内的数据下载和整理入库功能,来实现全市场期货数据的批量下载和自动更新。
设置合约品种
首先,我们要先生成一个字典,来指定需要下载的数据,关键字段有3个:
基于以上字段,我们即可生成在使用rq_client(位于vnpy.trader.rqdata模块中)下载数据时所需的本地代码vt_symbol(如rb99.SHFE)。
然后,由于要下载全市场品种的行情数据,所以采用字典和列表嵌套的复合数结构来表示:
symbols = {
"SHFE": ["cu", "al", "zn", "pb", "ni", "sn", "au", "ag", "rb", "wr", "hc", "ss", "bu", "ru", "nr", "sp", "sc", "lu", "fu"],
"DCE": ["c", "cs", "a", "b", "m", "y", "p", "fb","bb", "jd", "rr", "l", "v", "pp", "j", "jm", "i", "eg", "eb", "pg"],
"CZCE": ["SR", "CF", "CY", "PM","WH", "RI", "LR", "AP","JR","OI", "RS", "RM", "TA", "MA", "FG", "SF", "ZC", "SM", "UR", "SA", "CL"],
"CFFEX": ["IH","IC","IF", "TF","T", "TS"]}
symbol_type = "99"
在symbols字典中,每个键(key)是交易所的英文缩写,而对应的值(value)则是包含了该交易所各期货品种英文前缀的列表。这样在使用时只需要遍历这个字典,就可以快速生成所有如rb99.SHFE这样结构的本地代码。
设置下载时间段
这一步我们只需对所有品种设置相同的下载数据开始时间和结束时间即可。需要注意的是,rq_client中关于时间的参数采用datetime.datetime格式,所以这里要做到格式的一致,代码如下:
from datetime import datetime
start_date = datetime(2005,1,1)
end_date = datetime(2020,9,10)
执行批量下载任务
完成了前面的两步准备,最后就是运行脚本来执行批量下载任务,脚本的整理逻辑步骤如下:
from vnpy.trader.rqdata import rqdata_client
from vnpy.trader.database import database_manager
from vnpy.trader.constant import Exchange, Interval
from vnpy.trader.object import HistoryRequest
def load_data(req):
data = rqdata_client.query_history(req)
database_manager.save_bar_data(data)
print(f"{req.symbol}历史数据下载完成")
for exchange, symbols_list in symbols.items():
for s in symbols_list:
req = HistoryRequest(
symbol=s+symbol_type,
exchange=Exchange(exchange),
start=start_date,
interval=Interval.DAILY,
end=end_date,
)
load_data(req)
写好脚本文件后可以在cmd中通过python命令来直接运行,或者也可以选择将代码写在Jupyter Notebook来运行。每完成一个品种的下载任务后会打印输出相应的日志信息:
如果要下载小时或者分钟级别数据,只需把上一段代码中创建HistoryRequest对象时的interval参数,由日线周期的Interval.DAILY改成分钟线的Interval.MINUTE或者小时线的Interval.HOUR即可。
尽管已经构建了包含全市场品种的历史数据库,但随着每日交易数据的不断产生,还是需要每天下载新的数据来更新数据库。
每日定时执行
通常我们希望在收盘后的某个时间点(比如下午5点),来自动执行数据更新任务。最简单的方案就是在Python脚本中启动一个持续运行的while循环,然后不断检测当前时间,到下午5点时就开始下载更新数据,其他时间则sleep等待,相关代码如下:
from datetime import datetime, time
from time import sleep
last_dt = datetime.now()
start_time = time(17,0)
while True:
dt = datetime.now()
if dt.time() > start_time and last_dt.time <= start_time:
download_data()
last_dt = dt
数据库中已有的数据没必要重复下载,因此在执行每日数据更新时,应该选择当前数据库中每个合约的最新一条数据的时间作为今日更新下载的起始时间点(start_time)。这样在前一天已经批量下载完全市场所有合约的数据后,今天就只需要下载当日内产生的新数据即可。
增量更新的原理步骤如下:
1)通过database_manager(位于vnpy.trader.database模块中)来查询当前数据库中已有的合约数据统计情况:
data = database_manager.get_bar_data_statistics()
2)遍历data列表中各合约数据的统计信息,查询每个合约所对应的最新一条数据的时间戳:
for d in data:
newest_bar = database_manager.get_newest_bar_data(
d["symbol"], Exchange(d["exchange"]), Interval(d["interval"])
)
d["end"] = newest_bar.datetime
3)针对每个合约的数据更新,起始时间均为数据库中最新一条数据的时间戳,结束时间则是当前最新时间戳:
end_date = datetime.now()
for d in data:
symbol = d["symbol"]
exchange = d["exchange"]
req = HistoryRequest(
symbol=symbol,
exchange=Exchange(exchange),
start=d["end"],
interval=Interval.DAILY,
end=end_date,
)
load_data(req=req)
运行完成后,我们可以重复1、2两步来查询数据库中已有数据的最新一条记录时间戳,发现已经成功更新了今日所有合约的数据:
9月26日社区线下活动:CTA策略复杂交易算法实现
内容:
CTA策略复杂交易算法:
a.回调函数细节:
i. on_tick的K线合成
ii. on_order的推送时机
iii. on_trade的持仓更新
b. CTA交易中的算法状态机
c. 替换频繁cancel_all模式的停止单
d. 盘口连续挂单的交易执行逻辑
e. 实现一个策略内的TWAP算法
K线图表绘制回测买卖记录
a.成交的逐笔对冲统计模式
b. vnpy.chart模块的图层开发
c. 实现买卖记录连线图层
报名请扫描下方二维码:
发布于vn.py社区公众号【vnpy-community】
《30天解锁Python量化开发》课程已经更新过半(至25集),计划在9月底全部更新完毕,通过概念讲解和实践操作结合的方式,加上vn.py框架内部代码细节的梳理学习,来帮助你快速掌握Python量化开发能力,详情请戳。
原文作者:NY | 发布时间:2020-09-10
很多刚接触期货的投资者可能会疑惑,为什么商品期货在10:15到10:30之间,要有一个15分钟的交易休市,而国内的其他金融市场(证券股票、银行间债券、金融期货等)却都没有。
但其实,上午的这个休盘15分钟还真有历史渊源。话说在基于电脑的自动化交易系统还没有诞生的“远古”时期,交易所内还存在着一群场内交易员,一旦进入工作时间,需要持续保持精神高度集中,边盯着报价板边帮客户喊单交易。
上午开盘时间比较早,可能早盘行情的价格波动相对剧烈,也可能场内交易员们还没有准备充分,所以大家就需要一小段休息时间来调整一下,比如去下洗手间、吃个点心等。时至今日,国内各家期货交易所虽然都已经取消了场内交易员的模式,但商品期货上午15分钟休市的习惯却保留了下来。
回归到正题,vn.py中的K线合成器工具BarGenerator(位于vnpy.trader.utility模块下),从Tick合成K线的标准逻辑是对当前时间戳的分钟数字求余来进行切片的,具体代码如下:
if self.interval == Interval.MINUTE:
# x-minute bar
if not (bar.datetime.minute + 1) % self.window:
finished = True
要准确的把60分钟完整切片到等份,切片区间必须是以下这些能够整除60的数字:
2、3、5、6、10、15、20、30
但对于商品期货来说,由于上午的15分钟休盘,会导致某些情况下合成K线数据的不正确。
以20分钟为例,正常应该在每小时的19分、39分和59分的分钟线走完时合成20分钟K线。因为10:19的分钟数据不存在(休盘时间),不会触发切片合成,而要等到10:39分才会触发,导致这跟K线中会包含10:00-10:14和10:30-10:49两段共计35分钟的数据,和交易策略中预期的逻辑不符。
解决方案也很简单,只需对三大商品期货交易所的品种都进行一条特殊的逻辑处理,当收到上午10:14的分钟数据更新时我们立刻进行切分,这样下一根K线中的数据就一定从10点30分开始了:
if self.interval == Interval.MINUTE:
# x-minute bar
if not (bar.datetime.minute + 1) % self.window:
finished = True
elif (
bar.datetime.time() == time(10, 14)
and bar.exchange in [
Exchange.SHFE, Exchange.DCE, Exchange.CZCE
]
):
finished = True
完成修改后,我们可以通过使用1分钟的螺纹钢数据合成20分钟K线来测试下效果,如下图所示:
可以看到由于15分钟休盘时段的数据缺失,这个时刻的20分钟K线是从30分钟开始,该结果也和文华等其他期货软件中的K线数据一致。
由于国情区别,每个国家的开收盘时间也不一定相同,下文中的日K线合成方法仅针对国内期货市场。
和前文中的特殊分钟K线切分逻辑类似,日K线合成的关键也在于找到正确的当日行情的结束点,即当日收盘时间。
需要注意的是,目前国内期货品种中除了国债期货收盘时间是15:15,其他都是15:00整点收盘。所以我们只需对国债期货进行特殊处理,代码如下:
elif self.interval == Interval.DAILY:
day_end = time(14, 59)
if bar.exchange == Exchange.CFFEX and not bar.symbol.startswith("I"):
day_end = time(15, 14)
if bar.datetime.time() == day_end:
finished = True
self.interval_count = 0
通过交易所为CFFEX,以及合约代码不以I开头(股指期货为IF、IC和IH前缀)两个条件即可判断是否为国债期货。
这样我们就完成了从分钟K线到日K线的合成切分逻辑,使用上只需在创建BarGenerator对象时,将interval参数设置为Interval.DAILY,即可直接合成日线。
我们也用3个不同类型的期货品种来进行测试,效果如下图所示,都能正常合成:
对于螺纹钢等存在夜盘交易时段的期货品种,上述合成逻辑会将其夜盘时段的行情合并到下一日行情中切分(日K线的开盘价是夜盘开盘价),该逻辑也符合交易所对于每日交易时段的划分规则。
9月26日社区线下活动:CTA策略复杂交易算法实现
内容:
CTA策略复杂交易算法:
a.回调函数细节:
i. on_tick的K线合成
ii. on_order的推送时机
iii. on_trade的持仓更新
b. CTA交易中的算法状态机
c. 替换频繁cancel_all模式的停止单
d. 盘口连续挂单的交易执行逻辑
e. 实现一个策略内的TWAP算法
K线图表绘制回测买卖记录
a.成交的逐笔对冲统计模式
b. vnpy.chart模块的图层开发
c. 实现买卖记录连线图层
报名请扫描下方二维码:
发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2020-08-24
先来做一个简单的测试,看看你在学习和使用vn.py的过程中有没有碰到过以下的疑问:
如果你的回答是【没有】,那么可以直接退出不用继续往下看了,你已经很好的掌握了Python这门语言。
如果回答是【有】,那么这门新的课程《量化交易零基础入门系列 - 30天解锁Python量化开发》相信正好能帮到你。
这几年随着vn.py的用户数量不断增加,用户群体也从最初的专业机构量化交易员(老用户)逐渐下沉:有些刚从TB、文华之类的传统量化平台转过来,有些之前主要做的是主观交易刚开始接触量化,有些是对量化交易感兴趣的财经专业学生等等(新用户)。
而对于这些vn.py新用户来说,比起一上来就跟着老用户学CTA策略怎么写或者期权波动率交易怎么做,更重要的是先学好Python这门语言来解锁自己的量化开发能力。
否则就可能出现目前我们社区里一些新用户的情况:来回在论坛和QQ群里学习提问,折腾几个月却连一个完整能跑的Python程序都写不出来。工欲善其事必先利其器,这句老话在量化交易领域也同样适用。
整个课程完全针对Python零基础的初学者设计,目前已经计划的课时是50节。制作过程中也会根据大家的反馈增加额外的实战案例讲解课时,内容大纲如下:
课程目前已经上线,原价399元,还是老规矩前100名用户8折(319元)。直接在vn.py社区微信公众号(vnpy-community)里就能购买和观看(点击底部菜单栏的【进阶课程】进入)。推荐使用PC微信打开,视频分辨率更加清晰。
发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2020-06-29
差不多半年没做统计了,这次做的是【金融市场】相关的开源量化项目排名,主要内容就一张图:
总结下:
需要注意的是,无论Star数量还是排名位置,都不能绝对说明某个项目的实际价值和代码质量,更多还是作为一种参考信息,来了解当前的量化开源生态情况。
关于这个排名,有什么观点或者建议,欢迎留言讨论~
发布于vn.py社区公众号【vnpy-community】
《30天解锁Python量化开发》课程已经更新过半(至25集),计划在9月底全部更新完毕,通过概念讲解和实践操作结合的方式,加上vn.py框架内部代码细节的梳理学习,来帮助你快速掌握Python量化开发能力,详情请戳。
原文作者:用Python的交易员 | 发布时间:2020-09-20
本周末发布了vn.py的2.1.6版本,本次更新的内容主要是增加了PaperAccount本地模拟交易模块,以及优化了CtaBacktester模块的回测记录K线图表显示功能。
和之前一样,对于使用VN Studio的用户,启动VN Station后,直接点击界面右下角的【更新】按钮就能完成自动更新升级,对于没有安装的用户,请下载VNStudio-2.1.6,体验一键安装的量化交易Python发行版,下载链接:
https://download.vnpy.com/vnstudio-2.1.6.exe
该模块在vn.py社区是呼声已久,主要为了解决目前各类需要依赖服务端功能的仿真交易账户的问题,例如:
SimNow的CTP仿真环境,从2019年之后稳定性就逐渐变差,时不时会出现宕机连不上,或者和实盘行情出现较大的偏差;
截至目前,国内期权方面始终没有类似SimNow能提供实盘行情撮合的仿真环境:
对于上述这类问题,最简单的解决方案就是直接在交易客户端内部提供一套本地化的模拟交易环境,同时基于实盘行情的盘口数据进行委托撮合,也就是我们全新的PaperAccount本地模拟交易模块。
该模块在使用上非常傻瓜,首先在VN Station中加载PaperAccount模块,以及想要进行模拟交易的接口。
启动VN Trader后,连接登录交易接口。点击菜单栏的【帮助】->【合约查询】,在弹出的对话框中直接点击右上角的【查询】按钮,可以发现所有合约的交易接口列均显示为PAPER。
此时所有合约的交易委托和撤单请求均已被PaperAccount模块接管,不会再发往实盘服务器,可以放心大胆的进行各种交易测试了。
关闭合约信息对话框,回到主界面先订阅某一合约的行情(注意一定要订阅),再进行常规的下单和撤单操作。
这里列出PaperAccount模块在使用中的一些业务逻辑要点:
支持的委托类型(不支持的类型会被拒单):
委托撮合规则采用到价成交模式,以买入委托为例:
委托成交时不考虑盘口挂单量,一次性全部成交;
成交后,先推送委托状态更新OrderData,再推送成交信息TradeData,和实盘交易中的顺序一致;
委托成交后,模块会自动记录相应的持仓信息PositionData:
数据的持久化保存:
点击菜单栏的【功能】->【模拟交易】,在打开的对话框中可以对PaperAccount模块的功能进行若干配置:
PaperAccount模块同样也可以配合vn.py内部的所有其他策略应用模块一起使用(如CtaStrategy、SpreadTrading等),从而实现完全本地化的量化策略仿真交易测试。
CTA回测结果K线图增强
先来看一眼新的CtaBacktester回测K线图:
图表中各个组成部分的功能看最下方的说明就行。这次修改的版本,整体上参考了TB和文华的回测图表显示风格,尽管我个人不太喜欢这种配色,但是社区里大部分用户似乎都属于【看久后已经习惯了】,所以还是满足大部分人的需求吧。
买卖交易结果的配对,采用了v1.0版本中CTA回测盈亏统计曾经提供的【逐笔对冲】逻辑,可以比较精确的显示出策略每次开平仓交易的结果。
其他更新
接口方面:
文档方面:
9月26日社区线下活动:CTA策略复杂交易算法实现
内容:
CTA策略复杂交易算法:
a.回调函数细节:
i. on_tick的K线合成
ii. on_order的推送时机
iii. on_trade的持仓更新
b. CTA交易中的算法状态机
c. 替换频繁cancel_all模式的停止单
d. 盘口连续挂单的交易执行逻辑
e. 实现一个策略内的TWAP算法
K线图表绘制回测买卖记录
a.成交的逐笔对冲统计模式
b. vnpy.chart模块的图层开发
c. 实现买卖记录连线图层
报名请扫描下方二维码:
发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2020-08-14
最近两个月公司的商业项目比较忙,这次2.1.5版本拖了一个月的时间到这周才发布,本次更新的内容主要是增加了对金纳算法交易服务的支持。
和之前一样,对于使用VN Studio的用户,启动VN Station后,直接点击界面右下角的【更新】按钮就能完成自动更新升级,对于没有安装的用户,请下载VNStudio-2.1.5,体验一键安装的量化交易Python发行版,下载链接:
https://download.vnpy.com/vnstudio-2.1.5.exe
如果使用AlgoTrading模块还需要quickfix这个包(开源FIX引擎),直接通过pip安装的话需要机器上有Visual Studio编译器,否则会报错安装失败,所以推荐在cmd中运行下列命令来安装:
pip install https://pip.vnpy.com/colletion/quickfix-1.15.1-cp37-cp37m-win_amd64.whl
上图中的本文封面图是从金纳科技首页截取的,简洁明了的一句话说出了这家公司的主要产品:智能算法交易。
有些朋友可能搞不清楚【交易算法】和【量化策略】这两个词之间的区别,举一个简单的例子来说明:
所以,总结下来:
对于交易数量不大的用户,可以选择直接将买卖委托的操作嵌入到量化策略的信号生成中(比如中小型资金跑CTA策略)。但对于资金较大的量化机构,算法交易就是业务中的刚需了。
所以从很早的版本中,vn.py就提供了AlgoTrading模块用于满足这类中低频量化策略的算法交易需求。
对比vn.py中的标准执行算法实现,金纳科技提供的交易算法则加入了更多智能的功能,通过结合全息盘口和L2行情数据来优化算法中的买卖执行细节,从而进一步降低交易成本,或者大家也可以理解为在金纳的交易算法中嵌入了一部分高频量化策略的信号来优化买卖点。
金纳算法交易服务对外提供了FIX协议的接口,用户可以自行联系金纳科技购买账号(按年收费),或者也可以联系自己的券商看是否有采购(比如中泰、申万宏源)来申请免费账号。
vn.py使用了QuickFIX引擎来和金纳算法交易服务对接,针对算法母单的上行和算法子单的下行分别创建了对应的FIX APP,拿到金纳的账户信息后,只需在vn.py主界面的全局配置中添加相关的配置:
上图中以genus.为前缀的就是金纳相关的配置选项,parent前缀的是母单部分,child前缀的是子单部分,都需要填入对应的服务器地址、通讯端口、发送方名称、接收方名称信息。
修改完成后点击【确定】按钮退出,然后重启VN Trader打开AlgoTrading算法交易模块,检查日志监控中输出的内容:
同时能看到母单和子单FIX APP连接成功的信息,就说明已经成功完成了金纳算法交易服务的登录,此时就可以和之前调用vn.py内置算法一样,来使用金纳提供的交易算法了:
同时,由于FIX协议的标准化和通用性特点(金纳用的是FIX 4.2),如果大家有对接其他FIX协议交易系统(海外金融市场居多,比如各种MT4 Bridge)的需求,也可以参考vnpy.app.algo_trading.genus中的代码来进行开发。
随着vn.py对多标的类量化策略的支持(PortfolioStrategy模块),数据存储访问的需求(尤其是在读取跑回测上)变得越来越高,毕竟谁都不想浪费太多时间在等待数据从硬盘载入到内存上。
InfluxDB是一套专门针对时间序列数据存储设计的NoSQL类数据库,相比于SQL类数据库的MySQL能提供快得多的写入和读取速度(5-20倍),同时采用独立服务进程的模式运行,也能支持多进程的并发访问需求(SQLite的痛点)。
新的2.1.5版本中,也加入了对InfluxDB数据库的原生支持。首先前往InfluxDB的官网:https://portal.influxdata.com/downloads/ ,下载v1.8.1版本的InfluxDB。解压下载后的压缩包,编辑配置influxdb.conf文件,将max_body_size参数设为0。最后打开cmd,切换到当前目录,运行下述命令启动:
./influxd.exe –config ./influxdb.conf
然后同样打开VN Trader主界面的全局配置对话框,修改数据库相关的配置如下:
确认保存设置后重启VN Trader,即可开始享受InfluxDB带来的迅捷数据存储。最后需要提醒的是,运行influxd.exe的cmd需要保持运行,如果关闭则会导致InfluxDB退出,或者也可以用一些辅助工具将其注册为后台运行的Windows服务。
接口方面:
同样,有什么想问的问题或者分享的内容,欢迎在文章下方留言。本期我们将会随机抽取一位留言者赠送《vn.py全实战进阶 - 深入定价模型》课程的5折优惠券一张。
发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2020-05-30
更新日期:2020-5-30
针对许多初学者在刚开始使用vn.py时,容易遇到的各种常见问题,整理了这份FAQ文档,后续也会持续保持更新。
CTP期货接口
【Q】已经连接登录了CTP接口,但是在VN Trader主界面,左上角的编辑框中输入合约代码后,为何回车无法订阅行情?
【A】请检查合约代码是否输入正确,国内4家期货交易所的合约命名规则有所区别,vn.py内部全部采用官方命名,举例来说:
请注意以上命名中的英文字母大小写,以及年月的数字。
【Q】连接SimNow的CTP服务器,主界面左下角日志区域没有任何输出信息,或者出现4097错误?
【A】可能有以下几个原因,请按顺序排查:
RQData数据服务
【Q】RQData中的连续合约数据,提供88、888、99等多种类型,做CTA策略回测应该用哪个?
【A】首先是数据的区别,以股指IF合约为例:
具体细节可以参考米筐官方的RQData文档页面。
CTA策略交易
【Q】我自己开发的策略,应该放到什么目录?
【A】CtaStrategy和CtaBacktester两个模块,在启动时都会自动扫描加载VN Trader运行时目录(主界面窗口顶部标题栏的路径)下的strategies目录中的策略文件。
默认情况下,运行时目录是当前操作系统的用户目录,假设你的用户名为abc:
Windows系统
Linux/Mac系统
历史数据和数据库
【Q】vn.py支持哪些数据库?对于用户来说应该怎么选择?
【A】目前一共支持四套数据库:SQLite、MySQL、PostgreSQL以及MongoDB。其中SQLite、MySQL和PostgreSQL属于SQL类数据库,MongoDB属于NoSQL类数据库。
从各自的特点看:
关于数据库的具体配置方法,请参考官网文档。
【Q】手头已有从其他来源(淘宝购买、软件导出等)获取的CSV格式的K线数据,如何导入到vn.py中用于策略历史回测分析?
【A】注意:最新版本中已将之前的CsvLoader模块的功能,合并到了DataManager模块中。
操作流程如下:
2020年的第二期vn.py小班课还剩最后一个名额!
两天共计10小时的密集提高课程
8套高端CTA策略源代码分享
动态风险仓位管理模型
策略内嵌复杂算法交易
详情请戳 2020年第二期小班课:CTA策略开发
发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2020-06-12
提示:如果只对封面3D图实现感兴趣的同学,可以直接跳转到文末的第三段内容~
对于CTA策略的回测研究,vn.py提供了两套工具:CtaBacktester图形界面
和CtaStrategy回测引擎。
其中CtaBacktester采用了基于PyQt的图形界面,适合刚开始上手的初学者,十分简洁易用,点点鼠标就能快速完成【数据下载入库】和【历史数据回测】功能:
CtaStrategy回测引擎作为CtaBacktester图形界面背后的计算引擎,也可以在Jupyter Notebook中直接使用。在CLI命令交互的模式下,用户操作的自由度大大提升,可以实现更加复杂的分析脚本,如【策略组合回测】、【主力切换回测】、【滚动窗口优化】等等:
尽管功能更加强大,但截止目前最新版本(2.1.3.1),回测引擎基于Matplotlib和Seaborn实现的绘图展示却一直备受大家吐槽:
整体图表极端单调的白蓝配色,第三张每日盈亏子图一旦数据太多就会无法显示,底部X轴坐标密集成了黑色长条,除了能右键另存为图片外几乎毫无交互功能......妥妥体现出了我们vn.py钢铁直男团队的审美。
长期在社区论坛被这么吐槽下去也不是个事,好在Python大数据分析的生态比起六年前vn.py刚起步的时候早就今非昔比,做了点功课后快速选定了新的方向:Plotly。
仅看能绘制的图表类型,Plotly对比Matplotlib来说可能优势未必那么明显(仅限于vn.py应用范围),但在绘图性能和交互式功能方面,Plotly基于Javascript在浏览器中动态绘制的图表,就吊打Matplotlib的静态图片几条大街了。
下面我们就试着用Plotly在Jupyter Notebook中绘制出上面的策略统计图表,首先是安装plotly.py,打开cmd直接运行:
pip install plotly
然后启动Jupyter Notebook来运行历史数据回测,并获取到回测的统计结果数据,当然在运行前请先确保已经准备好了历史数据(没有的同学可以申请RQData试用下载:点击链接),代码如下:
# 加载相关模块
from datetime import datetime
from vnpy.app.cta_strategy.backtesting import BacktestingEngine, OptimizationSetting
from vnpy.app.cta_strategy.strategies.atr_rsi_strategy import AtrRsiStrateg
# 运行策略回测
engine = BacktestingEngine()
engine.set_parameters(
vt_symbol="IF888.CFFEX",
interval="1m",
start=datetime(2010, 1, 1),
end=datetime(2020, 7, 30),
rate=0.3/10000,
slippage=0.2,
size=300,
pricetick=0.2,
capital=1_000_000,
)
engine.add_strategy(AtrRsiStrategy, {})
# 统计分析结果
engine.load_data()
engine.run_backtesting()
df = engine.calculate_result()
engine.calculate_statistics()
到这里,我们已经准备好了绘图所需的全部数据,保存在一个pandas.DataFrame数据结构df中,下一步就可以开始尝试用Plotly来画图了,首先加载Plotly相关模块:
import plotly.graph_objects as go
from plotly.subplots import make_subplots
其中的go模块包含了Plotly中的绘图组件(折线图、柱状图、面积图、散点图等等),而make_subplots函数则用于创建带子图的绘图区域。真正的绘图代码则十分简单,只有短短几行:
# 创建一个4行、1列的带子图绘图区域,并分别给子图加上标题
fig = make_subplots(rows=4, cols=1, subplot_titles=["Balance", "Drawdown", "Daily Pnl", "Pnl Distribution"], vertical_spacing=0.06)
# 第一张:账户净值子图,用折线图来绘制
fig.add_trace(go.Line(x=df.index, y=df["balance"], name="Balance"), row=1, col=1)
# 第二张:最大回撤子图,用面积图来绘制
fig.add_trace(go.Scatter(x=df.index, y=df["drawdown"], fillcolor="red", fill='tozeroy', line={"width": 0.5, "color": "red"}, name="Drawdown"), row=2, col=1)
# 第三张:每日盈亏子图,用柱状图来绘制
fig.add_trace(go.Bar(y=df["net_pnl"], name="Daily Pnl"), row=3, col=1)
# 第四张:盈亏分布子图,用直方图来绘制
fig.add_trace(go.Histogram(x=df["net_pnl"], nbinsx=100, name="Days"), row=4, col=1)
# 把图表放大些,默认小了点
fig.update_layout(height=1000, width=1000)
# 将绘制完的图表,正式显示出来
fig.show()
不必经历Matplotlib绘图的漫长等待,几乎一瞬间,结果就显示了出来:
就算是钢铁直男也得承认,比起之前的Matplotlib图表,这个真的好看太多。
首先作为能偷懒一定要偷懒的优秀开发者,我们是绝对不会没事跑去设置颜色的,四张子图中除了面积图必须设底色不然不显示外,另外三张子图的显示都是默认自动选择的颜色和效果。
另外还有个优点在上述截图上没法体现,将鼠标移动到子图上任意位置时,会自动显示当前X轴位置对应的Y轴数据细节,连日期时间戳也完全清楚,再也不用把df给print出来后一行行去翻数据了。
有了这么个方便的图表工具后,接下来自然要更进一步发挥作用。参数优化是所有量化策略开发过程中极为重要的步骤,通过调整参数的数值实现策略模型对历史数据的最优匹配,来更好的把握未来实盘交易中的行情。
关于参数优化的数学原理、详细步骤、注意事项,足足可以写上一个系列的文章来讲解,在这里我们不去做额外的深入,先用这段代码快速跑出一个优化结果:
# 创建优化配置
setting = OptimizationSetting()
# 选择夏普比率作为目标函数
setting.set_target("sharpe_ratio")
# 选择优化rsi_length和atr_length两个参数
setting.add_parameter("rsi_length", 2, 11, 1)
setting.add_parameter("atr_length", 10, 30, 2)
# 通过多进程穷举算法来执行优化
result = engine.run_optimization(setting)
采用文本形式输出的结果如下:
或者在CtaBacktester组件中通过图形界面执行优化,也可以得到类似的显示:
所有的参数组合,都已经根据目标函数的结果从高到低排列。比起单纯选择排在第一位的参数组合,有经验的同学会更加倾向于选择位于【参数平原】的结果,即其附近的结果都相对更好的组合。
尽管数字都已经显示出来了,但是对于我们人脑来说想要从其中找到这个平原,难度还是太大了点。更直观的方式还是通过图表来判断,2个自变量,1个因变量,正好可以组合为3D曲面。
首先是将上一步中保存下来的优化结果result列表中的数据,调整其格式生成X、Y、Z三条坐标轴上的数据点:
# 直接取出X、Y轴
x = setting.params["atr_length"]
y = setting.params["rsi_length"]
# 通过映射的方式取出Z轴
z_dict = {}
for param_str, target, statistics in result:
param = eval(param_str)
z_dict[(param["atr_length"], param["rsi_length"])] = target
z = []
for x_value in x:
z_buf = []
for y_value in y:
z_value = z_dict[(x_value, y_value)]
z_buf.append(z_value)
z.append(z_buf)
准备好了数据,又到了用上Plotly的时刻,简单的三行代码(update_layout中因为参数过长做了换行处理):
fig = go.Figure(data=[go.Surface(z=z, x=x, y=y)])
fig.update_layout(
title='优化结果', autosize=False,
width=600, height=600,
scene={
"xaxis": {"title": "atr_length"},
"yaxis": {"title": "rsi_length"},
"zaxis": {"title": setting.target_name},
},
margin={"l": 65, "r": 50, "b": 65, "t": 90}
)
fig.show()
同样几乎瞬间,就在Jupyter中看到了显示出来的参数优化结果的3D曲面图:
其中平面的X、Y轴分别显示的是两个参数rsi_length和atr_length,而垂直的Y轴则显示的优化目标sharpe_ratio的数值,三者共同构成了3D曲面图,同时【参数平原】也变得一目了然,真的就是选择曲面上“最高的平原区域”。
尽管截图中没法体现出来,该3D图表还可以非常方便的在三条轴线上进行360度旋转,以及拉近和拉远操作,并且当鼠标移动到曲面上时也可以直接看到该位置对应的具体数据内容,如下图所示:
也不多说啥了,接下来的v2.1.4版本让大家直接全都用上!
感谢vn.py社区用户的支持。
【vn.py全实战进阶 - CTA策略】课程销量已经达到778份。
销量超过800后将会进行一次涨价,涨价后的价格为499(目前是399)。
最后22份名额,感兴趣的朋友请抓紧时间吧。
购买请直接扫描下方二维码:
发布于vn.py社区公众号【vnpy-community】
原文作者:木头哥 | 发布时间:2020-6-12
作为Python开发的开源项目,vn.py本身具有非常好的跨平台通用性,毕竟Python几乎可以在所有主流操作系统上运行。但对于Linux系统,官方团队只提供了对Ubuntu 18.04版本的支持(主要就是安装脚本)。
本人一直用的是CentOS的服务器,折腾了几天,终于在上面把vnpy跑起来了。没有记录折腾的细节,只是记录了下正常的操作步骤,欢迎大家一起交流。
以下内容全部基于CentOS 7.6版本,首先准备好一个全新安装的系统,然后跟着一步步操作即可。
# 在anaconda.com官网下载安装包,这里需要下载linux版本的x86_64位的包
bash Anaconda3-2019.07-Linux-x86_64.sh
# 如果不修改安装路径的话,按照默认设置即可。
# 安装过anaconda后,需要将/root/.bashrc中的关于anaconda的部分屏蔽掉,不然后面vncserver无法正常启动。
yum groups install "X Window System" -y
yum install epel-release -y
yum groups install "MATE Desktop" -y
systemctl set-default graphical.target
yum install tigervnc-server -y
# 替换User为root,增加显示分辨率参数设置
sed -r -i "s/^(ExecStart.*)<USER>(.*%i)/\1root\2 -geometry 1920x1200 -depth 16/" /lib/systemd/system/vncserver@.service
sed -r -i "s/^(PIDFile.*)home\/<USER>(.*pid)/\1root\2/" /lib/systemd/system/vncserver@.service
mv /lib/systemd/system/vncserver@.service /lib/systemd/system/vncserver@:1.service
systemctl daemon-reload
vncpasswd
systemctl start vncserver@:1.service
systemctl enable vncserver@:1.service
# 屏蔽默认桌面,启动mate桌面
sed -r -i "s@^/etc/X11/xinit/xinitrc$@# &@" /root/.vnc/xstartup
echo "/usr/bin/mate-session &" >> /root/.vnc/xstartup
# 其它操作
# 禁用selinux
sed -r -i "s/^(SELINUX=).*/\1disabled/" /etc/selinux/config
# 关闭防火墙
systemctl stop firewalld.service
systemctl disable firewalld.service
reboot
# 如果是云服务器,需要确保开放了TCP 5901端口
rpm --import https://packages.microsoft.com/keys/microsoft.asc
sh -c 'echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/vscode.repo'
yum check-update
yum install code -y
注意GCC必须要使用9.1.0以上的版本,否则在编译vnpy的时候,会报-std=c++17相关的错误,另外GCC编译的时间很长,估计得几个小时。
yum install gcc gcc-c++ bzip2 m4 gmp-devel.x86_64 -y
wget https://mirrors.ustc.edu.cn/gnu/gcc/gcc-9.1.0/gcc-9.1.0.tar.gz
tar xvf gcc-9.1.0.tar.gz
cd gcc-9.1.0/
./contrib/download_prerequisites
cd gmp;mkdir temp;cd temp
../configure --prefix=/usr/local/gmp-6.1.0
make && make install
cd ../../mpfr;mkdir temp;cd temp
../configure --prefix=/usr/local/mpfr-3.1.4 --with-gmp=/usr/local/gmp-6.1.0
make && make install
cd ../../mpc;mkdir temp;cd temp
../configure --prefix=/usr/local/mpc-1.0.3 --with-gmp=/usr/local/gmp-6.1.0 --with-mpfr=/usr/local/mpfr-3.1.4
make && make install
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/mpc-1.0.3/lib:/usr/local/gmp-6.1.0/lib:/usr/local/mpfr-3.1.4/lib
cd ../..;mkdir temp;cd temp
../configure --disable-multilib --enable-languages=c,c++ --with-gmp=/usr/local/gmp-6.1.0 --with-mpfr=/usr/local/mpfr-3.1.4 --with-mpc=/usr/local/mpc-1.0.3
make -j4 && make install
我的服务器是4核的,就在make后面加了-j4,大家可根据自己的情况调整,缩短编译时间。
最后终于可以安装vn.py了,在此过程中会自动编译Linux上支持的交易接口,如CTP/OES等。
# 切换到python环境
. ~/miniconda3/bin/activate
yum install postgresql-devel* libxkbcommon-x11 -y
# 下载vnpy的最新源码包并解压后执行
cd vnpy
bash install.sh
发布于vn.py社区公众号【vnpy-community】
原文作者:李怡然 | 发布时间:2020-05-11
如何将本地CSV文件格式的历史数据导入到数据库中,是vn.py社区论坛上提问最多的问题之一。本文的主要目的是帮助初学者解决数据入库问题,以便可以快速开始量化策略的开发研究。
本文的内容主要分为三大部分:
在正文开始之前,还需要提醒大家:将数据导入数据库之前,务必要确保这些数据已经是被清洗过的干净的数据。如果将没有被清洗过质量差的数据,直接入库进行回测,可能会导致各种问题,影响策略开发的效率。因此,建议大家使用高质量的数据源。
vn.py 中默认使用 SQLite 数据库。因此,如果需要使用 MongoDB 数据库,则需要修改 vn.py 的全局配置。具体流程如下:
下图是我自己设置的数据库配置信息:
上图中的两个 "mongodb" 可能让人会有些困扰。实际上第一个"mongodb"是告诉 vn.py 我们使用的数据库类型是 MongoDB 数据库而不是默认的 SQLite 数据库。第二个 "mongodb" 则是告诉 vn.py 回测所需要的数据储存在 MongoDB 数据库中一个叫做 "mongodb" 的 database 中。这样说可能有些绕口,请看下图:
上图是 MongoDB 数据库的官方图形界面客户端MongoDB Compass。我们可以清楚的看到在该 MongoDB 数据库中一共有四个 database, 分别是 admin,config,local, mongodb。从2.0开始,vn.py采用ORM/ODM的方式来管理历史数据,因此所有的K线数据都被保存在db_bar_data这一集合中,所有Tick数据都被保存在db_tick_data这一集合中。
在配置好 MongoDB 数据库后,我们可以正式开始讨论数据入库操作,vn.py 提供了很多工具使数据入库这个过程变得简单快捷,以下是是数据入库的基本流程:
以上三步操作中的难点(或者说复杂点)集中在第二步,即如何将将本地CSV文件中的数据格式转换成vn.py 定义的 TickData 或 BarData,大部分情况下只需对数据的时间戳进行转化处理。另外,如果数据本身的质量不高,比如数据的整个时间戳格式前后不一致,那需要先对数据的时间戳格式进行统一。
在了解了数据入库的基本流程之后,我们来实现一次数据入库的过程。首先,来看一看我们要入库的数据:
从上图可以看出,C 列储存的是表示时间的数据且 C2 和 C3 的时间间隔是1分钟。所以,要入库的数据是1分钟的 Bar (K线)数据类型。下面我们进行第二步:将需要入库的数据转化成 vn.py 定义的 BarData。
首先,我们先来认识一下 vn.py 中的BarData:
从上图中可以看出,BarData 一共有11个属性。其中,BarData.vt_symbol 会在 BarData 实例化的时候自动生成。另外,需要指出的是BarData.exchange 和 BarData.inteval 的数据类型分别是 vn.py 中定义好的枚举常量 Exchange 和 Inteval 而 BarData.datetime 则是 Python 标准库 datetime 中的 datetime 数据类型。
Exchange 枚举值的定义:
Interval 枚举值的定义:
在认识了vn.py 中的 BarData 之后,我们开始着手将需要入库的数据转化成 BarData类型数据。再来重温一下,需要入库数据的格式:
通过和上文 BarData 的数据结构对比,我们有以下几个发现:
基于上面的发现,很自然的,我们需要进行如下的操作:
一般情况下,使用Python 的 pandas 库可以方便的完成上面的操作。如果数据的质量较差,比如数据的分隔符设置存在问题,会使得pd.read_csv函数没办法正确的读取.csv文件。这时则需要使用Python的 csv库。本文的数据入库过程统一使用 pandas 来完成。具体操作,如下:
from vnpy.trader.constant import (Exchange, Interval)
import pandas as pd
# 读取需要入库的csv文件,该文件是用gbk编码
imported_data = pd.read_csv('需要入库的数据的绝对路径',encoding='gbk')
# 将csv文件中 `市场代码`的 SC 替换成 Exchange.SHFE SHFE
imported_data['市场代码'] = Exchange.SHFE
# 增加一列数据 `inteval`,且该列数据的所有值都是 Interval.MINUTE
imported_data['interval'] = Interval.MINUTE
接下来,我们还需要对每列数据的数据类型进行修改,确保和 BarData 中各个属性的数据类型一致。BarData中属性的数据类型可以分为三大类:float 类, datetime 类 和 自定义枚举类 (Interval 和 Exchange)。因为,上面已经修改过了Interval 和 Exchange,下面只需要修改 float 和 datetime 类。
修改 float类代码:
# 明确需要是float数据类型的列
float_columns = ['开', '高', '低', '收', '成交量', '持仓量']
for col in float_columns:
imported_data[col] = imported_data[col].astype('float')
修改 datatime类代码:
# 明确时间戳的格式
# %Y/%m/%d %H:%M:%S 代表着你的csv数据中的时间戳必须是 2020/05/01 08:32:30 格式
datetime_format = '%Y%m%d %H:%M:%S'
imported_data['时间'] = pd.to_datetime(imported_data['时间'],format=datetime_format)
下一步,我们还需要对列名进行修改:
# 因为没有用到 成交额 这一列的数据,所以该列列名不变
imported_data.columns=['exchange','symbol','datetime','open','high','low','close','volume','成交额','open_interest','interval']
另外,因为该csv文件储存的是ag的主力连续数据,即多张ag合约的拼接。因此,symbol列中有多个不同到期日的ag合约代码,这里需要将合约代码统一为ag88:
imported_data['symbol'] ='ag88'
最后,我们使用 vn.py 封装好的 database_manager.save_bar_data 将数据入库:
# 导入 database_manager 模块
from vnpy.trader.database import database_manager
from vnpy.trader.object import (BarData,TickData)
# 封装函数
def move_df_to_mongodb(imported_data:pd.DataFrame,collection_name:str):
bars = []
start = None
count = 0
for row in imported_data.itertuples():
bar = BarData(
symbol=row.symbol,
exchange=row.exchange,
datetime=row.datetime,
interval=row.interval,
volume=row.volume,
open_price=row.open,
high_price=row.high,
low_price=row.low,
close_price=row.close,
open_interest=row.open_interest,
gateway_name="DB",
)
bars.append(bar)
# do some statistics
count += 1
if not start:
start = bar.datetime
end = bar.datetime
# insert into database
database_manager.save_bar_data(bars)
print(f"Insert Bar: {count} from {start} - {end}")
如果想要将数据储存储存在 SQLite 数据库中也很简单,只需要两步就可以完成。
首先创建一个sqlite数据库连接对象:
from vnpy.trader.database.initialize import init_sql
from vnpy.trader.database.database import Driver
settings={
"database": "database.db",
"host": "localhost",
"port": 3306,
"user": "root",
"password": "",
"authentication_source": "admin"
}
sqlite_manager = init_sql(driver=Driver.SQLITE, settings=settings)
然后使用sqlite数据库连接对象将数据入库:
# 替换函数 move_df_to_mongodb 的倒数第二行
sqlite_manager.save_bar_data(bars)
如果在进行Sqlite数据入库的时候,出现peewee.InterfaceError: Error binding parameter 2 - probably unsupported type错误,解决方法如下:
详细的Debug过程记录在sqlite数据入库Debug. 将该文件夹内的内容下载到本地同一个位置,运行Jupyter Notebook就可以复现整个过程.
本文尝试从数据库配置,数据入库基本流程,数据入库具体实现,三部分来帮助vn.py新用户解决编写python脚本实现数据入库这个难点。借助vn.py的database_manager模块,用户基本上可以无缝切换SQLite,MongoDB等vn.py支持的数据库来读取和存入数据。希望这篇文章能帮助大家快速进入量化策略的研究和开发。
from vnpy.trader.constant import (Exchange, Interval)
import pandas as pd
from vnpy.trader.database import database_manager
from vnpy.trader.object import (BarData,TickData)
# 封装函数
def move_df_to_mongodb(imported_data:pd.DataFrame,collection_name:str):
bars = []
start = None
count = 0
for row in imported_data.itertuples():
bar = BarData(
symbol=row.symbol,
exchange=row.exchange,
datetime=row.datetime,
interval=row.interval,
volume=row.volume,
open_price=row.open,
high_price=row.high,
low_price=row.low,
close_price=row.close,
open_interest=row.open_interest,
gateway_name="DB",
)
bars.append(bar)
# do some statistics
count += 1
if not start:
start = bar.datetime
end = bar.datetime
# insert into database
database_manager.save_bar_data(bars, collection_name)
print(f'Insert Bar: {count} from {start} - {end}')
if __name__ == "__main__":
# 读取需要入库的csv文件,该文件是用gbk编码
imported_data = pd.read_csv('D:/1分钟数据压缩包/FutAC_Min1_Std_2016/ag主力连续.csv',encoding='gbk')
# 将csv文件中 `市场代码`的 SC 替换成 Exchange.SHFE SHFE
imported_data['市场代码'] = Exchange.SHFE
# 增加一列数据 `inteval`,且该列数据的所有值都是 Interval.MINUTE
imported_data['interval'] = Interval.MINUTE
# 明确需要是float数据类型的列
float_columns = ['开', '高', '低', '收', '成交量', '持仓量']
for col in float_columns:
imported_data[col] = imported_data[col].astype('float')
# 明确时间戳的格式
# %Y/%m/%d %H:%M:%S 代表着你的csv数据中的时间戳必须是 2020/05/01 08:32:30 格式
datetime_format = '%Y%m%d %H:%M:%S'
imported_data['时间'] = pd.to_datetime(imported_data['时间'],format=datetime_format)
# 因为没有用到 成交额 这一列的数据,所以该列列名不变
imported_data.columns = ['exchange','symbol','datetime','open','high','low','close','volume','成交额','open_interest','interval']
imported_data['symbol'] ='ag88'
move_df_to_mongodb(imported_data,'ag88')
环境搭建
a. Python语言环境
b. 安装vn.py框架
c. 适合量化的IDE
d. 交易对接:以IB TWS为例
策略回测
a. 数据解决方案
b. 开发CTA策略
c. 历史数据回测
d. 策略参数优化
自动交易
a. CTA实盘模块
b. 策略生命周期
c. 运维注意事项
QA
发布于vn.py社区公众号【vnpy-community】
原文作者:上弦之月 | 发布时间:2020-03-20
随着量化交易在国内金融市场越来越普及,CTA策略之间的竞争也变得越发激烈,除了体现在策略的核心交易信号方面外,也同样体现在策略的实盘委托执行中。
大部分vn.py官方提供的CTA策略样例中,在K线回调函数on_bar内采用的都是两步操作:
这种简单粗暴的写法,更多是出于简化策略执行中的状态机控制,帮助vn.py初学者的降低学习难度,但由于较多的重复操作,在实盘中的运行效果未必能达到最佳。
好在策略模板CtaTemplate中委托函数(buy/sell/short/cover)可以直接返回委托号信息,以及on_order/on_trade回调函数会推送委托和成交状态变化,结合少量的底层代码改造,我们就可以实现更加精细的Tick级别委托挂撤单管理,让策略的核心交易信号和委托执行算法更加有机地结合起来。
找到vn.py源代码所在的路径,使用VN Studio的情况下,应该位于C:\vnstudio\Lib\site-packages\vnpy
,进入到目录vnpy\trader
下找到object.py
文件
首先需要对OrderData
类进行扩展,主要表现在:
类的初始化除了time属性外,还增加了date和cancel_time属性
在__post_init__
函数,增加了未成交量untraded和委托的具体日期时间datetime。
其中应该注意的是,由于每个行情API接口推送的时间格式不太相同,所以基于:
"%Y-%m-%d"
还是"%Y%m%d"
;
要用到4种不同的处理方法得到datetime。
@dataclass
class OrderData(BaseData):
"""
Order data contains information for tracking lastest status
of a specific order.
"""
symbol: str
exchange: Exchange
orderid: str
type: OrderType = OrderType.LIMIT
direction: Direction = ""
offset: Offset = Offset.NONE
price: float = 0
volume: float = 0
traded: float = 0
status: Status = Status.SUBMITTING
date: str = ""
time: str = ""
cancel_time: str = ""
def __post_init__(self):
""""""
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
self.vt_orderid = f"{self.gateway_name}.{self.orderid}"
self.untraded = self.volume - self.traded
# With millisecond
if self.date and "." in self.time:
if "-"in self.date:
self.datetime = datetime.strptime(" ".join([self.date, self.time]), "%Y-%m-%d %H:%M:%S.%f")
else:
self.datetime = datetime.strptime(" ".join([self.date, self.time]), "%Y%m%d %H:%M:%S.%f")
# Without millisecond
elif self.date:
if "-" in self.date:
self.datetime = datetime.strptime(" ".join([self.date, self.time]), "%Y-%m-%d %H:%M:%S")
else:
self.datetime = datetime.strptime(" ".join([self.date, self.time]), "%Y%m%d %H:%M:%S")
def is_active(self):
"""
Check if the order is active.
"""
if self.status in ACTIVE_STATUSES:
return True
else:
return False
def create_cancel_request(self):
"""
Create cancel request object from order.
"""
req = CancelRequest(
orderid=self.orderid, symbol=self.symbol, exchange=self.exchange
)
return req
进入目录vnpy\trader
打开converter.py
文件,这一次我们对PositionHolding类进行修改,主要改动如下:
__init__
类的初始化,新加入long_pnl,long_price,short_pnl,short_price这4个属性。即增加了持仓盈亏以及开仓均价;update_position
函数新增缓存持仓盈亏和开仓均价;update_order
函数首先修改了活动委托的定义,把【委托提交中】剔除,即在update_order函数只处理未成交或者部分成交状态的委托。
class PositionHolding:
""""""
def __init__(self, contract: ContractData):
""""""
self.vt_symbol = contract.vt_symbol
self.exchange = contract.exchange
self.active_orders = {}
self.long_pos = 0
self.long_pnl = 0
self.long_price = 0
self.long_yd = 0
self.long_td = 0
self.short_pos = 0
self.short_pnl = 0
self.short_price = 0
self.short_yd = 0
self.short_td = 0
self.long_pos_frozen = 0
self.long_yd_frozen = 0
self.long_td_frozen = 0
self.short_pos_frozen = 0
self.short_yd_frozen = 0
self.short_td_frozen = 0
def update_position(self, position: PositionData):
""""""
if position.direction == Direction.LONG:
self.long_pos = position.volume
self.long_pnl = position.pnl
self.long_price = position.price
self.long_yd = position.yd_volume
self.long_td = self.long_pos - self.long_yd
self.long_pos_frozen = position.frozen
else:
self.short_pos = position.volume
self.short_pnl = position.pnl
self.short_price = position.price
self.short_yd = position.yd_volume
self.short_td = self.short_pos - self.short_yd
self.short_pos_frozen = position.frozen
def update_order(self, order: OrderData):
""""""
#active_orders只记录未成交和部分成交委托单
if order.status in [Status.NOTTRADED, Status.PARTTRADED]:
self.active_orders[order.vt_orderid] = order
else:
if order.vt_orderid in self.active_orders:
self.active_orders.pop(order.vt_orderid)
self.calculate_frozen()
......
进入目录vnpy\app\cta_strategy
打开engine.py
文件,这一次我们要新增一个函数get_position_detail
。该函数功能就是获取我们修改后的PositionHolding对象,从而知道更加详细的持仓信息,如开仓均价,持仓盈亏等:
from collections import defaultdict,OrderedDict
......
def get_position_detail(self, vt_symbol):
"""
查询long_pos,short_pos(持仓),long_pnl,short_pnl(盈亏),active_order(未成交字典)
收到PositionHolding类数据
"""
try:
return self.offset_converter.get_position_holding(vt_symbol)
except:
self.write_log(f"当前获取持仓信息为:{self.offset_converter.get_position_holding(vt_symbol)},等待获取持仓信息")
position_detail = OrderedDict()
position_detail.active_orders = {}
position_detail.long_pos = 0
position_detail.long_pnl = 0
position_detail.long_yd = 0
position_detail.long_td = 0
position_detail.long_pos_frozen = 0
position_detail.long_price = 0
position_detail.short_pos = 0
position_detail.short_pnl = 0
position_detail.short_yd = 0
position_detail.short_td = 0
position_detail.short_price = 0
position_detail.short_pos_frozen = 0
return position_detail
然后,为了让交易策略能够直接从引擎调用get_position_detail
函数,对CTA策略模板也得增加一个调用函数。在同一目录找到template.py
文件,开打后在CtaTemplate类中加入以下代码即可:
def get_position_detail(self, vt_symbol: str):
""""""
return self.cta_engine.get_position_detail(vt_symbol)
底层的功能都添加完毕了,那么现在轮到对具体交易策略逻辑进行改动,从而实现基于实时tick行情的追单和撤单。
添加策略运行时变量
包括委托状态的触发控制器、具体委托量以及拆单间隔:
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
""""""
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
#状态控制初始化
self.chase_long_trigger = False
self.chase_sell_trigger = False
self.chase_short_trigger = False
self.chase_cover_trigger = False
self.long_trade_volume = 0
self.short_trade_volume = 0
self.sell_trade_volume = 0
self.cover_trade_volume = 0
self.chase_interval = 10 #拆单间隔:秒
......
需要在on_tick函数增加的对委托挂撤单管理逻辑如下:
调用cta策略模板新增的get_position_detail函数,通过engine获取活动委托字典active_orders。注意的是,该字典只缓存未成交或者部分成交的委托,其中key是字符串格式的vt_orderid,value对应OrderData对象;
engine取到的活动委托为空,表示委托已完成,即order_finished=True;否则,表示委托还未完成,即order_finished=False,需要进行精细度管理;
精细管理的第一步是先处理最老的活动委托,先获取委托号vt_orderid和OrderData对象,然后对OrderData对象的开平仓属性(即offst)判断是进行开仓追单还是平仓追单:
a. 开仓追单情况下,先得到未成交量order.untraded,若当前委托超过10秒还未成交(chase_interval = 10),并且没有触发追单(chase_long_trigger = False),先把该委托撤销掉,然后把触发追单器启动;
b. 平仓追单情况下,同样先得到未成交量,若委托超时还未成交并且平仓触发器没有启动,先撤单,然后启动平仓触发器;
当所有未成交委托处理完毕后,活动委托字典将清空,此时order_finished状态从False变成True,用最新的买卖一档行情,该追单开仓的追单开仓,该追单平仓的赶紧平仓,每次操作后恢复委托触发器的初始状态:
def on_tick(self, tick: TickData):
""""""
active_orders = self.get_position_detail(tick.vt_symbol).active_orders
if active_orders:
#委托完成状态
order_finished = False
vt_orderid = list(active_orders.keys())[0] #委托单vt_orderid
order = list(active_orders.values())[0] #委托单字典
#开仓追单,部分交易没有平仓指令(Offset.NONE)
if order.offset in (Offset.NONE, Offset.OPEN):
if order.direction == Direction.LONG:
self.long_trade_volume = order.untraded
if (tick.datetime - order.datetime).seconds > self.chase_interval and self.long_trade_volume > 0 and (not self.chase_long_trigger) and vt_orderid:
#撤销之前发出的未成交订单
self.cancel_order(vt_orderid)
self.chase_long_trigger = True
elif order.direction == Direction.SHORT:
self.short_trade_volume = order.untraded
if (tick.datetime - order.datetime).seconds > self.chase_interval and self.short_trade_volume > 0 and (not self.chase_short_trigger) and vt_orderid:
self.cancel_order(vt_orderid)
self.chase_short_trigger = True
#平仓追单
elif order.offset in (Offset.CLOSE, Offset.CLOSETODAY):
if order.direction == Direction.SHORT:
self.sell_trade_volume = order.untraded
if (tick.datetime - order.datetime).seconds > self.chase_interval and self.sell_trade_volume > 0 and (not self.chase_sell_trigger) and vt_orderid:
self.cancel_order(vt_orderid)
self.chase_sell_trigger = True
if order.direction == Direction.LONG:
self.cover_trade_volume = order.untraded
if (tick.datetime - order.datetime).seconds > self.chase_interval and self.cover_trade_volume > 0 and (not self.chase_cover_trigger) and vt_orderid:
self.cancel_order(vt_orderid)
self.chase_cover_trigger = True
else:
order_finished = True
if self.chase_long_trigger and order_finished:
self.buy(tick.ask_price_1, self.long_trade_volume)
self.chase_long_trigger = False
elif self.chase_short_trigger and order_finished:
self.short(tick.bid_price_1, self.short_trade_volume)
self.chase_short_trigger = False
elif self.chase_sell_trigger and order_finished:
self.sell(tick.bid_price_1, self.sell_trade_volume)
self.chase_sell_trigger = False
elif self.chase_cover_trigger and order_finished:
self.cover(tick.ask_price_1, self.cover_trade_volume)
self.chase_cover_trigger = False
最后需要注意的是,Tick级精细挂撤单的管理逻辑,无法通过K线来进行回测检验,因此通过仿真交易(比如期货基于SimNow)进行充分的测试就是重中之重了。
《vn.py全实战进阶 - 期权零基础入门》课程已经更新过半!内容专门面向从未接触过期权交易的新手,共计30节课程带你一步步掌握期权的基础知识、了解合约特征和品种细节、学习方向交易和套利组合等各种常用期权交易策略,详细内容请戳新课上线:《期权零基础入门》。
发布于vn.py社区公众号【vnpy-community】
原文作者:KeKe | 发布时间:2020-03-08
在对CTA策略的历史数据回测研究中,除了直接观察资金曲线的形状外,更多会基于各类围绕资金曲线的统计指标,来对策略的业绩表现进行评估,例如:
其中历史最大回撤和最长回撤时间两个统计指标,描述的是在最坏情况下(即连续亏损时)策略的损失程度。但在任何给定的样本数据上跑完回测,得到的回撤指标都分别只有一个点的估计值(最坏情况),同时其对策略参数变化的敏感性非常高。
因此在对策略的整体风险评估上,如果仅仅使用以上两个回撤指标,可能使得我们分关注极端情况。尤其是在参数优化时,导致我们选择一些仅仅只是幸运避开了个别几笔亏损交易,但整体预测效果未必最佳的参数组合。
好在,最近一篇新的论文提供了一个可靠的研究改进方向。
2019年德国科隆大学金融研究中心的Korn, Moller和Schwehm合作发布了一篇研究量化策略回撤分析的文章:
《Drawdown Measures: Are They All the Same?》
1) 在文章中,他们首先创建了6个不同的回撤指标:
指标的公式和图形如下:
2)然后做了一个对比分析,用不同回撤指标来识别:
发现平均回撤(ADD)、线性加权回撤(lwDD)以及均方回撤(ADD^2)三者的效果最为接近:
3)除了相似性以外,这3个指标的识别效果也更佳,即相比于最大回撤 MDD,这些回撤指标能够更有效的区分随机和正期望策略:
首先,我们需要在Jupyter Notebook中,以命令行CLI交互的模式调用CtaStrategy模块的回测功能(不会的请戳这里),对一个策略执行历史数据回测,得到资金曲线图和百分比回撤图:
此时可以访问BacktestingEngine对象实例的daily_df(DataFrame格式)数据,获取基于逐日盯市规则计算的每日策略统计数据(如当日盈亏、累计盈亏、成交笔数、百分比最大回撤等):
由于百分比回撤是负数,为了后续研究的时候方便观察,先创建一个新的列【ddpercent_justed】,这是调整为正数后的百分比最大回撤,然后画图显示出来:
最大回撤可以通过对百分比回撤序列调用max函数方法,得到结果为1.4%:
有了参考对象,接下来我们开始计算以上论文中提到的3个更为有效回撤指标。
平均回撤就是每日百分比回撤的算数平均,直接调用mean方法得到结果为0.41%,即采用简单的算术平均计算策略整体每日亏损的风险为0.41%:
线性加权回撤就是使用最小二乘法(OLS)对回撤曲线进行线性回归,从而得到一条回撤的趋势线。最小化误差平方和的方法有利于避免极端情况的影响,让我们把关注点更多集中在策略的整体风险水平上。一个好的策略,其回撤的回归直线的斜率应该尽可能的小。
由于这里要求的只是回归线的终值,而不需要过程中的其他信息,所以无需动用那些巨型的数据分析库,如sklearn、statsmodels、scipy等等,这里我们选择更加轻量级的talib库(没错,算技术指标的那个)。
在交互式模式下执行命令:help(talib.LINEARREG),我们可以知道线性回归函数的入参有2个:
其中:
这样,我们得到线性加权回撤的结果为0.31%,即通过最小二乘法去噪声后,策略整体每日亏损的风险为0.31%:
均方回撤就是每日百分比回撤的平方的期望值,采用这种计量方法的原因在于认为策略样本内回测属于小样本评估,属于有偏估算。
均方回撤的计算方法同样简单,只需要将百分比回撤这一列的数据平方后,再求平均数即可,最终得到均方回撤的结果为0.32%:
最后我们可以将上述3个新的回撤统计指标,添加到回测引擎的统计指标计算函数中。打开位于C:\vnstudio\Lib\site-packages\vnpy\app\cta_strategy目录中的backtesting.py文件,找到其中的calculate_statistics函数,修改的内容分为三步:
新增的代码如下:
...
average_drawdown = 0
lw_drawdown = 0
average_square_drawdown = 0
...
average_drawdown = df["ddpercent"].mean()
lw_drawdown = talib.LINEARREG(df["ddpercent"], total_days)[-1]
df["ddpercent^2"] = df["ddpercent"] * df["ddpercent"]
average_square_drawdown = - df["ddpercent^2"].mean()
...
self.output(f"百分比平均回撤: {average_drawdown:,.2f}%")
self.output(f"百分比线性加权回撤: {lw_drawdown:,.2f}%")
self.output(f"百分比均方回撤: {average_square_drawdown:,.2f}%")
完成修改后,再次回到Jupyter Notebook中执行回测,已经可以在打印输出的回测结果中找到三个新增的回撤指标:
同样在CLI命令行模式下,我们也可以很方便的这3个新的回撤指标(average_drawdown、lw_drawdown、average_square_drawdown),作为CTA策略参数优化的目标函数,筛选出稳健性更好的参数组合。
《vn.py全实战进阶 - 期权零基础入门》课程已经更新过半!内容专门面向从未接触过期权交易的新手,共计30节课程带你一步步掌握期权的基础知识、了解合约特征和品种细节、学习方向交易和套利组合等各种常用期权交易策略,详细内容请戳新课上线:《期权零基础入门》。
发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2020-02-11
新冠病毒肆虐,大家多多注意身体,有条件远程办公的还是远程吧,从概率角度讲复工后的一周风险也许是最大的。废话不多说,上周末发布了2020年的第一个版本v2.1.0:期权电子眼,主要更新了期权自动化交易(电子眼算法)相关的功能。
和之前一样,对于使用VN Studio的用户,启动VN Station后,直接点击界面右下角的【更新】按钮就能完成自动更新升级。对于没有安装的用户,请下载VNStudio-2.1.0,体验一键安装的量化交易Python发行版,下载链接:
https://download.vnpy.com/vnstudio-2.1.0.exe
几大股指期权上线后已经平稳运行一个多月,期权自动交易方面的口风也没那么紧了,所以新版本开源了OptionMaster Pro中的全部自动交易功能。
期权领域的量化交易主要集中在波动率交易策略上。而期权的波动率无法从其价格上直接反应出来,需要通过定价公式来实时反推隐含波动率。
通过上图中的波动率曲线图表,交易员可以直观的看到波动率曲线(面)的形状,并且作出相应的判断分析,每个月份的波动率曲线采用不同的颜色显示:
用户可以通过顶部的勾选框来选择当前要显示波动率曲线的月份,方便根据自己的需求来详细分析单一月份的形状或者多月份的曲面形态。
对当前市场的隐含波动率曲线形态有所了解后,交易员需要根据自己的判断,使用下图中的波动率管理组件,来设置定价参考波动率,作为后续电子眼交易算法的定价依据:
定价参考波动率的设置需要比较多的期权定价原理知识,以及一定的波动率交易经验,对于单一月份波动率曲线拟合的流程如下:
完成定价参考波动率曲线的拟合后,就可以通过上图中的期权电子眼算法(英文名Electronic Eye),来自动扫描市场上的最优交易机会(max price edge),并在Tick级的时间级别上瞬时完成交易。
在波动率交易过程中,无论是期权开平仓本身引入的Delta,或者是由于持仓Gamma随着市场波动带来的Delta,都必须根据交易需求及时处理:
使用上图中的Delta对冲算法,交易员可以根据自己的需求选择任意定价标的物作为对冲合约,并灵活设置对冲检查执行的频率、Delta对冲目标、对冲触发阈值,以及下单时采用的委托超价等参数,来实现Delta自动实时对冲的功能。
在最初的设计上,vnpy.chart这个K线图表主要是为了满足CtaBacktester模块中对于CTA策略回测历史买卖点显示的需求。
没想到自从发布以来,社区中就不断出现实时K线图表的需求,加上前段时间的文华事件,开发一套100%完全开源的高性能图表工具,可能也变成了挺有价值的工作。
所以v2.1.0版本中,新增了ChartWizard模块,用于实时K线图表的显示,如下图:
目前只是第一个版本,仅仅实现了简单的1分钟实时K线行情显示,直接在本地合约代码的编辑框中输入vt_symbol(比如IF2002.CFFEX),点击【新建图表】的按钮就会打开对应合约的图表。
需要注意的是,vn.py本身并不提供任何数据服务,对于绘图要用到的历史K线数据,国内期货目前通过RQData数据服务获取(需要准备和配置RQData账号)。
后续会根据社区用户的反馈情况,来决定是否要进一步增加图表功能,例如:
RTD全称是RealTimeData,是微软主要为金融行业的实时数据需求设计的Excel数据对接方案,号称可以达到每秒全表60000次的刷新速率,能不能达到我们其实都不用管(其实1秒6次数字刷新普通人眼估计就要瞎了吧)...
对于个人投资者来说可能比较陌生,但对于机构交易员来说(不管量化还是主观),Wind/Choice等金融行情终端的Excel RTD插件,可以说是日常工作中常用的工具之一。毕竟即使是量化交易员,也没必要为了所有要跟踪的市场行情数据都开发一套前端界面(性价比太低),方便易用的Excel电子表格就是好的替代工具。
本次更新中添加的ExcelRtd模块,用于实现在Excel中访问vn.py程序内任意数据信息的功能,上图中的简单例子是在Excel表中获取来自CTP接口的实时行情数据刷新。ExcelRtd依赖于PyXLL模块(www.pyxll.com),该模块属于商业软件,需要购买才能使用(提供30天免费试用)。
2.1.0版本中新增了恒生UFT接口的对接,可以用于期货(包括期货期权)和ETF期权的量化交易,终于vn.py可以对接国内几乎所有期货公司了(部分还是只有金仕达柜台的就算了)。
UFT接口在使用时比起CTP来说稍微麻烦一些:除了满足穿透式认证的需求外(获取AppID和AuthCode),还需要用户向期货公司申请获取接入授权文件(liscense.dat),具体请联系咨询期货公司的IT部门。
vn.py的CI服务从CircleCI切换到了Github Actions,用了一个多月下来发现无论脚本配置方便度还是测试运行速度都有明显的提升,对于在vn.py上做二次扩展开发的用户强烈推荐使用。
PS:CI的全称是Continuous Integration,中文名“持续集成”,主要用于在每次更新代码后自动运行各类测试检查程序,来保证持续开发过程中整体代码的质量。许多用户反应v2.0后的代码质量有了明显的提升,其中CI的帮助居功至伟。
Python自从3.5版本引入Type-Hint可选类型声明后,代码的可读性以及IDE开发工具(尤其是VSCode)的智能提示检查功能都有了飞跃式的进步。vn.py在后续的开发中会全面加入Type-Hint的类型声明,目前主要完成了核心模块的修改(包括vnpy.trader/rpc/event模块)。
考虑到大家的健康安全的风险,准备把2、3月份社区的线下活动全部改为线上活动的模式,计划在2月底举行的同时也是vn.py社区2020年第1次线上活动了。
内容:
ChartWizard模块扩展开发
a. vnpy.chart模块
b. 静态绘图脚本
c. 行情事件流
d. 绘图图层开发
e. 技术指标扩展
ExcelRtd模块应用
a. pyxll安装配置
时间: 2月29日下午14:00-16:00
报名费:99元
报名方式:扫描下方二维码
发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2019-08-11
因为某些外部原因,年初开始和华尔街见闻合作的《Quant全实战成长计划》,在完成第一阶段的内容后等待了两个月的时间,目前已经确定无法再往下推进了。之前购买了的见闻课程,依旧可以通过见识APP来回看学习(直接用见闻账户登录见识APP即可),这点请放心。
考虑到社区里很多用户还是希望有在线课程来帮助学习vn.py在量化方面的进阶使用,接下来准备由我们核心开发团队自己,来独立制作一个完整的《vn.py全实战进阶》系列(避免再出现外部幺蛾子~~~)。
上周在QQ群里做了个关于主题的投票调查,结果如下:
决定接下来推出的第一阶段内容,就是CTA策略主题了。后面按顺序的应该是:价差交易、算法交易、期权波动率、其他内容。其中期权波动率和算法交易的票数比较接近,同时目前国内最主要的50ETF期权限制了新增程序化接入(只有老用户能用API),所以做了调整。
课程一共计划50节,内容大纲如下:
计划下周正式上线,直接在社区公众号(vnpy-community)里就能购买和观看,方便大家利用各种碎片时间,同样可以在PC上打开,边学边练更加深入。
发布于vn.py社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2020-04-17
v2.1.2版本的vn.py已经于本周三正式发布,此次主要更新的内容包括多标的组合策略模块PortfolioStrategy的支持。
和之前一样,对于使用VN Studio的用户,启动VN Station后,直接点击界面右下角的【更新】按钮就能完成自动更新升级,对于没有安装的用户,请下载VNStudio-2.1.2,体验一键安装的量化交易Python发行版,下载链接:
PortfolioStrategy模块专门针对需要同时交易多合约的量化策略设计,满足其历史数据回测和实盘自动交易的需求,一些策略例子包括:
目前模块自带的Demo策略只有个最简单的趋势跟踪策略(AtrRsiStrategy移植过来的),后面更新中争取会把上述每个策略类型都提供一个Demo。
PortfolioStrategy模块在设计上为了降低大家的学习成本,采用了尽可能接近CTA策略模块的方案。下图中展示的是一套铁矿石期权平价套利策略在Jupyter Notebook中的回测结果,同时涉及到三个合约,包括看涨期权、看跌期权、铁矿石期货:
除了上图中的回测功能外,PortfolioStrategy的实盘自动交易管理界面也和CtaStrategy比较相似,只是少掉了右上角停止单状态的显示区域:
对于已经掌握CTA策略开发的用户来说,基本可以无缝过渡到多标的组合策略开发上:
当然,毕竟是针对不同类型的量化策略,PortfolioStrategy在一些细节方面和CTA策略模块还是有所区别:
CtaStrategyCTA策略 | PortfolioStrategy组合策略 | |
---|---|---|
K线推送 | on_bar收到单一合约的K线 | on_bars同时收到所有合约的K线 |
TICK推送 | on_tick回测和实盘均支持 | on_tick只有实盘中会调用,回测不支持 |
成交推送 | on_trade | 无 |
委托推送 | on_order | 无 |
查询持仓 | 通过访问策略的pos获取 | 调用get_pos获取某一合约的持仓 |
查询委托 | 无 | 调用get_order获取某一委托的状态 |
查询活动委托号 | 无 | 调用get_all_active_orderids获取当前全部活动委托号 |
交易委托 | buy/sell/short/cover只需传入价格、数量 | buy/sell/short/cover需要传入合约代码、价格、数量 |
其中最主要的区别,在于组合策略在接收K线推送时,是通过on_bars回调函数一次性接收该时间点上所有合约的K线数据,而不是通过on_bar函数一个个接收(无法判断当前时点的K线是否全部走完了 )。组合策略的回测只支持K线模式,但在实盘中用户可以在on_tick回调下自行管理所有K线的合成推送逻辑(比如用非标准的K线切分点)。
第二个区别点,在于组合策略中需要对多合约同时下单交易,在回测时无法判断某一段K线内部每个合约委托成交的先后时间顺序,因此无法提供on_order和on_trade获取委托成交推送,而只能在每次on_bars回调时通过get_pos和get_order来进行相关的状态查询。
第三点区别在于委托下单函数:组合策略在调用委托函数下单时必须传入具体要交易合约的代码,而不像CTA策略只能交易一个合约所以用不着。同时组合策略模块只支持限价单交易,不提供停止单功能(StopOrder)。
DataManager模块发布后,有一堆用户询问了如何删除不需要的数据的问题,所以新版本中我们加上了一键删除的功能,就在查询出来后的数据统计结果每行的最右侧按钮。
接口方面的更新包括:
vn.py核心团队的底层接口开发小班课上线
日期:2020年5月16日(周六)和5月17日(周日)
时间:两天下午1点-6点,共计10小时
内容:
两天共计10小时的实战训练课程
市场微观结构知识的详细剖析
C++代码的Python封装实操
底层接口业务功能的对接设计
价格:9999元
报名请发送邮件到vn.py@foxmail.com,注明想参加的课程、姓名、手机、公司、职位。