Time Weighted Average Pricing (TWAP) Orders
CQ Elite Only
The Time-Weighted Average Pricing (TWAP) order achieves time-weighted average price by time slicing the order over a period of time specified by the order creator. This strategy is used to accumulate (or liquidate) a position over the specified time. The orders need to be sliced in such a way as to help hide the accumulation of the position from others watching the market activity.
TWAP orders can be for buying or selling (including short selling)
The most common use of TWAP is for distributing big orders throughout the trading day. For example, you wish to accumulate 20,000 shares of CURE, an ETF which typically trades only 170,000 shares per day. Putting one big order would vastly impact the market and the price most likely would start to raise. To prevent that, you define a time period in TWAP Strategy over which they want to buy shares. It will slice the big order into smaller ones and execute them over the defined time frame.
Not for use with live trading
This order.twap() method is for use with backtesting only. It isn't supported in live trading.
ParametersName | Type | Required? | Default | Information |
---|---|---|---|---|
symbol | string | Yes | The trading Symbol | |
Order Quantity | integer | Yes | Total number of shares to buy or sell. Always positive number | |
side | string | Yes | "buy" or "sell" | |
start_time | muts | No | (immediately) service.system_time | In UTC, the time to start making TWAP orders |
end_time | muts | No | md.market_close_time - 30 sec | In UTC, the time to end making TWAP orders |
time_frame | integer | No | None | The number of minutes for the TWAP to run starting at the start time. If specified, this will override the specified end time. |
order_aggression | integer | No | 1 | Determines the aggressiveness of the sub-orders submitted to the market. 1 = Very Aggressive (market suborders) - Default behavior 2 = Aggressive (marketable limit suborders for likely immediate fill) 3 = Midpoint order 4 = Passive (sub orders join the best bid or offer) |
num_slices | integer | No | order_quantity / 100 | The number of suborders to use in submitting the order to the market. |
Please Note: All other optional parameters for _algo_order can be passed in (user_key, exit_script, etc.)
SubOrder pricing
The suborder pricing always uses the best bid best offer at the time of the order slice.
SubOrders always fill
The suborders will always fill. If a suborder reaches the end of the timeframe for the given slice, the TWAP will cancel the open suborder and add in a market order so that the order is immediately filled.
Buy or Sell
The TWAP reviews the position as it submits suborders. Therefore it is not necessary to specify Sell Short if you are going short. If the account has a zero position or is expanding its position then the TWAP will correctly identify the suborders prior to submission.
ReturnsType | Special | Notes |
---|---|---|
integer | order_id | unique twap/vwap order identifier |
The return integer is a "handle" to the overall TWAP order. This handle can be used to cancel the order.
Working Examples: Simple TWAP to Buy 5000 sharesBuying 5000 Shares time sliced over the next 10 minutes, starting immediately. All suborders will start as limit orders with Midpoint Pricing.
order.twap(self.symbol, 5000, "buy", time_frame=10,order_aggression=3)
This TWAP will start 5 minutes before the close and will end 30 seconds before the end of the trading day. All suborders will be entered as market orders.
twap_start = md.market_close_time - service.time_interval(0,5) # start_time = 5 min before close
c_twap = order.twap(self.symbol, 7500, "sell", start_time=twap_start)
c_twap = order.twap(self.symbol, 7500, "sell", start_time=twap_start,
time_frame=10, order_aggression=3)
order.twap_cancel(c_twap)
# Copyright Cloudquant, LLC. All right reserved.
from cloudquant.interfaces import Strategy
class TwapExample(Strategy):
@classmethod
def is_symbol_qualified(cls, symbol, md, service, account):
return symbol == 'GOOG'
def on_start(self, md, order, service, account):
twap_start = md.market_open_time + service.time_interval(0,1) # start_time = 1 min after open
twap_end = md.market_open_time + service.time_interval(0,6) # end_time = 6 min after open
order.twap(self.symbol, 500, "buy", start_time=twap_start, end_time=twap_end, order_aggression=4)
def on_minute_bar(self, event, md, order, service, account, bar):
if event.timestamp > md.market_open_time + service.time_interval(0,1):
print(service.time_to_string(event.timestamp) + "\tin on_minute_bar()")
# print position information
print(self.symbol + ' Information:\n')
print(account[self.symbol].position)
if event.timestamp > md.market_open_time + service.time_interval(0,6):
service.terminate()
Console Output from this script
2017-12-05 09:31:02.582000 in on_minute_bar()
GOOG Information:
shares : 0
entry_price : 0.00
mtm_price : 0.00
capital_long : 0.00
capital_short : 0.00
2017-12-05 09:32:00.272000 in on_minute_bar()
GOOG Information:
shares : 100
entry_price : 991.00
mtm_price : 991.00
capital_long : 99100.00
capital_short : 0.00
2017-12-05 09:33:00.195000 in on_minute_bar()
GOOG Information:
shares : 200
entry_price : 993.50
mtm_price : 993.50
capital_long : 198700.00
capital_short : 0.00
2017-12-05 09:34:01.833000 in on_minute_bar()
GOOG Information:
shares : 300
entry_price : 996.33
mtm_price : 996.33
capital_long : 298900.00
capital_short : 0.00
2017-12-05 09:35:00.452000 in on_minute_bar()
GOOG Information:
shares : 400
entry_price : 997.25
mtm_price : 997.25
capital_long : 398900.00
capital_short : 0.00
2017-12-05 09:36:01.567000 in on_minute_bar()
GOOG Information:
shares : 500
entry_price : 998.20
mtm_price : 998.20
capital_long : 499100.00
capital_short : 0.00