Lean  $LEAN_TAG$
FluentScheduledEventBuilder.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 System.Collections.Generic;
19 using System.Linq;
20 using NodaTime;
22 
24 {
25  /// <summary>
26  /// Provides a builder class to allow for fluent syntax when constructing new events
27  /// </summary>
28  /// <remarks>
29  /// This builder follows the following steps for event creation:
30  ///
31  /// 1. Specify an event name (optional)
32  /// 2. Specify an IDateRule
33  /// 3. Specify an ITimeRule
34  /// a. repeat 3. to define extra time rules (optional)
35  /// 4. Specify additional where clause (optional)
36  /// 5. Register event via call to Run
37  /// </remarks>
39  {
40  private IDateRule _dateRule;
41  private ITimeRule _timeRule;
42  private Func<DateTime, bool> _predicate;
43 
44  private readonly string _name;
45  private readonly ScheduleManager _schedule;
46  private readonly SecurityManager _securities;
47 
48  /// <summary>
49  /// Initializes a new instance of the <see cref="FluentScheduledEventBuilder"/> class
50  /// </summary>
51  /// <param name="schedule">The schedule to send created events to</param>
52  /// <param name="securities">The algorithm's security manager</param>
53  /// <param name="name">A specific name for this event</param>
54  public FluentScheduledEventBuilder(ScheduleManager schedule, SecurityManager securities, string name = null)
55  {
56  _name = name;
57  _schedule = schedule;
58  _securities = securities;
59  }
60 
61  private FluentScheduledEventBuilder SetTimeRule(ITimeRule rule)
62  {
63  // if it's not set, just set it
64  if (_timeRule == null)
65  {
66  _timeRule = rule;
67  return this;
68  }
69 
70  // if it's already a composite, open it up and make a new composite
71  // prevent nesting composites
72  var compositeTimeRule = _timeRule as CompositeTimeRule;
73  if (compositeTimeRule != null)
74  {
75  var rules = compositeTimeRule.Rules;
76  _timeRule = new CompositeTimeRule(rules.Concat(new[] { rule }));
77  return this;
78  }
79 
80  // create a composite from the existing rule and the new rules
81  _timeRule = new CompositeTimeRule(_timeRule, rule);
82  return this;
83  }
84 
85  #region DateRules and TimeRules delegation
86 
87  /// <summary>
88  /// Creates events on each of the specified day of week
89  /// </summary>
90  IFluentSchedulingTimeSpecifier IFluentSchedulingDateSpecifier.Every(params DayOfWeek[] days)
91  {
92  _dateRule = _schedule.DateRules.Every(days);
93  return this;
94  }
95 
96  /// <summary>
97  /// Creates events on every day of the year
98  /// </summary>
99  IFluentSchedulingTimeSpecifier IFluentSchedulingDateSpecifier.EveryDay()
100  {
101  _dateRule = _schedule.DateRules.EveryDay();
102  return this;
103  }
104 
105  /// <summary>
106  /// Creates events on every trading day of the year for the symbol
107  /// </summary>
108  IFluentSchedulingTimeSpecifier IFluentSchedulingDateSpecifier.EveryDay(Symbol symbol)
109  {
110  _dateRule = _schedule.DateRules.EveryDay(symbol);
111  return this;
112  }
113 
114  /// <summary>
115  /// Creates events on the first day of the month
116  /// </summary>
117  IFluentSchedulingTimeSpecifier IFluentSchedulingDateSpecifier.MonthStart()
118  {
119  _dateRule = _schedule.DateRules.MonthStart();
120  return this;
121  }
122 
123  /// <summary>
124  /// Creates events on the first trading day of the month
125  /// </summary>
126  IFluentSchedulingTimeSpecifier IFluentSchedulingDateSpecifier.MonthStart(Symbol symbol)
127  {
128  _dateRule = _schedule.DateRules.MonthStart(symbol);
129  return this;
130  }
131 
132  /// <summary>
133  /// Filters the event times using the predicate
134  /// </summary>
135  IFluentSchedulingTimeSpecifier IFluentSchedulingDateSpecifier.Where(Func<DateTime, bool> predicate)
136  {
137  _predicate = _predicate == null
138  ? predicate
139  : (time => _predicate(time) && predicate(time));
140  return this;
141  }
142 
143  /// <summary>
144  /// Creates events that fire at the specific time of day in the algorithm's time zone
145  /// </summary>
146  IFluentSchedulingRunnable IFluentSchedulingTimeSpecifier.At(TimeSpan timeOfDay)
147  {
148  return SetTimeRule(_schedule.TimeRules.At(timeOfDay));
149  }
150 
151  /// <summary>
152  /// Creates events that fire a specified number of minutes after market open
153  /// </summary>
154  IFluentSchedulingRunnable IFluentSchedulingTimeSpecifier.AfterMarketOpen(Symbol symbol, double minutesAfterOpen, bool extendedMarketOpen)
155  {
156  return SetTimeRule(_schedule.TimeRules.AfterMarketOpen(symbol, minutesAfterOpen, extendedMarketOpen));
157  }
158 
159  /// <summary>
160  /// Creates events that fire a specified numer of minutes before market close
161  /// </summary>
162  IFluentSchedulingRunnable IFluentSchedulingTimeSpecifier.BeforeMarketClose(Symbol symbol, double minuteBeforeClose, bool extendedMarketClose)
163  {
164  return SetTimeRule(_schedule.TimeRules.BeforeMarketClose(symbol, minuteBeforeClose, extendedMarketClose));
165  }
166 
167  /// <summary>
168  /// Creates events that fire on a period define by the specified interval
169  /// </summary>
170  IFluentSchedulingRunnable IFluentSchedulingTimeSpecifier.Every(TimeSpan interval)
171  {
172  return SetTimeRule(_schedule.TimeRules.Every(interval));
173  }
174 
175  /// <summary>
176  /// Filters the event times using the predicate
177  /// </summary>
178  IFluentSchedulingTimeSpecifier IFluentSchedulingTimeSpecifier.Where(Func<DateTime, bool> predicate)
179  {
180  _predicate = _predicate == null
181  ? predicate
182  : (time => _predicate(time) && predicate(time));
183  return this;
184  }
185 
186  /// <summary>
187  /// Register the defined event with the callback
188  /// </summary>
189  ScheduledEvent IFluentSchedulingRunnable.Run(Action callback)
190  {
191  return ((IFluentSchedulingRunnable)this).Run((name, time) => callback());
192  }
193 
194  /// <summary>
195  /// Register the defined event with the callback
196  /// </summary>
197  ScheduledEvent IFluentSchedulingRunnable.Run(Action<DateTime> callback)
198  {
199  return ((IFluentSchedulingRunnable)this).Run((name, time) => callback(time));
200  }
201 
202  /// <summary>
203  /// Register the defined event with the callback
204  /// </summary>
205  ScheduledEvent IFluentSchedulingRunnable.Run(Action<string, DateTime> callback)
206  {
207  var name = _name ?? _dateRule.Name + ": " + _timeRule.Name;
208  // back the date up to ensure we get all events, the event scheduler will skip past events that whose time has passed
209  var dates = ScheduleManager.GetDatesDeferred(_dateRule, _securities);
210  var eventTimes = _timeRule.CreateUtcEventTimes(dates);
211  if (_predicate != null)
212  {
213  eventTimes = eventTimes.Where(_predicate);
214  }
215  var scheduledEvent = new ScheduledEvent(name, eventTimes, callback);
216  _schedule.Add(scheduledEvent);
217  return scheduledEvent;
218  }
219 
220  /// <summary>
221  /// Filters the event times using the predicate
222  /// </summary>
223  IFluentSchedulingRunnable IFluentSchedulingRunnable.Where(Func<DateTime, bool> predicate)
224  {
225  _predicate = _predicate == null
226  ? predicate
227  : (time => _predicate(time) && predicate(time));
228  return this;
229  }
230 
231  /// <summary>
232  /// Filters the event times to only include times where the symbol's market is considered open
233  /// </summary>
234  IFluentSchedulingRunnable IFluentSchedulingRunnable.DuringMarketHours(Symbol symbol, bool extendedMarket)
235  {
236  var security = GetSecurity(symbol);
237  Func<DateTime, bool> predicate = time =>
238  {
239  var localTime = time.ConvertFromUtc(security.Exchange.TimeZone);
240  return security.Exchange.IsOpenDuringBar(localTime, localTime, extendedMarket);
241  };
242  _predicate = _predicate == null
243  ? predicate
244  : (time => _predicate(time) && predicate(time));
245  return this;
246  }
247 
248  IFluentSchedulingTimeSpecifier IFluentSchedulingDateSpecifier.On(int year, int month, int day)
249  {
250  _dateRule = _schedule.DateRules.On(year, month, day);
251  return this;
252  }
253 
254  IFluentSchedulingTimeSpecifier IFluentSchedulingDateSpecifier.On(params DateTime[] dates)
255  {
256  _dateRule = _schedule.DateRules.On(dates);
257  return this;
258  }
259 
260  IFluentSchedulingRunnable IFluentSchedulingTimeSpecifier.At(int hour, int minute, int second)
261  {
262  return SetTimeRule(_schedule.TimeRules.At(hour, minute, second));
263  }
264 
265  IFluentSchedulingRunnable IFluentSchedulingTimeSpecifier.At(int hour, int minute, DateTimeZone timeZone)
266  {
267  return SetTimeRule(_schedule.TimeRules.At(hour, minute, 0, timeZone));
268  }
269 
270  IFluentSchedulingRunnable IFluentSchedulingTimeSpecifier.At(int hour, int minute, int second, DateTimeZone timeZone)
271  {
272  return SetTimeRule(_schedule.TimeRules.At(hour, minute, second, timeZone));
273  }
274 
275  IFluentSchedulingRunnable IFluentSchedulingTimeSpecifier.At(TimeSpan timeOfDay, DateTimeZone timeZone)
276  {
277  return SetTimeRule(_schedule.TimeRules.At(timeOfDay, timeZone));
278  }
279 
280  private Security GetSecurity(Symbol symbol)
281  {
282  Security security;
283  if (!_securities.TryGetValue(symbol, out security))
284  {
285  throw new KeyNotFoundException($"{symbol} not found in portfolio. Request this data when initializing the algorithm.");
286  }
287 
288  return security;
289  }
290 
291  #endregion
292  }
293 
294  /// <summary>
295  /// Specifies the date rule component of a scheduled event
296  /// </summary>
298  {
299  /// <summary>
300  /// Filters the event times using the predicate
301  /// </summary>
302  IFluentSchedulingTimeSpecifier Where(Func<DateTime, bool> predicate);
303  /// <summary>
304  /// Creates events only on the specified date
305  /// </summary>
306  IFluentSchedulingTimeSpecifier On(int year, int month, int day);
307  /// <summary>
308  /// Creates events only on the specified dates
309  /// </summary>
310  IFluentSchedulingTimeSpecifier On(params DateTime[] dates);
311  /// <summary>
312  /// Creates events on each of the specified day of week
313  /// </summary>
314  IFluentSchedulingTimeSpecifier Every(params DayOfWeek[] days);
315  /// <summary>
316  /// Creates events on every day of the year
317  /// </summary>
319  /// <summary>
320  /// Creates events on every trading day of the year for the symbol
321  /// </summary>
323  /// <summary>
324  /// Creates events on the first day of the month
325  /// </summary>
327  /// <summary>
328  /// Creates events on the first trading day of the month
329  /// </summary>
331  }
332 
333  /// <summary>
334  /// Specifies the time rule component of a scheduled event
335  /// </summary>
337  {
338  /// <summary>
339  /// Filters the event times using the predicate
340  /// </summary>
341  IFluentSchedulingTimeSpecifier Where(Func<DateTime, bool> predicate);
342  /// <summary>
343  /// Creates events that fire at the specified time of day in the specified time zone
344  /// </summary>
345  IFluentSchedulingRunnable At(int hour, int minute, int second = 0);
346  /// <summary>
347  /// Creates events that fire at the specified time of day in the specified time zone
348  /// </summary>
349  IFluentSchedulingRunnable At(int hour, int minute, DateTimeZone timeZone);
350  /// <summary>
351  /// Creates events that fire at the specified time of day in the specified time zone
352  /// </summary>
353  IFluentSchedulingRunnable At(int hour, int minute, int second, DateTimeZone timeZone);
354  /// <summary>
355  /// Creates events that fire at the specified time of day in the specified time zone
356  /// </summary>
357  IFluentSchedulingRunnable At(TimeSpan timeOfDay, DateTimeZone timeZone);
358  /// <summary>
359  /// Creates events that fire at the specific time of day in the algorithm's time zone
360  /// </summary>
361  IFluentSchedulingRunnable At(TimeSpan timeOfDay);
362  /// <summary>
363  /// Creates events that fire on a period define by the specified interval
364  /// </summary>
365  IFluentSchedulingRunnable Every(TimeSpan interval);
366  /// <summary>
367  /// Creates events that fire a specified number of minutes after market open
368  /// </summary>
369  IFluentSchedulingRunnable AfterMarketOpen(Symbol symbol, double minutesAfterOpen = 0, bool extendedMarketOpen = false);
370  /// <summary>
371  /// Creates events that fire a specified numer of minutes before market close
372  /// </summary>
373  IFluentSchedulingRunnable BeforeMarketClose(Symbol symbol, double minuteBeforeClose = 0, bool extendedMarketClose = false);
374  }
375 
376  /// <summary>
377  /// Specifies the callback component of a scheduled event, as well as final filters
378  /// </summary>
380  {
381  /// <summary>
382  /// Filters the event times using the predicate
383  /// </summary>
384  new IFluentSchedulingRunnable Where(Func<DateTime, bool> predicate);
385  /// <summary>
386  /// Filters the event times to only include times where the symbol's market is considered open
387  /// </summary>
388  IFluentSchedulingRunnable DuringMarketHours(Symbol symbol, bool extendedMarket = false);
389  /// <summary>
390  /// Register the defined event with the callback
391  /// </summary>
392  ScheduledEvent Run(Action callback);
393  /// <summary>
394  /// Register the defined event with the callback
395  /// </summary>
396  ScheduledEvent Run(Action<DateTime> callback);
397  /// <summary>
398  /// Register the defined event with the callback
399  /// </summary>
400  ScheduledEvent Run(Action<string, DateTime> callback);
401  }
402 }