/* * 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 Newtonsoft.Json; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Interfaces; namespace QuantConnect { /// /// Contains insight population run time statistics /// public class AlphaRuntimeStatistics { private DateTime _startDate; private double _daysCompleted; // this is only used when deserializing to this type since it represents a computed property dependent on internal state private decimal _overrideEstimatedMonthlyAlphaValue; private readonly IAccountCurrencyProvider _accountCurrencyProvider; private decimal _fitnessScore; private decimal _portfolioTurnover; private decimal _returnOverMaxDrawdown; private decimal _sortinoRatio; /// /// Creates a new instance /// public AlphaRuntimeStatistics(IAccountCurrencyProvider accountCurrencyProvider) { _accountCurrencyProvider = accountCurrencyProvider; } /// /// Default constructor /// /// Required for proper deserialization public AlphaRuntimeStatistics() { } /// /// Gets the mean scores for the entire population of insights /// public InsightScore MeanPopulationScore { get; } = new InsightScore(); /// /// Gets the 100 insight ema of insight scores /// public InsightScore RollingAveragedPopulationScore { get; } = new InsightScore(); /// /// Gets the total number of insights with an up direction /// public long LongCount { get; set; } /// /// Gets the total number of insights with a down direction /// public long ShortCount { get; set; } /// /// The ratio of over /// public decimal LongShortRatio => ShortCount == 0 ? 1m : LongCount / (decimal) ShortCount; /// /// The total accumulated estimated value of trading all insights /// public decimal TotalAccumulatedEstimatedAlphaValue { get; set; } /// /// Score of the strategy's performance, and suitability for the Alpha Stream Market /// /// See https://www.quantconnect.com/research/3bc40ecee68d36a9424fbd1b338eb227. /// For performance we only truncate when the value is gotten public decimal FitnessScore { get { return _fitnessScore.TruncateTo3DecimalPlaces(); } set { _fitnessScore = value; } } /// /// Measurement of the strategies trading activity with respect to the portfolio value. /// Calculated as the sales volume with respect to the average total portfolio value. /// /// For performance we only truncate when the value is gotten public decimal PortfolioTurnover { get { return _portfolioTurnover.TruncateTo3DecimalPlaces(); } set { _portfolioTurnover = value; } } /// /// Provides a risk adjusted way to factor in the returns and drawdown of the strategy. /// It is calculated by dividing the Portfolio Annualized Return by the Maximum Drawdown seen during the backtest. /// /// For performance we only truncate when the value is gotten public decimal ReturnOverMaxDrawdown { get { return _returnOverMaxDrawdown.TruncateTo3DecimalPlaces(); } set { _returnOverMaxDrawdown = value; } } /// /// Gives a relative picture of the strategy volatility. /// It is calculated by taking a portfolio's annualized rate of return and subtracting the risk free rate of return. /// /// For performance we only truncate when the value is gotten public decimal SortinoRatio { get { return _sortinoRatio.TruncateTo3DecimalPlaces(); } set { _sortinoRatio = value; } } /// /// Suggested Value of the Alpha On A Monthly Basis For Licensing /// [JsonProperty] public decimal EstimatedMonthlyAlphaValue { get { if (_daysCompleted == 0) { return _overrideEstimatedMonthlyAlphaValue; } return (TotalAccumulatedEstimatedAlphaValue / (decimal) _daysCompleted) * 30; } private set { _overrideEstimatedMonthlyAlphaValue = value; } } /// /// The total number of insight signals generated by the algorithm /// public long TotalInsightsGenerated { get; set; } /// /// The total number of insight signals generated by the algorithm /// public long TotalInsightsClosed { get; set; } /// /// The total number of insight signals generated by the algorithm /// public long TotalInsightsAnalysisCompleted { get; set; } /// /// Gets the mean estimated insight value /// public decimal MeanPopulationEstimatedInsightValue => TotalInsightsClosed > 0 ? TotalAccumulatedEstimatedAlphaValue / TotalInsightsClosed : 0; /// /// Creates a dictionary containing the statistics /// public Dictionary ToDictionary() { var accountCurrencySymbol = Currencies.GetCurrencySymbol(_accountCurrencyProvider?.AccountCurrency ?? Currencies.USD); return new Dictionary { {"Fitness Score", $"{FitnessScore}"}, {"Sortino Ratio", $"{SortinoRatio}"}, {"Return Over Maximum Drawdown", $"{ReturnOverMaxDrawdown}"}, {"Portfolio Turnover", $"{PortfolioTurnover}"}, {"Total Insights Generated", $"{TotalInsightsGenerated}"}, {"Total Insights Closed", $"{TotalInsightsClosed}"}, {"Total Insights Analysis Completed", $"{TotalInsightsAnalysisCompleted}"}, {"Long Insight Count", $"{LongCount}"}, {"Short Insight Count", $"{ShortCount}"}, {"Long/Short Ratio", $"{Math.Round(100*LongShortRatio, 2)}%"}, {"Estimated Monthly Alpha Value", $"{accountCurrencySymbol}{EstimatedMonthlyAlphaValue.SmartRounding()}"}, {"Total Accumulated Estimated Alpha Value", $"{accountCurrencySymbol}{TotalAccumulatedEstimatedAlphaValue.SmartRounding()}"}, {"Mean Population Estimated Insight Value", $"{accountCurrencySymbol}{MeanPopulationEstimatedInsightValue.SmartRounding()}"}, {"Mean Population Direction", $"{Math.Round(100 * MeanPopulationScore.Direction, 4)}%"}, {"Mean Population Magnitude", $"{Math.Round(100 * MeanPopulationScore.Magnitude, 4)}%"}, {"Rolling Averaged Population Direction", $"{Math.Round(100 * RollingAveragedPopulationScore.Direction, 4)}%"}, {"Rolling Averaged Population Magnitude", $"{Math.Round(100 * RollingAveragedPopulationScore.Magnitude, 4)}%"}, }; } /// /// Set the current date of the backtest /// /// public void SetDate(DateTime now) { _daysCompleted = (now - _startDate).TotalDays; } /// /// Set the date range of the statistics /// /// public void SetStartDate(DateTime algorithmStartDate) { _startDate = algorithmStartDate; } } }