Mariner Backtesting - order.vwap()

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.

Parameters
Name 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.

Returns
Type 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 shares

Buying 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)
Simple VWAP to sell 7500 shares starting at 5 min before close

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)
Cancelling a VWAP order
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