算法交易-TWAP算法

书接上节:)
上节提到了冲击成本和被动交易算法,那么本节跟大家探讨下最简单的被动交易算法–TWAP

说算法前我们先套入一个场景:
Mr.Rich要买100000股中信证券,大概281万块钱吧。那么Mr.Rich有这么几种购买方式:
1)直接下1000手市价买单—除非市场上有大于100000股的卖单,否则这100000股买单将慢慢的成交直到成交完毕,平均价格大概28.2
2)下1000手限价买单—除非市场上有接近这个量的卖单,否者这1000手限价单的成交将比较缓慢,可能三个小时后成交完毕,均价28.15,也可能直到收盘都没成交。

以上的例子经过了简化,这两种购买方式分别对应交易的两个重要成本:冲击成本和等待成本;直接市价成交则基本没有等待成本,但冲击成本大概有50-100bp左右(bp是万分之一,这个估计是按照两交所的报告来的),全部限价单挂着则基本没有冲击成本,但等待成本太高,有可能无法完成成交。

So,这样的情况机构交易员们天天遇到,也是他们最先将算法应用到下单里。我猜他们第一个尝试的就是TWAP算法。

TWAP算法是 Time Weighted Average Price 的缩写,直译为时间加权平均价格算法,所谓时间加权就是将时间段的长短作为权重,对于一个时间段(下单所要求的时间段)来说时间加权就是等权重,也就是平均分布的。

比如Mr.Rich要求15分钟内完成这100000股,那么交易员会把这么多股拆分成(100000/15*60~=100*1000)1000个100股,然后每秒下100-200股的频率一直下15分钟的单。这样一来等待成本就限制在15分钟左右,冲击成本则肯定要比1单十万股要低,算法是有效的。

当然,以上是理想的情况,如果下市价单还好,如果是限价单则容易出现子单不成交的情况,一般这时候算法就会撤掉价格偏离较大的单重新下。

所以逻辑应该是这样的:交易员规定下单时长和数量–>算法模块将母单数量按时长拆分->交易模块按照算法模块的拆分下单–>成交的部分回报给交易员,未成交的部分回报给算法模块–>算法模块将未成交的部分再次挂单,分配给后续时间–>成交完成

无疑,这里面最重要的是核心算法模块,就是如何拆单的模块。TWAP的核心算法要注意这几个方面1)买单最小是100股,卖单可以一次性把不足100股的卖掉2)深交所的收盘前有集合竞价,别下单到那个时间段3)拆单部分可以由几个参数取模取余等实现,不必要用遍历的方法,那样单子多了会很慢…

上面是临时想到的,我用R语言简单写了个demo

需要timeDate包,提前加载好install.packages(“timeDate”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
MyTwap<-function (T_Symbol,T_Start,T_End,T_Quantity){
#T_Symbol is the Target Symbol which we want to splite
#T_Start is the start time we send the order, style as HHMMSS
#T_End is the end time we want the order be finish, style as HHMMSS
#T_Quantity is the quantity of the order
#T_Symbol<-"600123";T_Start<-"095600";T_End<-"101100";T_Quantity<-100000; #Get the order ! ##style:symbol,price,startTime,Endtime # 600123,10.05,HHMMSS,HHMMSS read.csv(file="E:Research DocTWAPorder.csv",header=T)->tm.p
#Transform and standardised the Order
data.frame(
Symbol=
formatC(tm.p$Symbol,width = 6, flag = 0),
Price=
tm.p$Price,
StartTime=
formatC(tm.p$StartTime,width = 6, flag = 0),
EndTime=
formatC(tm.p$EndTime,width = 6, flag = 0),
T_Quantity=tm.p$Quantity,
stringsAsFactors=F
)->MyOrder

rm(tm.p)
#Read VolumeProfile
#unnecessary for TWAP

#Using Twap to Splits the Big Order
library(timeDate)
Interval_Seconds <- difftimeDate(timeDate(MyOrder$EndTime,format="%H%M%S"),timeDate(MyOrder$StartTime,format="%H%M%S"),units="secs") #Build the Order Queue #Build the Part I data.frame( timeSequence(from=MyOrder$StartTime,to=MyOrder$EndTime,format="%H%M%S",by="sec",length.out=as.numeric(Interval_Seconds)), rep( floor((MyOrder$T_Quantity/as.numeric(Interval_Seconds))/100)*100, as.numeric(Interval_Seconds)), rep(0.0,as.numeric(Interval_Seconds)) )->Order_Plan
names(Order_Plan)<-c("Time","Quantity","Price")
#Build the part II
leaved_quantity<-MyOrder$T_Quantity-sum(Order_Plan$Quantity) while (leaved_quantity>100){

floor(leaved_quantity/100)->Avaliable_Order
ceiling(as.numeric(Interval_Seconds)/Avaliable_Order)->Time_Gap

Order_Plan[seq(from=Time_Gap,by=Time_Gap,to=as.numeric(Interval_Seconds)),2]+100 -> Order_Plan[seq(from=Time_Gap,by=Time_Gap,to=as.numeric(Interval_Seconds)),2]

leaved_quantity<-leaved_quantity-(as.numeric(Interval_Seconds)/Time_Gap)*100 } #Build the Last Part Order_Plan[(-dim(Order_Plan)[1]+1):-1,2]+round(leaved_quantity/100)*100-> Order_Plan[(-dim(Order_Plan)[1]+1):-1,2]
#End of Order_Plan
#Debug

sum(Order_Plan$Quantity)
Order_Plan$Quantity
rm(Avaliable_Order,Interval_Seconds,Time_Gap,leaved_quantity)
return(Order_Plan)
}

TWAP的效果示例如下

309f90e39271a615b757ffe680d38d8b

黑色部分为某分钟的实际成交量,红色部分为TWAP算法下单的数量,可见TWAP确实像其名字一样是按照时间加权分布的。由于目前还没有条件去实盘测试..

本文遵守署名-非营利性使用-相同方式共享协议,转载请保留本段:冰丝带雨 » 算法交易-TWAP算法

赞 (4)