Lean  $LEAN_TAG$
VolumeWeightedAveragePriceExecutionModel.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Collections.Generic;
18 using System.Linq;
20 using QuantConnect.Data;
25 using QuantConnect.Orders;
26 
28 {
29  /// <summary>
30  /// Execution model that submits orders while the current market price is more favorable that the current volume weighted average price.
31  /// </summary>
33  {
34  private readonly PortfolioTargetCollection _targetsCollection = new PortfolioTargetCollection();
35  private readonly Dictionary<Symbol, SymbolData> _symbolData = new Dictionary<Symbol, SymbolData>();
36 
37  /// <summary>
38  /// Gets or sets the maximum order quantity as a percentage of the current bar's volume.
39  /// This defaults to 0.01m = 1%. For example, if the current bar's volume is 100, then
40  /// the maximum order size would equal 1 share.
41  /// </summary>
42  public decimal MaximumOrderQuantityPercentVolume { get; set; } = 0.01m;
43 
44  /// <summary>
45  /// Submit orders for the specified portfolio targets.
46  /// This model is free to delay or spread out these orders as it sees fit
47  /// </summary>
48  /// <param name="algorithm">The algorithm instance</param>
49  /// <param name="targets">The portfolio targets to be ordered</param>
50  public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets)
51  {
52  // update the complete set of portfolio targets with the new targets
53  _targetsCollection.AddRange(targets);
54 
55  // for performance we check count value, OrderByMarginImpact and ClearFulfilled are expensive to call
56  if (!_targetsCollection.IsEmpty)
57  {
58  foreach (var target in _targetsCollection.OrderByMarginImpact(algorithm))
59  {
60  var symbol = target.Symbol;
61 
62  // calculate remaining quantity to be ordered
63  var unorderedQuantity = OrderSizing.GetUnorderedQuantity(algorithm, target);
64 
65  // fetch our symbol data containing our VWAP indicator
66  SymbolData data;
67  if (!_symbolData.TryGetValue(symbol, out data))
68  {
69  continue;
70  }
71 
72  // check order entry conditions
73  if (PriceIsFavorable(data, unorderedQuantity))
74  {
75  // adjust order size to respect maximum order size based on a percentage of current volume
77  data.Security, MaximumOrderQuantityPercentVolume, unorderedQuantity);
78 
79  if (orderSize != 0)
80  {
81  algorithm.MarketOrder(data.Security.Symbol, orderSize);
82  }
83  }
84  }
85 
86  _targetsCollection.ClearFulfilled(algorithm);
87  }
88  }
89 
90  /// <summary>
91  /// Event fired each time the we add/remove securities from the data feed
92  /// </summary>
93  /// <param name="algorithm">The algorithm instance that experienced the change in securities</param>
94  /// <param name="changes">The security additions and removals from the algorithm</param>
95  public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
96  {
97  foreach (var added in changes.AddedSecurities)
98  {
99  if (!_symbolData.ContainsKey(added.Symbol))
100  {
101  _symbolData[added.Symbol] = new SymbolData(algorithm, added);
102  }
103  }
104 
105  foreach (var removed in changes.RemovedSecurities)
106  {
107  // clean up removed security data
108  SymbolData data;
109  if (_symbolData.TryGetValue(removed.Symbol, out data))
110  {
111  if (IsSafeToRemove(algorithm, removed.Symbol))
112  {
113  _symbolData.Remove(removed.Symbol);
114  algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator);
115  }
116  }
117  }
118  }
119 
120  /// <summary>
121  /// Determines if it's safe to remove the associated symbol data
122  /// </summary>
123  protected virtual bool IsSafeToRemove(QCAlgorithm algorithm, Symbol symbol)
124  {
125  // confirm the security isn't currently a member of any universe
126  return !algorithm.UniverseManager.Any(kvp => kvp.Value.ContainsMember(symbol));
127  }
128 
129  /// <summary>
130  /// Determines if the current price is better than VWAP
131  /// </summary>
132  protected virtual bool PriceIsFavorable(SymbolData data, decimal unorderedQuantity)
133  {
134  if (unorderedQuantity > 0)
135  {
136  if (data.Security.BidPrice < data.VWAP)
137  {
138  return true;
139  }
140  }
141  else
142  {
143  if (data.Security.AskPrice > data.VWAP)
144  {
145  return true;
146  }
147  }
148 
149  return false;
150  }
151 
152  /// <summary>
153  /// Symbol data for this Execution Model
154  /// </summary>
155  protected class SymbolData
156  {
157  /// <summary>
158  /// Security
159  /// </summary>
160  public Security Security { get; }
161 
162  /// <summary>
163  /// VWAP Indicator
164  /// </summary>
165  public IntradayVwap VWAP { get; }
166 
167  /// <summary>
168  /// Data Consolidator
169  /// </summary>
171 
172  /// <summary>
173  /// Initialize a new instance of <see cref="SymbolData"/>
174  /// </summary>
175  public SymbolData(QCAlgorithm algorithm, Security security)
176  {
177  Security = security;
178  Consolidator = algorithm.ResolveConsolidator(security.Symbol, security.Resolution);
179  var name = algorithm.CreateIndicatorName(security.Symbol, "VWAP", security.Resolution);
180  VWAP = new IntradayVwap(name);
181 
182  algorithm.RegisterIndicator(security.Symbol, VWAP, Consolidator, bd => (BaseData) bd);
183  }
184  }
185  }
186 }