报错没有plotly, 应该是因为2.1.4图形显示从matplotlib换成了plotly。可以打开cmd执行pip install plotly命令手动安装试试看启动还报不报错。
1.原因应该是你用的sl_multiplier太小了,以至于价格太接近close_price,所以总是触到停止单定的价格,继而发出委托。
自己不确定的时候其实可以print看看。
图上没有改你的参数,只是加上了trailing_percent(拿的原策略里的0.8)算出的long_stop进行对比,可以看到,你的atr1算出的long_stop一直比trailing_percent算出的更贴近close_price,所以交易笔数多是正常的。
此外,策略跑起来是没有问题的,但是有现成的atr建议不用再加个不一样的window来计算atr。这样增加了复杂度,但不一定能对策略起到实际的提升效果。而且如果之后走到优化,因为本来atrrsi就有很多参数了,再增加新的参数不仅会消耗更多时间也会带来更大的过拟合的风险。如果要改atr止损就像下面一样简单改动就可以了。
from vnpy.app.cta_strategy import (
CtaTemplate,
StopOrder,
TickData,
BarData,
TradeData,
OrderData,
BarGenerator,
ArrayManager,
)
class AtrRsiStrategy_百分比修改为atr(CtaTemplate):
""""""
author = "用Python的交易员"
atr_length = 22
atr_ma_length = 10
rsi_length = 5
rsi_entry = 16
atr_multiplier = 3
fixed_size = 1
atr_value = 0
atr_ma = 0
rsi_value = 0
rsi_buy = 0
rsi_sell = 0
intra_trade_high = 0
intra_trade_low = 0
parameters = [
"atr_length",
"atr_ma_length",
"rsi_length",
"rsi_entry",
"atr_multiplier",
"fixed_size"
]
variables = [
"atr_value",
"atr_ma",
"rsi_value",
"rsi_buy",
"rsi_sell",
"intra_trade_high",
"intra_trade_low"
]
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
""""""
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
self.bg = BarGenerator(self.on_bar)
self.am = ArrayManager()
def on_init(self):
"""
Callback when strategy is inited.
"""
self.write_log("策略初始化")
self.rsi_buy = 50 + self.rsi_entry
self.rsi_sell = 50 - self.rsi_entry
self.load_bar(10)
def on_start(self):
"""
Callback when strategy is started.
"""
self.write_log("策略启动")
def on_stop(self):
"""
Callback when strategy is stopped.
"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""
Callback of new tick data update.
"""
self.bg.update_tick(tick)
def on_bar(self, bar: BarData):
"""
Callback of new bar data update.
"""
self.cancel_all()
am = self.am
am.update_bar(bar)
if not am.inited:
return
atr_array = am.atr(self.atr_length, array=True)
self.atr_value = atr_array[-1]
self.atr_ma = atr_array[-self.atr_ma_length:].mean()
self.rsi_value = am.rsi(self.rsi_length)
if self.pos == 0:
self.intra_trade_high = bar.high_price
self.intra_trade_low = bar.low_price
if self.atr_value > self.atr_ma:
if self.rsi_value > self.rsi_buy:
self.buy(bar.close_price + 5, self.fixed_size)
elif self.rsi_value < self.rsi_sell:
self.short(bar.close_price - 5, self.fixed_size)
elif self.pos > 0:
self.intra_trade_high = max(self.intra_trade_high, bar.high_price)
self.intra_trade_low = bar.low_price
long_stop = self.intra_trade_high - self.atr_value * self.atr_multiplier
self.sell(long_stop, abs(self.pos), stop=True)
elif self.pos < 0:
self.intra_trade_low = min(self.intra_trade_low, bar.low_price)
self.intra_trade_high = bar.high_price
short_stop = self.intra_trade_low + self.atr_value * self.atr_multiplier
self.cover(short_stop, abs(self.pos), stop=True)
self.put_event()
def on_order(self, order: OrderData):
"""
Callback of new order data update.
"""
pass
def on_trade(self, trade: TradeData):
"""
Callback of new trade data update.
"""
self.put_event()
def on_stop_order(self, stop_order: StopOrder):
"""
Callback of stop order update.
"""
pass
2.可以相应调整参数,比如说atr_window和对应的乘数(e.g. 增大)。要是觉得交易次数过多,也可以考虑一下不同的品种进行尝试。
发布于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策略开发
螺纹钢手续费率是0.01%,应该填0.0001。如果是每手几元,可以用手续费除以每手价格得到一个大概的数值填上去。
关于交易滑点,就是最小变动价位*预计多少跳。
发布于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
请问是回测还是优化?一般的策略回测的话应该不需要一两天的时间的。如果是优化,可以先用遗传算法找出大概的参数平原范围之后,再用穷举算法来优化,这样可以节约不少时间。
因为成交记录为空了,自然就没有图形输出了。成交记录为空有很多可能的原因,下次可以注意一下,如果显示“历史数据加载完成,数据量:0”,有可能是没有该品种的数据,或者是填错了交易所。如果显示有数据而成交记录为空,则可能是数据太少不够初始化,或者策略的条件太苛刻以至于一直没有发出委托。
看不到完整代码,请问是使用的自己编写的DualThrustStrategy还是cta_strategy附带的呢?附带的DualThrustStrategy里好像没有trailing_percent这个参数。
策略内通过self.write_log函数就能输出日志信息。
get_order:根据vt_orderid查询委托单的详细信息。
0:账户的资金,交易品种的手续费,合约乘数等不需要在策略中填写,回测过程中在图形界面设定就好了。可用资金只能在实盘中查看,但是在vn.py框架中不推荐这么做。
1-2:on_trade可以收到委托的成交通知,其中:
trade.direction:买卖方向
trade.offset:开平方向
trade.volume:成交数量
trade.price:成交价格
3-4:交易信号与持仓方向需要自己在策略中去写。
5:拿AtrRsiStrategy中第99行代码举例:
elif self.pos > 0:
self.intra_trade_high = max(self.intra_trade_high, bar.high_price)
# COND:最新K线的最高价高于持仓周期的最高价
# bar.high_price = X ; self.intra_trade_high = 上一个X
self.intra_trade_low = bar.low_price
long_stop = self.intra_trade_high * \
(1 - self.trailing_percent / 100)
self.sell(long_stop, abs(self.pos), stop=True)
6-7:如想求N天最高,最低值,可直接调用am中的唐奇安指标。如想求收盘价,可参考此贴。
发布于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,注明想参加的课程、姓名、手机、公司、职位。