书接上节:)
上节提到了冲击成本和被动交易算法,那么本节跟大家探讨下最简单的被动交易算法–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的效果示例如下
黑色部分为某分钟的实际成交量,红色部分为TWAP算法下单的数量,可见TWAP确实像其名字一样是按照时间加权分布的。由于目前还没有条件去实盘测试..
本文遵守署名-非营利性使用-相同方式共享协议,转载请保留本段:冰丝带雨 » 算法交易-TWAP算法