Volume-Weighted Average Pricing (VWAP) Orders
CQ Elite Only
The Volume-Weighted Average Pricing (VWAP) order strives to achieve volume-weighted average price by slicing the order over a period of time specified by the order creator and fitting the order slice quantity to the recent historical volume curve for the specified time range. This strategy is used to accumulate (or liquidate) a position over the specified time. The orders are sliced (broken up) in such a way as to help hide the accumulation of the position from others watching the market activity.
VWAP orders can be for buying or selling (including short selling)
The most common use of VWAP 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 VWAP 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.vwap() 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 VWAP orders |
end_time | muts | No | md.market_close_time - 30 sec | In UTC, the time to end making VWAP orders |
time_frame | integer | No | None | The number of minutes for the VWAP 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.
Sub-Orders (time slices) always fill
The suborders will always fill. If a suborder reaches the end of the timeframe for the given slice, the VWAP will cancel the open suborder and add in a market order so that the order is immediately filled.
Buy or Sell
The VWAP 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 VWAP will correctly identify the suborders prior to submission.
ReturnsType | Special | Notes |
---|---|---|
integer | order_id | unique twap/vwap order identifier |
The returned integer is a "handle" to the overall VWAP order. This handle can be used to cancel the order.
Working Examples: Simple VWAP 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.vwap(self.symbol, 5000, "buy", time_frame=10,order_aggression=3)
This VWAP 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.
vwap_start = md.market_close_time - service.time_interval(0,5) # start_time = 5 min before close
c_vwap = order.vwap(self.symbol, 7500, "sell", start_time=vwap_start)
c_vwap = order.vwap(self.symbol, 7500, "sell", start_time=vwap_start,
time_frame=10, order_aggression=3)
order.vwap_cancel(c_vwap)
Full Working Example:(Link)
# Copyright Cloudquant, LLC. All right reserved.
from cloudquant.interfaces import Strategy
class FB_VWAP_StartOfDay_To_EOD(Strategy):
@classmethod
def is_symbol_qualified(cls, symbol, md, service, account):
return symbol in ['FB']
def __init__(self): #, **params - if passing in parameters is desired
self.vwapID = None
pass
def on_start(self, md, order, service, account):
vwap_start = md.market_open_time
vwap_end = md.market_open_time + service.time_interval(0,31) # end_time = 31 min after open
#adding a timed trigger to get the vwap price from the market at the end of the buy VWAP time
service.add_time_trigger(vwap_end)
#Order Aggression
# 1 = Very Aggressive (market sub orders) - Default behavior
# 2 = Aggressive (marketable limit orders for likely immmediate fill)
# 3 = Midpoint order
# 4 = Passive (join the best bid or offer)
#order.twap(self.symbol, 15000, "buy", num_slices=200, time_frame = 1, order_aggression=2)
self.vwapID = order.vwap(self.symbol, 15000, "buy", start_time=vwap_start,
end_time=vwap_end, order_aggression=3)
print("Vwap id = {}".format(self.vwapID))
# Close position at EOD
vwap_start = md.market_close_time - service.time_interval(0,16) # start_time = 16 min before close
order.vwap(self.symbol, 15000, "sell", start_time=vwap_start, order_aggression=3) # will end 30 seconds before close by default
#adding a timed trigger to get the vwap price from the market at the end of the sell vwap time
#service.add_time_trigger(md.market_close_time)
def on_timer(self, event, md, order, service, account):
print(service.time_to_string(event.timestamp) + " - Timer triggered")
min_bars = md.bar.minute(start=-30,include_empty=False) # grab 30 minute bars of historical data
total_vol = 0.0
market_vwap = 0.0
mlen = len(min_bars.vwap)
n = 0
while n < mlen:
market_vwap = market_vwap + (min_bars.vwap[n] * min_bars.volume[n])
total_vol = total_vol + min_bars.volume[n]
n = n+1
print("{} Info 30 minute VWAP for {} = {}".format(service.time_to_string(event.timestamp),self.symbol, market_vwap/total_vol) )
print("{} Info Position Qty {} at Entry Price of {}".format(service.time_to_string(event.timestamp), account[self.symbol].position.shares, account[self.symbol].position.entry_price))
print("{} Info Price difference = {}".format(service.time_to_string(event.timestamp), account[self.symbol].position.entry_price - market_vwap/total_vol))
def on_minute_bar(self, event, md, order, service, account, bar):
# Commented out logic for showing how to cancel a vwap order
############################################################
#if self.vwapID != None:
# print("CXL Vwap id = {}".format(self.vwapID))
# order.vwap_cancel(self.vwapID)
# self.vwapID = None
pass
def on_ack(self, event, md, order, service, account):
px = "MKT"
if event.price >= 0.0001:
px = event.price
mbar = md.bar.minute(-1)
if len(mbar.vwap) > 0:
print("{} Info: Order Ack on Symbol: {} Shares: {} Price: {} Bar_Volume: {} bar_vwap: {}".format(service.time_to_string(event.timestamp), event.symbol, event.shares, px, mbar.volume[-1], mbar.vwap[-1]))
else:
print("{} Info: Order Ack on Symbol: {} Shares: {} Price: {} Bar_Volume: None bar_vwap: None".format(service.time_to_string(event.timestamp), event.symbol, event.shares, px))
Full Working Example Notes
This working example will produce a series of time slices for 30 minutes at the beginning of the trading day to enter a long position. It will then close that position during the last 16 minutes of the trading day. When this script was run for simulation day January 2, 2018, it produced the following order slices.
You will notice that the slices starting on market open are larger - 2316 shares on the first order. By the end of the 30 minutes, the time-sliced sub-order is for 229. This is in keeping with the volume curve for the first 30 minutes. If a Time-Weighted Average Price (TWAP) order had been placed for the same quantity and time range then each sub-order would have been for the same quantity.
Output Interesting Points to Observe
1) At time slice 2018-01-02 09:41:00.000699 you will see a market sub-order entered. This is because the previous limit sub-order failed to be filled in the allotted time slice, therefore, the limit order was canceled and replaced with a market order. Please remember that on a VWAP the time slices always fill unless you cancel the VWAP.
2) The difference between the observed VWAP and the achieved VWAP is 0.1358490148. This is due to the order slicer picking the statistical size of each sub-order based upon the previous 20 days volume in that timeslice. No VWAP can guess what the future volume is going to be therefore they all attempt to guess the best order quantity based upon the statistics.
2018-01-02 09:30:00.000700 Info: Order Ack on Symbol: FB Shares: 2316 Price: 177.620002747 Bar_Volume: None bar_vwap: None
2018-01-02 09:31:00.000699 Info: Order Ack on Symbol: FB Shares: 2316 Price: 178.519996643 Bar_Volume: 471043 bar_vwap: 177.93699646
2018-01-02 09:32:00.000699 Info: Order Ack on Symbol: FB Shares: 480 Price: 178.105003357 Bar_Volume: 133885 bar_vwap: 178.030548096
2018-01-02 09:33:00.000699 Info: Order Ack on Symbol: FB Shares: 317 Price: 178.25 Bar_Volume: 157028 bar_vwap: 178.088912964
2018-01-02 09:34:00.000699 Info: Order Ack on Symbol: FB Shares: 305 Price: 178.36000061 Bar_Volume: 51968 bar_vwap: 178.100753784
2018-01-02 09:35:00.000699 Info: Order Ack on Symbol: FB Shares: 314 Price: 178.080001831 Bar_Volume: 111468 bar_vwap: 178.111709595
2018-01-02 09:36:00.000699 Info: Order Ack on Symbol: FB Shares: 250 Price: 177.994995117 Bar_Volume: 105220 bar_vwap: 178.109802246
2018-01-02 09:37:00.000699 Info: Order Ack on Symbol: FB Shares: 525 Price: 178.11000061 Bar_Volume: 89775 bar_vwap: 178.103164673
2018-01-02 09:38:00.000699 Info: Order Ack on Symbol: FB Shares: 519 Price: 178.334999084 Bar_Volume: 188573 bar_vwap: 178.121963501
2018-01-02 09:39:00.000699 Info: Order Ack on Symbol: FB Shares: 558 Price: 178.384994507 Bar_Volume: 71278 bar_vwap: 178.133102417
2018-01-02 09:40:00.000699 Info: Order Ack on Symbol: FB Shares: 317 Price: 178.544998169 Bar_Volume: 130347 bar_vwap: 178.16204834
2018-01-02 09:41:00.000699 Info: Order Ack on Symbol: FB Shares: 317 Price: MKT Bar_Volume: 173996 bar_vwap: 178.212890625
2018-01-02 09:41:00.000699 Info: Order Ack on Symbol: FB Shares: 715 Price: 178.720001221 Bar_Volume: 173996 bar_vwap: 178.212890625
2018-01-02 09:42:00.000699 Info: Order Ack on Symbol: FB Shares: 475 Price: 178.88999939 Bar_Volume: 200709 bar_vwap: 178.275009155
2018-01-02 09:43:00.000699 Info: Order Ack on Symbol: FB Shares: 722 Price: 178.779998779 Bar_Volume: 148891 bar_vwap: 178.313415527
2018-01-02 09:44:00.000699 Info: Order Ack on Symbol: FB Shares: 273 Price: 178.970001221 Bar_Volume: 82607 bar_vwap: 178.33241272
2018-01-02 09:45:00.000699 Info: Order Ack on Symbol: FB Shares: 357 Price: 178.875 Bar_Volume: 171032 bar_vwap: 178.379119873
2018-01-02 09:46:00.000699 Info: Order Ack on Symbol: FB Shares: 185 Price: 178.980003357 Bar_Volume: 97653 bar_vwap: 178.403320312
2018-01-02 09:47:00.000699 Info: Order Ack on Symbol: FB Shares: 187 Price: 178.919998169 Bar_Volume: 102533 bar_vwap: 178.428497314
2018-01-02 09:48:00.000699 Info: Order Ack on Symbol: FB Shares: 373 Price: 178.869995117 Bar_Volume: 79762 bar_vwap: 178.444885254
2018-01-02 09:49:00.000699 Info: Order Ack on Symbol: FB Shares: 206 Price: 178.779998779 Bar_Volume: 109946 bar_vwap: 178.462936401
2018-01-02 09:50:00.000699 Info: Order Ack on Symbol: FB Shares: 154 Price: 178.754997253 Bar_Volume: 64274 bar_vwap: 178.471069336
2018-01-02 09:51:00.000699 Info: Order Ack on Symbol: FB Shares: 397 Price: 178.924995422 Bar_Volume: 47158 bar_vwap: 178.478179932
2018-01-02 09:52:00.000699 Info: Order Ack on Symbol: FB Shares: 168 Price: 178.825004578 Bar_Volume: 96426 bar_vwap: 178.491973877
2018-01-02 09:53:00.000699 Info: Order Ack on Symbol: FB Shares: 153 Price: 178.774993896 Bar_Volume: 33612 bar_vwap: 178.495452881
2018-01-02 09:54:00.000699 Info: Order Ack on Symbol: FB Shares: 303 Price: 178.835006714 Bar_Volume: 71541 bar_vwap: 178.504226685
2018-01-02 09:55:00.000699 Info: Order Ack on Symbol: FB Shares: 420 Price: 178.779998779 Bar_Volume: 45469 bar_vwap: 178.507995605
2018-01-02 09:56:00.000699 Info: Order Ack on Symbol: FB Shares: 861 Price: 178.620002747 Bar_Volume: 31854 bar_vwap: 178.509963989
2018-01-02 09:57:00.000699 Info: Order Ack on Symbol: FB Shares: 185 Price: 178.459999084 Bar_Volume: 75557 bar_vwap: 178.511535645
2018-01-02 09:58:00.000699 Info: Order Ack on Symbol: FB Shares: 266 Price: 178.330001831 Bar_Volume: 32701 bar_vwap: 178.510726929
2018-01-02 09:59:00.000699 Info: Order Ack on Symbol: FB Shares: 154 Price: 178.454994202 Bar_Volume: 80145 bar_vwap: 178.508361816
2018-01-02 10:00:00.000699 Info: Order Ack on Symbol: FB Shares: 229 Price: 178.595001221 Bar_Volume: 62451 bar_vwap: 178.508728027
2018-01-02 10:01:00.000000 - Timer triggered
2018-01-02 10:01:00.000000 Info 30 minute VWAP for FB = 178.291893562
2018-01-02 10:01:00.000000 Info Position Qty 15000 at Entry Price of 178.427742576
2018-01-02 10:01:00.000000 Info Price difference = 0.135849014848
2018-01-02 15:44:00.000699 Info: Order Ack on Symbol: FB Shares: -766 Price: 181.284996033 Bar_Volume: 80954 bar_vwap: 179.902130127
2018-01-02 15:45:02.000699 Info: Order Ack on Symbol: FB Shares: -809 Price: 181.26499939 Bar_Volume: 55804 bar_vwap: 179.908111572
2018-01-02 15:46:04.000699 Info: Order Ack on Symbol: FB Shares: -461 Price: 181.23500061 Bar_Volume: 63644 bar_vwap: 179.915084839
2018-01-02 15:47:06.000699 Info: Order Ack on Symbol: FB Shares: -670 Price: 181.26499939 Bar_Volume: 60350 bar_vwap: 179.921630859
2018-01-02 15:48:08.000699 Info: Order Ack on Symbol: FB Shares: -582 Price: 181.325004578 Bar_Volume: 45206 bar_vwap: 179.926223755
2018-01-02 15:49:10.000699 Info: Order Ack on Symbol: FB Shares: -611 Price: 181.345001221 Bar_Volume: 34742 bar_vwap: 179.930267334
2018-01-02 15:50:12.000699 Info: Order Ack on Symbol: FB Shares: -818 Price: 181.455001831 Bar_Volume: 59230 bar_vwap: 179.936004639
2018-01-02 15:51:14.000699 Info: Order Ack on Symbol: FB Shares: -2404 Price: 181.480003357 Bar_Volume: 86471 bar_vwap: 179.944778442
2018-01-02 15:52:16.000699 Info: Order Ack on Symbol: FB Shares: -793 Price: 181.525001526 Bar_Volume: 86380 bar_vwap: 179.953491211
2018-01-02 15:53:18.000699 Info: Order Ack on Symbol: FB Shares: -745 Price: 181.544998169 Bar_Volume: 61844 bar_vwap: 179.95980835
2018-01-02 15:54:20.000699 Info: Order Ack on Symbol: FB Shares: -1021 Price: 181.544998169 Bar_Volume: 92419 bar_vwap: 179.96937561
2018-01-02 15:55:22.000699 Info: Order Ack on Symbol: FB Shares: -1436 Price: 181.465003967 Bar_Volume: 69757 bar_vwap: 179.976318359
2018-01-02 15:56:24.000699 Info: Order Ack on Symbol: FB Shares: -1316 Price: 181.495002747 Bar_Volume: 82241 bar_vwap: 179.983978271
2018-01-02 15:57:26.000699 Info: Order Ack on Symbol: FB Shares: -1279 Price: 181.48500061 Bar_Volume: 76135 bar_vwap: 179.991073608
2018-01-02 15:58:28.000699 Info: Order Ack on Symbol: FB Shares: -1289 Price: 181.470001221 Bar_Volume: 146127 bar_vwap: 180.004653931