导语:算法交易,又称订单执行算法,主要用于基金公司、券商等资金量庞大的主体。本篇内容主要向大家介绍标准的订单执行算法—VWAP算法交易,又称交易量加权平均价格。
众所周知,每笔交易都有成本,包括:佣金、税费、市场冲击、滑点等。即使两个策略都买卖同样的股票,也可能因为其控制成本的方法不同,导致其最终收益率不同。尤其对于资金体量较大的投资者,如果每次交易你都能省下成本,投资者组合的表现就会更好。一个明显的推论是,交易次数越多,每次交易时节省成本就显得越重要。而节省交易成本就不可避免的需要接触算法交易,因此对于一个理性的量化交易者,拥有一套能有效节省成本的算法交易是非常重要的。
再举个例子,如果我已经选好股,要大量买入200万股,但是单凭交易员操作海量单,这是有点的困难的,但是我一下子挂单买200万股,则会暴露我的踪迹,最终导致成交成本提高。那么这时候怎样解决拆单,防止冲击成本的问题呢?只有依靠算法交易。
根据各个算法交易中算法的主动程度不同,可以把算法交易分为被动型算法交易、主动型算法交易、综合型算法交易三大类。而TWAP(时间加权平均价格)、VWAP(成交量加权平均价格)就属于被动型算法交易,也是在日常算法交易中应用最为广泛的策略算法。
VWAP简称成交量加权平均价,是一种拆分大额委托单,在约定时间段内分批执行,使得最终买入或卖出的成交均价尽量接近该段时间内整个市场成交均价的算法交易策略。具体公式如下:
VWAP算法交易有四个重要因素:历史成交量,未来的成交量预测、市场动态总成交量,拆单的时间段,整个流程如下图所示:
VWAP算法交易关键在于有效解决未来成交量分布问题。假设我们已经知道下个交易日的成交量分布,那么只需要根据成交量分布,将大单拆成N个小单,并在对应N个交易时段买入即可。通常以5分钟为单位,进行一次交易,一天进行48次。
那么如何解决未来成交量分布问题?
通常我们根据过去历史20个交易日的成交量分布,取各个时间段的均值来推测下个交易日的成交量分布,并结合当日市场动态总成交量与过去20日均值的偏差来确定单位时间内的下单量。
最终每5分钟的下单量==,其中Vt代表当天交易日任意5分钟的交易量,Vp代表过去20个交易日任意五分钟的成交量均值,Vall代表过去20日平均交易量,N代表当天任意5分钟需下单量。
我们构建VWAP算法交易,并针对平安银行进行了5天算法交易测试,结果如下:
VWAP算法交易测试 | 测试结果 |
---|---|
测试1次 | 交易日20180124,执行VWAP算法交易,最终成本价为14.841,市场均价为14.814,偏差为0.0018,一共下单量为1288300,完成总量的1.2883。 |
测试2次 | 交易日20180125,执行VWAP算法交易,最终成本价为14.189,市场均价为14.181,偏差为0.0006,一共下单量为1139700,完成总量的1.1397。 |
测试3次 | 交易日20180126,执行VWAP算法交易,最终成本价为14.230,市场均价为14.191,偏差为0.0013,一共下单量为918600,完成总量的0.9186。 |
测试4次 | 交易日20180129,执行VWAP算法交易,最终成本价为13.944,市场均价为13.927,偏差为0.0013,一共下单量为969200,完成总量的0.9692。 |
测试5次 | 交易日20180130,执行VWAP算法交易,最终成本价为13.703,市场均价为13.681,偏差为0.0016,一共下单量为482300,完成总量的0.4823。 |
总结 | 1.均价偏差度较小,突出VWAP算法的优势 |
总结 | 2.根据当天市场的交易活跃度,适当在下单目标量上进行扩充或减弱 |
VWAP算法交易的python代码:
import pandas as pd
import numpy as np
# 初始化函数,全局只运行一次
def init(context):
#设置要交易的证券(000001.SZ 平安银行)
g.security = '000001.SZ'
g.N = 1000000 #设置当天需下单总量,100万股
g.volume_mean = 0 #设置过去20日成交量均值
#run_daily(handle_bar,time_rule='after_open', hours=0, minutes=5, reference_security=g.security)
#将trade交易函数设置为定时运行:每个月第一个交易日
#每日开盘前9:00被调用一次,用于储存自定义参数、全局变量,执行盘前选股等
def before_trading(context):
volume = history(g.security, ['volume'], 20, '1d', False, 'pre', is_panel=1)
g.volume_mean = volume.values.mean()
## 开盘时运行函数
def handle_bar(context, bar_dict):
date = get_datetime().strftime('%H%M%S')
if date == '093000':
pass
else:
if date[3] == '5' or date[3] == '0':#每5分钟交易一次
volume = history(g.security, ['volume'], 5, '1m', False, 'pre', is_panel=1).sum().values
VWAP=(volume/g.volume_mean)*g.N
order(g.security,VWAP[0])
## 收盘后运行函数,用于储存自定义参数、全局变量,执行盘后选股等
def after_trading(context):
date=get_datetime().strftime('%Y%m%d')
avg_price2 = context.portfolio.positions[g.security].cost_basis
avg_price1 = get_price(g.security, None, date, '1d', ['avg_price'], False, 'pre', 1, is_panel=1).values[0][0]
t=(avg_price2-avg_price1)/avg_price1
log.error('交易日{},执行VWAP算法交易,最终成本价为{},市场均价为{},偏差为{}'.format(date,avg_price2,avg_price1,t))
pass