Lean  $LEAN_TAG$
ClassicRenkoConsolidator.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 Python.Runtime;
19 
21 {
22  /// <summary>
23  /// This consolidator can transform a stream of <see cref="IBaseData"/> instances into a stream of <see cref="RenkoBar"/>
24  /// </summary>
26  {
27  private decimal _barSize;
28  private bool _evenBars;
29  private decimal? _lastCloseValue;
30 
31  /// <summary>
32  /// Bar being created
33  /// </summary>
34  protected override RenkoBar CurrentBar { get; set; }
35 
36  /// <summary>
37  /// Gets the kind of the bar
38  /// </summary>
39  public RenkoType Type => RenkoType.Classic;
40 
41  /// <summary>
42  /// Gets a clone of the data being currently consolidated
43  /// </summary>
44  public override IBaseData WorkingData => CurrentBar?.Clone();
45 
46  /// <summary>
47  /// Gets <see cref="RenkoBar"/> which is the type emitted in the <see cref="IDataConsolidator.DataConsolidated"/> event.
48  /// </summary>
49  public override Type OutputType => typeof(RenkoBar);
50 
51  /// <summary>
52  /// Initializes a new instance of the <see cref="ClassicRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
53  /// The value selector will by default select <see cref="IBaseData.Value"/>
54  /// The volume selector will by default select zero.
55  /// </summary>
56  /// <param name="barSize">The constant value size of each bar</param>
57  /// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
58  public ClassicRenkoConsolidator(decimal barSize, bool evenBars = true)
59  : base()
60  {
61  EpsilonCheck(barSize);
62  _barSize = barSize;
63  _evenBars = evenBars;
64  }
65 
66  /// <summary>
67  /// Initializes a new instance of the <see cref="ClassicRenkoConsolidator" /> class.
68  /// </summary>
69  /// <param name="barSize">The size of each bar in units of the value produced by <paramref name="selector"/></param>
70  /// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RenkoBar"/>. The default
71  /// value is (x => x.Value) the <see cref="IBaseData.Value"/> property on <see cref="IBaseData"/></param>
72  /// <param name="volumeSelector">Extracts the volume from a data instance. The default value is null which does
73  /// not aggregate volume per bar.</param>
74  /// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
76  decimal barSize,
77  Func<IBaseData, decimal> selector,
78  Func<IBaseData, decimal> volumeSelector = null,
79  bool evenBars = true)
80  : base(selector, volumeSelector)
81  {
82  EpsilonCheck(barSize);
83  _barSize = barSize;
84  _evenBars = evenBars;
85  }
86 
87  /// <summary>
88  ///Initializes a new instance of the <see cref="ClassicRenkoConsolidator" /> class.
89  /// </summary>
90  /// <param name="barSize">The constant value size of each bar</param>
91  /// <param name="type">The RenkoType of the bar</param>
92  [Obsolete("Please use the new RenkoConsolidator if RenkoType is not Classic")]
93  public ClassicRenkoConsolidator(decimal barSize, RenkoType type)
94  : this(barSize, true)
95  {
96  if (type != RenkoType.Classic)
97  {
98  throw new ArgumentException("Please use the new RenkoConsolidator type if RenkoType is not Classic");
99  }
100  }
101 
102  /// <summary>
103  /// Initializes a new instance of the <see cref="ClassicRenkoConsolidator" /> class.
104  /// </summary>
105  /// <param name="barSize">The size of each bar in units of the value produced by <paramref name="selector"/></param>
106  /// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RenkoBar"/>. The default
107  /// value is (x => x.Value) the <see cref="IBaseData.Value"/> property on <see cref="IBaseData"/></param>
108  /// <param name="volumeSelector">Extracts the volume from a data instance. The default value is null which does
109  /// not aggregate volume per bar.</param>
110  /// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
111  public ClassicRenkoConsolidator(decimal barSize,
112  PyObject selector,
113  PyObject volumeSelector = null,
114  bool evenBars = true)
115  : base(selector, volumeSelector)
116  {
117  EpsilonCheck(barSize);
118  _barSize = barSize;
119  _evenBars = evenBars;
120  }
121 
122  /// <summary>
123  /// Updates the current RangeBar being created with the given data.
124  /// Additionally, if it's the case, it consolidates the current RangeBar
125  /// </summary>
126  /// <param name="time">Time of the given data</param>
127  /// <param name="currentValue">Value of the given data</param>
128  /// <param name="volume">Volume of the given data</param>
129  protected override void UpdateBar(DateTime time, decimal currentValue, decimal volume)
130  {
131  CurrentBar.Update(time, currentValue, volume);
132 
133  if (CurrentBar.IsClosed)
134  {
135  _lastCloseValue = CurrentBar.Close;
137  CurrentBar = null;
138  }
139  }
140 
141  /// <summary>
142  /// Creates a new bar with the given data
143  /// </summary>
144  /// <param name="data">The new data for the bar</param>
145  /// <param name="currentValue">The new value for the bar</param>
146  /// <param name="volume">The new volume to the bar</param>
147  protected override void CreateNewBar(IBaseData data, decimal currentValue, decimal volume)
148  {
149  var open = _lastCloseValue ?? currentValue;
150  if (_evenBars && !_lastCloseValue.HasValue)
151  {
152  open = Math.Ceiling(open / _barSize) * _barSize;
153  }
154 
155  CurrentBar = new RenkoBar(data.Symbol, data.Time, _barSize, open, volume);
156  }
157 
158  private static void EpsilonCheck(decimal barSize)
159  {
160  if (barSize < Extensions.GetDecimalEpsilon())
161  {
162  throw new ArgumentOutOfRangeException(nameof(barSize),
163  "RenkoConsolidator bar size must be positve and greater than 1e-28");
164  }
165  }
166  }
167 
168  /// <summary>
169  /// Provides a type safe wrapper on the RenkoConsolidator class. This just allows us to define our selector functions with the real type they'll be receiving
170  /// </summary>
171  /// <typeparam name="TInput"></typeparam>
173  where TInput : IBaseData
174  {
175  /// <summary>
176  /// Initializes a new instance of the <see cref="ClassicRenkoConsolidator" /> class.
177  /// </summary>
178  /// <param name="barSize">The size of each bar in units of the value produced by <paramref name="selector"/></param>
179  /// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RenkoBar"/>. The default
180  /// value is (x => x.Value) the <see cref="IBaseData.Value"/> property on <see cref="IBaseData"/></param>
181  /// <param name="volumeSelector">Extracts the volume from a data instance. The default value is null which does
182  /// not aggregate volume per bar.</param>
183  /// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
185  decimal barSize,
186  Func<TInput, decimal> selector,
187  Func<TInput, decimal> volumeSelector = null,
188  bool evenBars = true
189  )
190  : base(barSize, x => selector((TInput) x),
191  volumeSelector == null ? (Func<IBaseData, decimal>) null : x => volumeSelector((TInput) x), evenBars)
192  {
193  }
194 
195  /// <summary>
196  /// Initializes a new instance of the <see cref="ClassicRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
197  /// The value selector will by default select <see cref="IBaseData.Value"/>
198  /// The volume selector will by default select zero.
199  /// </summary>
200  /// <param name="barSize">The constant value size of each bar</param>
201  /// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
202  public ClassicRenkoConsolidator(decimal barSize, bool evenBars = true)
203  : this(barSize, x => x.Value, x => 0, evenBars)
204  {
205  }
206 
207  /// <summary>
208  /// Initializes a new instance of the <see cref="ClassicRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
209  /// The value selector will by default select <see cref="IBaseData.Value"/>
210  /// The volume selector will by default select zero.
211  /// </summary>
212  /// <param name="barSize">The constant value size of each bar</param>
213  /// <param name="type">The RenkoType of the bar</param>
214  [Obsolete("Please use the WickedRenkoConsolidator if RenkoType is not Classic")]
215  public ClassicRenkoConsolidator(decimal barSize, RenkoType type)
216  : base(barSize, type)
217  {
218  }
219 
220  /// <summary>
221  /// Updates this consolidator with the specified data.
222  /// </summary>
223  /// <remarks>
224  /// Type safe shim method.
225  /// </remarks>
226  /// <param name="data">The new data for the consolidator</param>
227  public void Update(TInput data)
228  {
229  base.Update(data);
230  }
231  }
232 }