Lean  $LEAN_TAG$
Beta.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;
18 using MathNet.Numerics.Statistics;
19 
21 {
22  /// <summary>
23  /// In technical analysis Beta indicator is used to measure volatility or risk of a target (ETF) relative to the overall
24  /// risk (volatility) of the reference (market indexes). The Beta indicators compares target's price movement to the
25  /// movements of the indexes over the same period of time.
26  ///
27  /// It is common practice to use the SPX index as a benchmark of the overall reference market when it comes to Beta
28  /// calculations.
29  /// </summary>
31  {
32  /// <summary>
33  /// RollingWindow to store the data points of the target symbol
34  /// </summary>
35  private readonly RollingWindow<decimal> _targetDataPoints;
36 
37  /// <summary>
38  /// RollingWindow to store the data points of the reference symbol
39  /// </summary>
40  private readonly RollingWindow<decimal> _referenceDataPoints;
41 
42  /// <summary>
43  /// Symbol of the reference used
44  /// </summary>
45  private readonly Symbol _referenceSymbol;
46 
47  /// <summary>
48  /// Symbol of the target used
49  /// </summary>
50  private readonly Symbol _targetSymbol;
51 
52  /// <summary>
53  /// RollingWindow of returns of the target symbol in the given period
54  /// </summary>
55  private readonly RollingWindow<double> _targetReturns;
56 
57  /// <summary>
58  /// RollingWindow of returns of the reference symbol in the given period
59  /// </summary>
60  private readonly RollingWindow<double> _referenceReturns;
61 
62  /// <summary>
63  /// Beta of the target used in relation with the reference
64  /// </summary>
65  private decimal _beta;
66 
67  /// <summary>
68  /// Required period, in data points, for the indicator to be ready and fully initialized.
69  /// </summary>
70  public int WarmUpPeriod { get; private set; }
71 
72  /// <summary>
73  /// Gets a flag indicating when the indicator is ready and fully initialized
74  /// </summary>
75  public override bool IsReady => _targetDataPoints.Samples >= WarmUpPeriod && _referenceDataPoints.Samples >= WarmUpPeriod;
76 
77  /// <summary>
78  /// Creates a new Beta indicator with the specified name, target, reference,
79  /// and period values
80  /// </summary>
81  /// <param name="name">The name of this indicator</param>
82  /// <param name="targetSymbol">The target symbol of this indicator</param>
83  /// <param name="period">The period of this indicator</param>
84  /// <param name="referenceSymbol">The reference symbol of this indicator</param>
85  public Beta(string name, Symbol targetSymbol, Symbol referenceSymbol, int period)
86  : base(name)
87  {
88  // Assert the period is greater than two, otherwise the beta can not be computed
89  if (period < 2)
90  {
91  throw new ArgumentException($"Period parameter for Beta indicator must be greater than 2 but was {period}");
92  }
93 
94  WarmUpPeriod = period + 1;
95  _referenceSymbol = referenceSymbol;
96  _targetSymbol = targetSymbol;
97 
98  _targetDataPoints = new RollingWindow<decimal>(2);
99  _referenceDataPoints = new RollingWindow<decimal>(2);
100 
101  _targetReturns = new RollingWindow<double>(period);
102  _referenceReturns = new RollingWindow<double>(period);
103  _beta = 0;
104  }
105 
106  /// <summary>
107  /// Creates a new Beta indicator with the specified target, reference,
108  /// and period values
109  /// </summary>
110  /// <param name="targetSymbol">The target symbol of this indicator</param>
111  /// <param name="period">The period of this indicator</param>
112  /// <param name="referenceSymbol">The reference symbol of this indicator</param>
113  public Beta(Symbol targetSymbol, Symbol referenceSymbol, int period)
114  : this($"B({period})", targetSymbol, referenceSymbol, period)
115  {
116  }
117 
118  /// <summary>
119  /// Creates a new Beta indicator with the specified name, period, target and
120  /// reference values
121  /// </summary>
122  /// <param name="name">The name of this indicator</param>
123  /// <param name="period">The period of this indicator</param>
124  /// <param name="targetSymbol">The target symbol of this indicator</param>
125  /// <param name="referenceSymbol">The reference symbol of this indicator</param>
126  /// <remarks>Constructor overload for backward compatibility.</remarks>
127  public Beta(string name, int period, Symbol targetSymbol, Symbol referenceSymbol)
128  : this(name, targetSymbol, referenceSymbol, period)
129  {
130  }
131 
132  /// <summary>
133  /// Computes the next value for this indicator from the given state.
134  ///
135  /// As this indicator is receiving data points from two different symbols,
136  /// it's going to compute the next value when the amount of data points
137  /// of each of them is the same. Otherwise, it will return the last beta
138  /// value computed
139  /// </summary>
140  /// <param name="input">The input value of this indicator on this time step.
141  /// It can be either from the target or the reference symbol</param>
142  /// <returns>The beta value of the target used in relation with the reference</returns>
143  protected override decimal ComputeNextValue(IBaseDataBar input)
144  {
145  var inputSymbol = input.Symbol;
146  if (inputSymbol == _targetSymbol)
147  {
148  _targetDataPoints.Add(input.Close);
149  }
150  else if(inputSymbol == _referenceSymbol)
151  {
152  _referenceDataPoints.Add(input.Close);
153  }
154  else
155  {
156  throw new ArgumentException("The given symbol was not target or reference symbol");
157  }
158 
159  if (_targetDataPoints.Samples == _referenceDataPoints.Samples && _referenceDataPoints.Count > 1)
160  {
161  _targetReturns.Add(GetNewReturn(_targetDataPoints));
162  _referenceReturns.Add(GetNewReturn(_referenceDataPoints));
163 
164  ComputeBeta();
165  }
166  return _beta;
167  }
168 
169  /// <summary>
170  /// Computes the returns with the new given data point and the last given data point
171  /// </summary>
172  /// <param name="rollingWindow">The collection of data points from which we want
173  /// to compute the return</param>
174  /// <returns>The returns with the new given data point</returns>
175  private static double GetNewReturn(RollingWindow<decimal> rollingWindow)
176  {
177  return (double) ((rollingWindow[0].SafeDivision(rollingWindow[1]) - 1));
178  }
179 
180  /// <summary>
181  /// Computes the beta value of the target in relation with the reference
182  /// using the target and reference returns
183  /// </summary>
184  private void ComputeBeta()
185  {
186  var varianceComputed = _referenceReturns.Variance();
187  var covarianceComputed = _targetReturns.Covariance(_referenceReturns);
188 
189  // Avoid division with NaN or by zero
190  var variance = !varianceComputed.IsNaNOrZero() ? varianceComputed : 1;
191  var covariance = !covarianceComputed.IsNaNOrZero() ? covarianceComputed : 0;
192  _beta = (decimal) (covariance / variance);
193  }
194 
195  /// <summary>
196  /// Resets this indicator to its initial state
197  /// </summary>
198  public override void Reset()
199  {
200  _targetDataPoints.Reset();
201  _referenceDataPoints.Reset();
202 
203  _targetReturns.Reset();
204  _referenceReturns.Reset();
205  _beta = 0;
206  base.Reset();
207  }
208  }
209 }