Lean  $LEAN_TAG$
QuoteBarConsolidator.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 
17 using System;
18 using Python.Runtime;
20 
22 {
23  /// <summary>
24  /// Consolidates QuoteBars into larger QuoteBars
25  /// </summary>
26  public class QuoteBarConsolidator : PeriodCountConsolidatorBase<QuoteBar, QuoteBar>
27  {
28  /// <summary>
29  /// Initializes a new instance of the <see cref="QuoteBarConsolidator"/> class
30  /// </summary>
31  /// <param name="period">The minimum span of time before emitting a consolidated bar</param>
32  public QuoteBarConsolidator(TimeSpan period)
33  : base(period)
34  {
35  }
36 
37  /// <summary>
38  /// Initializes a new instance of the <see cref="QuoteBarConsolidator"/> class
39  /// </summary>
40  /// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
41  public QuoteBarConsolidator(int maxCount)
42  : base(maxCount)
43  {
44  }
45 
46  /// <summary>
47  /// Initializes a new instance of the <see cref="QuoteBarConsolidator"/> class
48  /// </summary>
49  /// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
50  /// <param name="period">The minimum span of time before emitting a consolidated bar</param>
51  public QuoteBarConsolidator(int maxCount, TimeSpan period)
52  : base(maxCount, period)
53  {
54  }
55 
56  /// <summary>
57  /// Creates a consolidator to produce a new 'QuoteBar' representing the last count pieces of data or the period, whichever comes first
58  /// </summary>
59  /// <param name="func">Func that defines the start time of a consolidated data</param>
60  public QuoteBarConsolidator(Func<DateTime, CalendarInfo> func)
61  : base(func)
62  {
63  }
64 
65  /// <summary>
66  /// Creates a consolidator to produce a new 'QuoteBar' representing the last count pieces of data or the period, whichever comes first
67  /// </summary>
68  /// <param name="pyfuncobj">Python function object that defines the start time of a consolidated data</param>
69  public QuoteBarConsolidator(PyObject pyfuncobj)
70  : base(pyfuncobj)
71  {
72  }
73 
74  /// <summary>
75  /// Aggregates the new 'data' into the 'workingBar'. The 'workingBar' will be
76  /// null following the event firing
77  /// </summary>
78  /// <param name="workingBar">The bar we're building, null if the event was just fired and we're starting a new consolidated bar</param>
79  /// <param name="data">The new data</param>
80  protected override void AggregateBar(ref QuoteBar workingBar, QuoteBar data)
81  {
82  var bid = data.Bid;
83  var ask = data.Ask;
84 
85  if (workingBar == null)
86  {
87  workingBar = new QuoteBar(GetRoundedBarTime(data), data.Symbol, null, 0, null, 0, IsTimeBased && Period.HasValue ? Period : data.Period);
88 
89  // open ask and bid should match previous close ask and bid
90  if (Consolidated != null)
91  {
92  // note that we will only fill forward previous close ask and bid when a new data point comes in and we generate a new working bar which is not a fill forward bar
93  var previous = Consolidated as QuoteBar;
94  workingBar.Update(0, previous.Bid?.Close ?? 0, previous.Ask?.Close ?? 0, 0, previous.LastBidSize, previous.LastAskSize);
95  }
96  }
97  else if (!IsTimeBased)
98  {
99  // we should only increment the period after the first data we get, else we would be accouting twice for the inital bars period
100  // because in the `if` above we are already providing the `data.Period` as argument. See test 'AggregatesNewCountQuoteBarProperly' which assert period
101  workingBar.Period += data.Period;
102  }
103 
104  // update the bid and ask
105  if (bid != null)
106  {
107  workingBar.LastBidSize = data.LastBidSize;
108  if (workingBar.Bid == null)
109  {
110  workingBar.Bid = new Bar(bid.Open, bid.High, bid.Low, bid.Close);
111  }
112  else
113  {
114  workingBar.Bid.Close = bid.Close;
115  if (workingBar.Bid.High < bid.High) workingBar.Bid.High = bid.High;
116  if (workingBar.Bid.Low > bid.Low) workingBar.Bid.Low = bid.Low;
117  }
118  }
119  if (ask != null)
120  {
121  workingBar.LastAskSize = data.LastAskSize;
122  if (workingBar.Ask == null)
123  {
124  workingBar.Ask = new Bar(ask.Open, ask.High, ask.Low, ask.Close);
125  }
126  else
127  {
128  workingBar.Ask.Close = ask.Close;
129  if (workingBar.Ask.High < ask.High) workingBar.Ask.High = ask.High;
130  if (workingBar.Ask.Low > ask.Low) workingBar.Ask.Low = ask.Low;
131  }
132  }
133 
134  workingBar.Value = data.Value;
135  }
136  }
137 }