예제 #1
0
        /// <summary>
        /// Strategy long/short exposure by asset class
        /// </summary>
        /// <param name="equityCurve">Equity curve</param>
        /// <param name="orders">Orders of the strategy</param>
        /// <param name="direction">Long or short</param>
        /// <returns>
        /// Frame keyed by <see cref="SecurityType"/> and <see cref="OrderDirection"/>.
        /// Returns a Frame of exposure per asset per direction over time
        /// </returns>
        public static Frame <DateTime, Tuple <SecurityType, OrderDirection> > Exposure(Series <DateTime, double> equityCurve, List <Order> orders, OrderDirection direction)
        {
            if (equityCurve.IsEmpty || orders.Count == 0)
            {
                return(Frame.CreateEmpty <DateTime, Tuple <SecurityType, OrderDirection> >());
            }

            return(Exposure(PortfolioLooper.FromOrders(equityCurve, orders).ToList(), direction));
        }
예제 #2
0
        /// <summary>
        /// Utility method to get historical data for a list of securities
        /// </summary>
        /// <param name="securities">List of securities you want historical data for</param>
        /// <param name="start">Start date of the history request</param>
        /// <param name="end">End date of the history request</param>
        /// <param name="resolution">Resolution of the history request</param>
        /// <returns>Enumerable of slices</returns>
        public static IEnumerable <Slice> GetHistory(List <Security> securities, DateTime start, DateTime end, Resolution resolution)
        {
            var looper = new PortfolioLooper(0, new List <Order>(), resolution);

            looper.Algorithm.SetStartDate(start);
            looper.Algorithm.SetEndDate(end);

            return(GetHistory(looper.Algorithm, securities, resolution));
        }
예제 #3
0
        /// <summary>
        /// Calculates the portfolio's asset allocation percentage over time. The series used to call this extension function should
        /// be the equity curve with the associated <see cref="Order"/> objects that go along with it.
        /// </summary>
        /// <param name="equityCurve">Equity curve series</param>
        /// <param name="orders">Orders associated with the equity curve</param>
        /// <returns></returns>
        public static Series <Symbol, double> AssetAllocations(Series <DateTime, double> equityCurve, List <Order> orders)
        {
            if (equityCurve.IsEmpty || orders.Count == 0)
            {
                return(new Series <Symbol, double>(new Symbol[] { }, new double[] { }));
            }

            // Convert PointInTimePortfolios to List because for some reason our AbsoluteHoldingsValue is multiplied by two whenever we GroupBy on the raw IEnumerable
            return(AssetAllocations(PortfolioLooper.FromOrders(equityCurve, orders).ToList()));
        }
        /// <summary>
        /// Gets the point in time portfolio over multiple deployments
        /// </summary>
        /// <param name="equityCurve">Equity curve series</param>
        /// <param name="orders">Orders</param>
        /// <returns>Enumerable of <see cref="PointInTimePortfolio"/></returns>
        public static IEnumerable <PointInTimePortfolio> FromOrders(Series <DateTime, double> equityCurve, IEnumerable <Order> orders)
        {
            // Don't do anything if we have no orders or equity curve to process
            if (!orders.Any() || equityCurve.IsEmpty)
            {
                yield break;
            }

            // Chunk different deployments into separate Lists for separate processing
            var portfolioDeployments = new List <List <Order> >();

            // Orders are guaranteed to start counting from 1. This ensures that we have
            // no collision at all with the start of a deployment
            var previousOrderId   = 0;
            var currentDeployment = new List <Order>();

            // Make use of reference semantics to add new deployments to the list
            portfolioDeployments.Add(currentDeployment);

            foreach (var order in orders)
            {
                // In case we have two different deployments with only a single
                // order in the deployments, <= was chosen because it covers duplicate values
                if (order.Id <= previousOrderId)
                {
                    currentDeployment = new List <Order>();
                    portfolioDeployments.Add(currentDeployment);
                }

                currentDeployment.Add(order);
                previousOrderId = order.Id;
            }

            PointInTimePortfolio prev = null;

            foreach (var deploymentOrders in portfolioDeployments)
            {
                // For every deployment, we want to start fresh.
                var looper = new PortfolioLooper(
                    equityCurve.Where(kvp => kvp.Key <= deploymentOrders.First().Time).LastValue(),
                    deploymentOrders
                    );

                foreach (var portfolio in looper.ProcessOrders(deploymentOrders))
                {
                    prev = portfolio;
                    yield return(portfolio);
                }
            }

            if (prev != null)
            {
                yield return(new PointInTimePortfolio(prev, equityCurve.LastKey()));
            }
        }
예제 #5
0
        /// <summary>
        /// Calculates the leverage used from trades. The series used to call this extension function should
        /// be the equity curve with the associated <see cref="Order"/> objects that go along with it.
        /// </summary>
        /// <param name="equityCurve">Equity curve series</param>
        /// <param name="orders">Orders associated with the equity curve</param>
        /// <returns>Leverage utilization over time</returns>
        public static Series <DateTime, double> LeverageUtilization(Series <DateTime, double> equityCurve, List <Order> orders)
        {
            if (equityCurve.IsEmpty || orders.Count == 0)
            {
                return(new Series <DateTime, double>(new DateTime[] { }, new double[] { }));
            }

            var pointInTimePortfolios = PortfolioLooper.FromOrders(equityCurve, orders)
                                        .ToList(); // Required because for some reason our AbsoluteHoldingsValue is multiplied by two whenever we GroupBy on the raw IEnumerable

            return(LeverageUtilization(pointInTimePortfolios));
        }
예제 #6
0
        /// <summary>
        /// Create beautiful HTML and PDF Reports based on backtest and live data.
        /// </summary>
        /// <param name="name">Name of the strategy</param>
        /// <param name="description">Description of the strategy</param>
        /// <param name="version">Version number of the strategy</param>
        /// <param name="backtest">Backtest result object</param>
        /// <param name="live">Live result object</param>
        public Report(string name, string description, string version, BacktestResult backtest, LiveResult live)
        {
            var backtestCurve = new Series <DateTime, double>(ResultsUtil.EquityPoints(backtest));
            var liveCurve     = new Series <DateTime, double>(ResultsUtil.EquityPoints(live));

            var backtestOrders = backtest?.Orders?.Values.ToList() ?? new List <Order>();
            var liveOrders     = live?.Orders?.Values.ToList() ?? new List <Order>();

            Log.Trace($"QuantConnect.Report.Report(): Processing backtesting orders");
            var backtestPortfolioInTime = PortfolioLooper.FromOrders(backtestCurve, backtestOrders).ToList();

            Log.Trace($"QuantConnect.Report.Report(): Processing live orders");
            var livePortfolioInTime = PortfolioLooper.FromOrders(liveCurve, liveOrders, liveSeries: true).ToList();

            _elements = new List <IReportElement>
            {
                //Basics
                new TextReportElement("strategy name", ReportKey.StrategyName, name),
                new TextReportElement("description", ReportKey.StrategyDescription, description),
                new TextReportElement("version", ReportKey.StrategyVersion, version),
                new TextReportElement("stylesheet", ReportKey.Stylesheet, File.ReadAllText("css/report.css")),
                new TextReportElement("live marker key", ReportKey.LiveMarker, live == null ? string.Empty : "Live "),

                //KPI's Backtest:
                new DaysLiveReportElement("days live kpi", ReportKey.DaysLive, live),
                new CAGRReportElement("cagr kpi", ReportKey.CAGR, backtest, live),
                new TurnoverReportElement("turnover kpi", ReportKey.Turnover, backtest, live),
                new MaxDrawdownReportElement("max drawdown kpi", ReportKey.MaxDrawdown, backtest, live),
                new KellyEstimateReportElement("kelly estimate kpi", ReportKey.KellyEstimate, backtest, live),
                new SharpeRatioReportElement("sharpe kpi", ReportKey.SharpeRatio, backtest, live),
                new PSRReportElement("psr kpi", ReportKey.PSR, backtest, live),
                new InformationRatioReportElement("ir kpi", ReportKey.InformationRatio, backtest, live),
                new MarketsReportElement("markets kpi", ReportKey.Markets, backtest, live),
                new TradesPerDayReportElement("trades per day kpi", ReportKey.TradesPerDay, backtest, live),

                // Generate and insert plots MonthlyReturnsReportElement
                new MonthlyReturnsReportElement("monthly return plot", ReportKey.MonthlyReturns, backtest, live),
                new CumulativeReturnsReportElement("cumulative returns", ReportKey.CumulativeReturns, backtest, live),
                new AnnualReturnsReportElement("annual returns", ReportKey.AnnualReturns, backtest, live),
                new ReturnsPerTradeReportElement("returns per trade", ReportKey.ReturnsPerTrade, backtest, live),
                new AssetAllocationReportElement("asset allocation over time pie chart", ReportKey.AssetAllocation, backtest, live, backtestPortfolioInTime, livePortfolioInTime),
                new DrawdownReportElement("drawdown plot", ReportKey.Drawdown, backtest, live),
                //new DailyReturnsReportElement("daily returns plot", ReportKey.DailyReturns, backtest, live),
                //new RollingPortfolioBetaReportElement("rolling beta to equities plot", ReportKey.RollingBeta, backtest, live),
                //new RollingSharpeReportElement("rolling sharpe ratio plot", ReportKey.RollingSharpe, backtest, live),
                //new LeverageUtilizationReportElement("leverage plot", ReportKey.LeverageUtilization, backtest, live, backtestPortfolioInTime, livePortfolioInTime),
                //new ExposureReportElement("exposure plot", ReportKey.Exposure, backtest, live, backtestPortfolioInTime, livePortfolioInTime),

                // Array of Crisis Plots:
                new CrisisReportElement("crisis page", ReportKey.CrisisPageStyle, backtest, live),
                new CrisisReportElement("crisis plots", ReportKey.CrisisPlots, backtest, live)
            };
        }
예제 #7
0
        /// <summary>
        /// Gets the history for the given symbols from the <paramref name="start"/> to the <paramref name="end"/>
        /// </summary>
        /// <param name="symbols">Symbols to request history for</param>
        /// <param name="start">Start date of history request</param>
        /// <param name="end">End date of history request</param>
        /// <param name="resolution">Resolution of history request</param>
        /// <returns>Enumerable of slices</returns>
        public static IEnumerable <Slice> GetHistory(List <Symbol> symbols, DateTime start, DateTime end, Resolution resolution)
        {
            // Handles the conversion of Symbol to Security for us.
            var looper     = new PortfolioLooper(0, new List <Order>(), resolution);
            var securities = new List <Security>();

            looper.Algorithm.SetStartDate(start);
            looper.Algorithm.SetEndDate(end);

            foreach (var symbol in symbols)
            {
                var configs = looper.Algorithm.SubscriptionManager.SubscriptionDataConfigService.Add(symbol, resolution, false, false);
                securities.Add(looper.Algorithm.Securities.CreateSecurity(symbol, configs));
            }

            return(GetHistory(looper.Algorithm, securities, resolution));
        }
예제 #8
0
        /// <summary>
        /// Gets the point in time portfolio over multiple deployments
        /// </summary>
        /// <param name="equityCurve">Equity curve series</param>
        /// <param name="orders">Orders</param>
        /// <param name="liveSeries">Equity curve series originates from LiveResult</param>
        /// <returns>Enumerable of <see cref="PointInTimePortfolio"/></returns>
        public static IEnumerable <PointInTimePortfolio> FromOrders(Series <DateTime, double> equityCurve, IEnumerable <Order> orders, bool liveSeries = false)
        {
            // Don't do anything if we have no orders or equity curve to process
            if (!orders.Any() || equityCurve.IsEmpty)
            {
                yield break;
            }

            // Chunk different deployments into separate Lists for separate processing
            var portfolioDeployments = new List <List <Order> >();

            // Orders are guaranteed to start counting from 1. This ensures that we have
            // no collision at all with the start of a deployment
            var previousOrderId   = 0;
            var currentDeployment = new List <Order>();

            // Make use of reference semantics to add new deployments to the list
            portfolioDeployments.Add(currentDeployment);

            foreach (var order in orders)
            {
                // In case we have two different deployments with only a single
                // order in the deployments, <= was chosen because it covers duplicate values
                if (order.Id <= previousOrderId)
                {
                    currentDeployment = new List <Order>();
                    portfolioDeployments.Add(currentDeployment);
                }

                currentDeployment.Add(order);
                previousOrderId = order.Id;
            }

            PortfolioLooper      looper = null;
            PointInTimePortfolio prev   = null;

            foreach (var deploymentOrders in portfolioDeployments)
            {
                if (deploymentOrders.Count == 0)
                {
                    Log.Trace($"PortfolioLooper.FromOrders(): Deployment contains no orders");
                    continue;
                }
                var startTime  = deploymentOrders.First().Time;
                var deployment = equityCurve.Where(kvp => kvp.Key <= startTime);
                if (deployment.IsEmpty)
                {
                    Log.Trace($"PortfolioLooper.FromOrders(): Equity series is empty after filtering with upper bound: {startTime}");
                    continue;
                }

                // Skip any deployments that haven't been ran long enough to be generated in live mode
                if (liveSeries && deploymentOrders.First().Time.Date == deploymentOrders.Last().Time.Date)
                {
                    Log.Trace("PortfolioLooper.FromOrders(): Filtering deployment because it has not been deployed for more than one day");
                    continue;
                }

                // For every deployment, we want to start fresh.
                looper = new PortfolioLooper(deployment.LastValue(), deploymentOrders);

                foreach (var portfolio in looper.ProcessOrders(deploymentOrders))
                {
                    prev = portfolio;
                    yield return(portfolio);
                }
            }

            if (prev != null)
            {
                yield return(new PointInTimePortfolio(prev, equityCurve.LastKey()));
            }

            looper.DisposeSafely();
        }
예제 #9
0
        /// <summary>
        /// Create beautiful HTML and PDF Reports based on backtest and live data.
        /// </summary>
        /// <param name="name">Name of the strategy</param>
        /// <param name="description">Description of the strategy</param>
        /// <param name="version">Version number of the strategy</param>
        /// <param name="backtest">Backtest result object</param>
        /// <param name="live">Live result object</param>
        /// <param name="pointInTimePortfolioDestination">Point in time portfolio json output base filename</param>
        public Report(string name, string description, string version, BacktestResult backtest, LiveResult live, string pointInTimePortfolioDestination = null)
        {
            var backtestCurve = new Series <DateTime, double>(ResultsUtil.EquityPoints(backtest));
            var liveCurve     = new Series <DateTime, double>(ResultsUtil.EquityPoints(live));

            var backtestOrders = backtest?.Orders?.Values.ToList() ?? new List <Order>();
            var liveOrders     = live?.Orders?.Values.ToList() ?? new List <Order>();

            Log.Trace($"QuantConnect.Report.Report(): Processing backtesting orders");
            var backtestPortfolioInTime = PortfolioLooper.FromOrders(backtestCurve, backtestOrders).ToList();

            Log.Trace($"QuantConnect.Report.Report(): Processing live orders");
            var livePortfolioInTime = PortfolioLooper.FromOrders(liveCurve, liveOrders, liveSeries: true).ToList();

            var destination = pointInTimePortfolioDestination ?? Config.Get("report-destination");

            if (!string.IsNullOrWhiteSpace(destination))
            {
                if (backtestPortfolioInTime.Count != 0)
                {
                    var dailyBacktestPortfolioInTime = backtestPortfolioInTime
                                                       .Select(x => new PointInTimePortfolio(x, x.Time.Date).NoEmptyHoldings())
                                                       .GroupBy(x => x.Time.Date)
                                                       .Select(kvp => kvp.Last())
                                                       .OrderBy(x => x.Time)
                                                       .ToList();

                    var outputFile = destination.Replace(".html", string.Empty) + "-backtesting-portfolio.json";
                    Log.Trace($"Report.Report(): Writing backtest point-in-time portfolios to JSON file: {outputFile}");
                    var backtestPortfolioOutput = JsonConvert.SerializeObject(dailyBacktestPortfolioInTime);
                    File.WriteAllText(outputFile, backtestPortfolioOutput);
                }
                if (livePortfolioInTime.Count != 0)
                {
                    var dailyLivePortfolioInTime = livePortfolioInTime
                                                   .Select(x => new PointInTimePortfolio(x, x.Time.Date).NoEmptyHoldings())
                                                   .GroupBy(x => x.Time.Date)
                                                   .Select(kvp => kvp.Last())
                                                   .OrderBy(x => x.Time)
                                                   .ToList();

                    var outputFile = destination.Replace(".html", string.Empty) + "-live-portfolio.json";
                    Log.Trace($"Report.Report(): Writing live point-in-time portfolios to JSON file: {outputFile}");
                    var livePortfolioOutput = JsonConvert.SerializeObject(dailyLivePortfolioInTime);
                    File.WriteAllText(outputFile, livePortfolioOutput);
                }
            }

            _elements = new List <IReportElement>
            {
                //Basics
                new TextReportElement("strategy name", ReportKey.StrategyName, name),
                new TextReportElement("description", ReportKey.StrategyDescription, description),
                new TextReportElement("version", ReportKey.StrategyVersion, version),
                new TextReportElement("stylesheet", ReportKey.Stylesheet, File.ReadAllText("css/report.css")),
                new TextReportElement("live marker key", ReportKey.LiveMarker, live == null ? string.Empty : "Live "),

                //KPI's Backtest:
                new DaysLiveReportElement("days live kpi", ReportKey.DaysLive, live),
                new CAGRReportElement("cagr kpi", ReportKey.CAGR, backtest, live),
                new TurnoverReportElement("turnover kpi", ReportKey.Turnover, backtest, live),
                new MaxDrawdownReportElement("max drawdown kpi", ReportKey.MaxDrawdown, backtest, live),
                new KellyEstimateReportElement("kelly estimate kpi", ReportKey.KellyEstimate, backtest, live),
                new SharpeRatioReportElement("sharpe kpi", ReportKey.SharpeRatio, backtest, live),
                new PSRReportElement("psr kpi", ReportKey.PSR, backtest, live),
                new InformationRatioReportElement("ir kpi", ReportKey.InformationRatio, backtest, live),
                new MarketsReportElement("markets kpi", ReportKey.Markets, backtest, live),
                new TradesPerDayReportElement("trades per day kpi", ReportKey.TradesPerDay, backtest, live),

                // Generate and insert plots MonthlyReturnsReportElement
                new MonthlyReturnsReportElement("monthly return plot", ReportKey.MonthlyReturns, backtest, live),
                new CumulativeReturnsReportElement("cumulative returns", ReportKey.CumulativeReturns, backtest, live),
                new AnnualReturnsReportElement("annual returns", ReportKey.AnnualReturns, backtest, live),
                new ReturnsPerTradeReportElement("returns per trade", ReportKey.ReturnsPerTrade, backtest, live),
                new AssetAllocationReportElement("asset allocation over time pie chart", ReportKey.AssetAllocation, backtest, live, backtestPortfolioInTime, livePortfolioInTime),
                new DrawdownReportElement("drawdown plot", ReportKey.Drawdown, backtest, live),
                new DailyReturnsReportElement("daily returns plot", ReportKey.DailyReturns, backtest, live),
                new RollingPortfolioBetaReportElement("rolling beta to equities plot", ReportKey.RollingBeta, backtest, live),
                new RollingSharpeReportElement("rolling sharpe ratio plot", ReportKey.RollingSharpe, backtest, live),
                new LeverageUtilizationReportElement("leverage plot", ReportKey.LeverageUtilization, backtest, live, backtestPortfolioInTime, livePortfolioInTime),
                new ExposureReportElement("exposure plot", ReportKey.Exposure, backtest, live, backtestPortfolioInTime, livePortfolioInTime),

                // Array of Crisis Plots:
                new CrisisReportElement("crisis page", ReportKey.CrisisPageStyle, backtest, live),
                new CrisisReportElement("crisis plots", ReportKey.CrisisPlots, backtest, live)
            };
        }