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  /// Resets the ClassicRenkoConsolidator
124  /// </summary>
125  public override void Reset()
126  {
127  base.Reset();
128  _lastCloseValue = null;
129  }
130 
131  /// <summary>
132  /// Updates the current RangeBar being created with the given data.
133  /// Additionally, if it's the case, it consolidates the current RangeBar
134  /// </summary>
135  /// <param name="time">Time of the given data</param>
136  /// <param name="currentValue">Value of the given data</param>
137  /// <param name="volume">Volume of the given data</param>
138  protected override void UpdateBar(DateTime time, decimal currentValue, decimal volume)
139  {
140  CurrentBar.Update(time, currentValue, volume);
141 
142  if (CurrentBar.IsClosed)
143  {
144  _lastCloseValue = CurrentBar.Close;
146  CurrentBar = null;
147  }
148  }
149 
150  /// <summary>
151  /// Creates a new bar with the given data
152  /// </summary>
153  /// <param name="data">The new data for the bar</param>
154  /// <param name="currentValue">The new value for the bar</param>
155  /// <param name="volume">The new volume to the bar</param>
156  protected override void CreateNewBar(IBaseData data, decimal currentValue, decimal volume)
157  {
158  var open = _lastCloseValue ?? currentValue;
159  if (_evenBars && !_lastCloseValue.HasValue)
160  {
161  open = Math.Ceiling(open / _barSize) * _barSize;
162  }
163 
164  CurrentBar = new RenkoBar(data.Symbol, data.Time, _barSize, open, volume);
165  }
166 
167  private static void EpsilonCheck(decimal barSize)
168  {
169  if (barSize < Extensions.GetDecimalEpsilon())
170  {
171  throw new ArgumentOutOfRangeException(nameof(barSize),
172  "RenkoConsolidator bar size must be positve and greater than 1e-28");
173  }
174  }
175  }
176 
177  /// <summary>
178  /// 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
179  /// </summary>
180  /// <typeparam name="TInput"></typeparam>
182  where TInput : IBaseData
183  {
184  /// <summary>
185  /// Initializes a new instance of the <see cref="ClassicRenkoConsolidator" /> class.
186  /// </summary>
187  /// <param name="barSize">The size of each bar in units of the value produced by <paramref name="selector"/></param>
188  /// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RenkoBar"/>. The default
189  /// value is (x => x.Value) the <see cref="IBaseData.Value"/> property on <see cref="IBaseData"/></param>
190  /// <param name="volumeSelector">Extracts the volume from a data instance. The default value is null which does
191  /// not aggregate volume per bar.</param>
192  /// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
194  decimal barSize,
195  Func<TInput, decimal> selector,
196  Func<TInput, decimal> volumeSelector = null,
197  bool evenBars = true
198  )
199  : base(barSize, x => selector((TInput) x),
200  volumeSelector == null ? (Func<IBaseData, decimal>) null : x => volumeSelector((TInput) x), evenBars)
201  {
202  }
203 
204  /// <summary>
205  /// Initializes a new instance of the <see cref="ClassicRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
206  /// The value selector will by default select <see cref="IBaseData.Value"/>
207  /// The volume selector will by default select zero.
208  /// </summary>
209  /// <param name="barSize">The constant value size of each bar</param>
210  /// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
211  public ClassicRenkoConsolidator(decimal barSize, bool evenBars = true)
212  : this(barSize, x => x.Value, x => 0, evenBars)
213  {
214  }
215 
216  /// <summary>
217  /// Initializes a new instance of the <see cref="ClassicRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
218  /// The value selector will by default select <see cref="IBaseData.Value"/>
219  /// The volume selector will by default select zero.
220  /// </summary>
221  /// <param name="barSize">The constant value size of each bar</param>
222  /// <param name="type">The RenkoType of the bar</param>
223  [Obsolete("Please use the WickedRenkoConsolidator if RenkoType is not Classic")]
224  public ClassicRenkoConsolidator(decimal barSize, RenkoType type)
225  : base(barSize, type)
226  {
227  }
228 
229  /// <summary>
230  /// Updates this consolidator with the specified data.
231  /// </summary>
232  /// <remarks>
233  /// Type safe shim method.
234  /// </remarks>
235  /// <param name="data">The new data for the consolidator</param>
236  public void Update(TInput data)
237  {
238  base.Update(data);
239  }
240  }
241 }