Lean  $LEAN_TAG$
SpreadExecutionModel.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 QuantConnect.Orders;
20 
22 {
23  /// <summary>
24  /// Execution model that submits orders while the current spread is in desirably tight extent.
25  /// </summary>
26  /// <remarks>Note this execution model will not work using <see cref="Resolution.Daily"/>
27  /// since Exchange.ExchangeOpen will be false, suggested resolution is <see cref="Resolution.Minute"/></remarks>
29  {
30  private readonly decimal _acceptingSpreadPercent;
31  private readonly PortfolioTargetCollection _targetsCollection;
32 
33  /// <summary>
34  /// Initializes a new instance of the <see cref="SpreadExecutionModel"/> class
35  /// </summary>
36  /// <param name="acceptingSpreadPercent">Maximum spread accepted comparing to current price in percentage.</param>
37  public SpreadExecutionModel(decimal acceptingSpreadPercent = 0.005m)
38  {
39  _acceptingSpreadPercent = Math.Abs(acceptingSpreadPercent);
40  _targetsCollection = new PortfolioTargetCollection();
41  }
42 
43  /// <summary>
44  /// Submit orders for the specified portfolio targets if the spread is tighter/equal to preset level
45  /// </summary>
46  /// <param name="algorithm">The algorithm instance</param>
47  /// <param name="targets">The portfolio targets to be ordered</param>
48  public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets)
49  {
50  // update the complete set of portfolio targets with the new targets
51  _targetsCollection.AddRange(targets);
52 
53  // for performance we check count value, OrderByMarginImpact and ClearFulfilled are expensive to call
54  if (!_targetsCollection.IsEmpty)
55  {
56  foreach (var target in _targetsCollection.OrderByMarginImpact(algorithm))
57  {
58  var symbol = target.Symbol;
59  // calculate remaining quantity to be ordered
60  var unorderedQuantity = OrderSizing.GetUnorderedQuantity(algorithm, target);
61 
62  if (unorderedQuantity != 0)
63  {
64  // get security object
65  var security = algorithm.Securities[symbol];
66 
67  // check order entry conditions
68  if (PriceIsFavorable(security))
69  {
70  algorithm.MarketOrder(symbol, unorderedQuantity);
71  }
72  }
73  }
74 
75  _targetsCollection.ClearFulfilled(algorithm);
76  }
77  }
78 
79  /// <summary>
80  /// Determines if the current spread is equal or tighter than preset level
81  /// </summary>
82  protected virtual bool PriceIsFavorable(Security security)
83  {
84  // Has to be in opening hours of exchange to avoid extreme spread in OTC period
85  // Price has to be larger than zero to avoid zero division error, or negative price causing the spread percentage lower than preset value by accident
86  return security.Exchange.ExchangeOpen
87  && security.Price > 0 && security.AskPrice > 0 && security.BidPrice > 0
88  && (security.AskPrice - security.BidPrice) / security.Price <= _acceptingSpreadPercent;
89  }
90  }
91 }