/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Securities; using static System.FormattableString; namespace QuantConnect.Algorithm.Framework.Alphas { /// /// Provides an implementation of that always returns the same insight for each security /// public class ConstantAlphaModel : AlphaModel { private readonly InsightType _type; private readonly InsightDirection _direction; private readonly TimeSpan _period; private readonly double? _magnitude; private readonly double? _confidence; private readonly double? _weight; private readonly HashSet _securities; private readonly Dictionary _insightsTimeBySymbol; /// /// Initializes a new instance of the class /// /// The type of insight /// The direction of the insight /// The period over which the insight with come to fruition public ConstantAlphaModel(InsightType type, InsightDirection direction, TimeSpan period) : this(type, direction, period, null, null) { } /// /// Initializes a new instance of the class /// /// The type of insight /// The direction of the insight /// The period over which the insight with come to fruition /// The predicted change in magnitude as a +- percentage /// The confidence in the insight /// The portfolio weight of the insights public ConstantAlphaModel(InsightType type, InsightDirection direction, TimeSpan period, double? magnitude, double? confidence, double? weight = null) { _type = type; _direction = direction; _period = period; // Optional _magnitude = magnitude; _confidence = confidence; _weight = weight; _securities = new HashSet(); _insightsTimeBySymbol = new Dictionary(); Name = $"{nameof(ConstantAlphaModel)}({type},{direction},{period}"; if (magnitude.HasValue) { Name += Invariant($",{magnitude.Value}"); } if (confidence.HasValue) { Name += Invariant($",{confidence.Value}"); } Name += ")"; } /// /// Creates a constant insight for each security as specified via the constructor /// /// The algorithm instance /// The new data available /// The new insights generated public override IEnumerable Update(QCAlgorithm algorithm, Slice data) { foreach (var security in _securities) { // security price could be zero until we get the first data point. e.g. this could happen // when adding both forex and equities, we will first get a forex data point if (security.Price != 0 && ShouldEmitInsight(algorithm.UtcTime, security.Symbol)) { yield return new Insight(security.Symbol, _period, _type, _direction, _magnitude, _confidence, weight: _weight); } } } /// /// Event fired each time the we add/remove securities from the data feed /// /// The algorithm instance that experienced the change in securities /// The security additions and removals from the algorithm public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { NotifiedSecurityChanges.UpdateCollection(_securities, changes); // this will allow the insight to be re-sent when the security re-joins the universe foreach (var removed in changes.RemovedSecurities) { _insightsTimeBySymbol.Remove(removed.Symbol); } } /// /// Determine if its time to emit insight for this symbol /// /// Time of the insight /// The symbol to emit an insight for protected virtual bool ShouldEmitInsight(DateTime utcTime, Symbol symbol) { if(symbol.IsCanonical()) { // canonical futures & options are none tradable return false; } DateTime generatedTimeUtc; if (_insightsTimeBySymbol.TryGetValue(symbol, out generatedTimeUtc)) { // we previously emitted a insight for this symbol, check it's period to see // if we should emit another insight if (utcTime - generatedTimeUtc < _period) { return false; } } // we either haven't emitted a insight for this symbol or the previous // insight's period has expired, so emit a new insight now for this symbol _insightsTimeBySymbol[symbol] = utcTime; return true; } } }