Lean  $LEAN_TAG$
RangeConsolidator.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 Python.Runtime;
19 using System;
20 
22 {
23  /// <summary>
24  /// This consolidator can transform a stream of <see cref="IBaseData"/> instances into a stream of <see cref="RangeBar"/>
25  /// </summary>
27  {
28  private bool _firstTick;
29  private decimal _minimumPriceVariation;
30 
31  /// <summary>
32  /// Symbol properties database to use to get the minimum price variation of certain symbol
33  /// </summary>
34  private static SymbolPropertiesDatabase _symbolPropertiesDatabase = SymbolPropertiesDatabase.FromDataFolder();
35 
36  /// <summary>
37  /// Bar being created
38  /// </summary>
39  protected override RangeBar CurrentBar { get; set; }
40 
41  /// <summary>
42  /// Range for each RangeBar, this is, the difference between the High and Low for each
43  /// RangeBar
44  /// </summary>
45  public decimal RangeSize { get; private set; }
46 
47  /// <summary>
48  /// Number of MinimumPriceVariation units
49  /// </summary>
50  public int Range { get; private set; }
51 
52  /// <summary>
53  /// Gets <see cref="RangeBar"/> which is the type emitted in the <see cref="IDataConsolidator.DataConsolidated"/> event.
54  /// </summary>
55  public override Type OutputType => typeof(RangeBar);
56 
57  /// <summary>
58  /// Gets a clone of the data being currently consolidated
59  /// </summary>
60  public override IBaseData WorkingData => CurrentBar?.Clone();
61 
62  /// <summary>
63  /// Initializes a new instance of the <see cref="RangeConsolidator" /> class.
64  /// </summary>
65  /// <param name="range">The Range interval sets the range in which the price moves, which in turn initiates the formation of a new bar.
66  /// One range equals to one minimum price change, where this last value is defined depending of the RangeBar's symbol</param>
67  /// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RangeBar"/>. The default
68  /// value is (x => x.Value) the <see cref="IBaseData.Value"/> property on <see cref="IBaseData"/></param>
69  /// <param name="volumeSelector">Extracts the volume from a data instance. The default value is null which does
70  /// not aggregate volume per bar, except if the input is a TradeBar.</param>
72  int range,
73  Func<IBaseData, decimal> selector = null,
74  Func<IBaseData, decimal> volumeSelector = null)
75  : base(selector, volumeSelector)
76  {
77  Range = range;
78  _firstTick = true;
79  }
80 
81  /// <summary>
82  /// Initializes a new instance of the <see cref="RangeConsolidator" /> class.
83  /// </summary>
84  /// <param name="range">The Range interval sets the range in which the price moves, which in turn initiates the formation of a new bar.
85  /// One range equals to one minimum price change, where this last value is defined depending of the RangeBar's symbol</param>
86  /// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RangeBar"/>. The default
87  /// value is (x => x.Value) the <see cref="IBaseData.Value"/> property on <see cref="IBaseData"/></param>
88  /// <param name="volumeSelector">Extracts the volume from a data instance. The default value is null which does
89  /// not aggregate volume per bar.</param>
90  public RangeConsolidator(int range,
91  PyObject selector,
92  PyObject volumeSelector = null)
93  : base(selector, volumeSelector)
94  {
95  Range = range;
96  _firstTick = true;
97  }
98 
99  /// <summary>
100  /// Resets the consolidator
101  /// </summary>
102  public override void Reset()
103  {
104  base.Reset();
105  _firstTick = true;
106  _minimumPriceVariation = 0m;
107  RangeSize = 0m;
108  }
109 
110  /// <summary>
111  /// Updates the current RangeBar being created with the given data.
112  /// Additionally, if it's the case, it consolidates the current RangeBar
113  /// </summary>
114  /// <param name="time">Time of the given data</param>
115  /// <param name="currentValue">Value of the given data</param>
116  /// <param name="volume">Volume of the given data</param>
117  protected override void UpdateBar(DateTime time, decimal currentValue, decimal volume)
118  {
119  bool isRising = default;
120  if (currentValue > CurrentBar.High)
121  {
122  isRising = true;
123  }
124  else if (currentValue < CurrentBar.Low)
125  {
126  isRising = false;
127  }
128 
129  CurrentBar.Update(time, currentValue, volume);
130  while (CurrentBar.IsClosed)
131  {
133  CurrentBar = new RangeBar(CurrentBar.Symbol, CurrentBar.EndTime, RangeSize, isRising ? CurrentBar.High + _minimumPriceVariation : CurrentBar.Low - _minimumPriceVariation);
134  CurrentBar.Update(time, currentValue, Math.Abs(CurrentBar.Low - currentValue) > RangeSize ? 0 : volume); // Intermediate/phantom RangeBar's have zero volume
135  }
136  }
137 
138  /// <summary>
139  /// Creates a new bar with the given data
140  /// </summary>
141  /// <param name="data">The new data for the bar</param>
142  /// <param name="currentValue">The new value for the bar</param>
143  /// <param name="volume">The new volume for the bar</param>
144  protected override void CreateNewBar(IBaseData data, decimal currentValue, decimal volume)
145  {
146  var open = currentValue;
147 
148  if (_firstTick)
149  {
150  _minimumPriceVariation = _symbolPropertiesDatabase.GetSymbolProperties(data.Symbol.ID.Market, data.Symbol, data.Symbol.ID.SecurityType, "USD").MinimumPriceVariation;
151  RangeSize = _minimumPriceVariation * Range;
152  open = Math.Ceiling(open / RangeSize) * RangeSize;
153  _firstTick = false;
154  }
155 
156  CurrentBar = new RangeBar(data.Symbol, data.Time, RangeSize, open, volume: volume);
157  }
158  }
159 }