Lean  $LEAN_TAG$
ConstantAlphaModel.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 System.Collections.Generic;
18 using QuantConnect.Data;
21 using static System.FormattableString;
22 
24 {
25  /// <summary>
26  /// Provides an implementation of <see cref="IAlphaModel"/> that always returns the same insight for each security
27  /// </summary>
29  {
30  private readonly InsightType _type;
31  private readonly InsightDirection _direction;
32  private readonly TimeSpan _period;
33  private readonly double? _magnitude;
34  private readonly double? _confidence;
35  private readonly double? _weight;
36  private readonly HashSet<Security> _securities;
37  private readonly Dictionary<Symbol, DateTime> _insightsTimeBySymbol;
38 
39  /// <summary>
40  /// Initializes a new instance of the <see cref="ConstantAlphaModel"/> class
41  /// </summary>
42  /// <param name="type">The type of insight</param>
43  /// <param name="direction">The direction of the insight</param>
44  /// <param name="period">The period over which the insight with come to fruition</param>
45  public ConstantAlphaModel(InsightType type, InsightDirection direction, TimeSpan period)
46  : this(type, direction, period, null, null)
47  {
48  }
49 
50  /// <summary>
51  /// Initializes a new instance of the <see cref="ConstantAlphaModel"/> class
52  /// </summary>
53  /// <param name="type">The type of insight</param>
54  /// <param name="direction">The direction of the insight</param>
55  /// <param name="period">The period over which the insight with come to fruition</param>
56  /// <param name="magnitude">The predicted change in magnitude as a +- percentage</param>
57  /// <param name="confidence">The confidence in the insight</param>
58  /// <param name="weight">The portfolio weight of the insights</param>
59  public ConstantAlphaModel(InsightType type, InsightDirection direction, TimeSpan period, double? magnitude, double? confidence, double? weight = null)
60  {
61  _type = type;
62  _direction = direction;
63  _period = period;
64 
65  // Optional
66  _magnitude = magnitude;
67  _confidence = confidence;
68  _weight = weight;
69 
70  _securities = new HashSet<Security>();
71  _insightsTimeBySymbol = new Dictionary<Symbol, DateTime>();
72 
73  Name = $"{nameof(ConstantAlphaModel)}({type},{direction},{period}";
74  if (magnitude.HasValue)
75  {
76  Name += Invariant($",{magnitude.Value}");
77  }
78 
79  if (confidence.HasValue)
80  {
81  Name += Invariant($",{confidence.Value}");
82  }
83 
84  Name += ")";
85  }
86 
87  /// <summary>
88  /// Creates a constant insight for each security as specified via the constructor
89  /// </summary>
90  /// <param name="algorithm">The algorithm instance</param>
91  /// <param name="data">The new data available</param>
92  /// <returns>The new insights generated</returns>
93  public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
94  {
95  foreach (var security in _securities)
96  {
97  // security price could be zero until we get the first data point. e.g. this could happen
98  // when adding both forex and equities, we will first get a forex data point
99  if (security.Price != 0
100  && ShouldEmitInsight(algorithm.UtcTime, security.Symbol))
101  {
102  yield return new Insight(security.Symbol, _period, _type, _direction, _magnitude, _confidence, weight: _weight);
103  }
104  }
105  }
106 
107  /// <summary>
108  /// Event fired each time the we add/remove securities from the data feed
109  /// </summary>
110  /// <param name="algorithm">The algorithm instance that experienced the change in securities</param>
111  /// <param name="changes">The security additions and removals from the algorithm</param>
112  public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
113  {
114  NotifiedSecurityChanges.UpdateCollection(_securities, changes);
115 
116  // this will allow the insight to be re-sent when the security re-joins the universe
117  foreach (var removed in changes.RemovedSecurities)
118  {
119  _insightsTimeBySymbol.Remove(removed.Symbol);
120  }
121  }
122 
123  /// <summary>
124  /// Determine if its time to emit insight for this symbol
125  /// </summary>
126  /// <param name="utcTime">Time of the insight</param>
127  /// <param name="symbol">The symbol to emit an insight for</param>
128  protected virtual bool ShouldEmitInsight(DateTime utcTime, Symbol symbol)
129  {
130  if(symbol.IsCanonical())
131  {
132  // canonical futures & options are none tradable
133  return false;
134  }
135  DateTime generatedTimeUtc;
136  if (_insightsTimeBySymbol.TryGetValue(symbol, out generatedTimeUtc))
137  {
138  // we previously emitted a insight for this symbol, check it's period to see
139  // if we should emit another insight
140  if (utcTime - generatedTimeUtc < _period)
141  {
142  return false;
143  }
144  }
145 
146  // we either haven't emitted a insight for this symbol or the previous
147  // insight's period has expired, so emit a new insight now for this symbol
148  _insightsTimeBySymbol[symbol] = utcTime;
149  return true;
150  }
151  }
152 }