/* * 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 QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Data; using QuantConnect.Interfaces; using System; using System.Collections.Generic; using System.Linq; namespace QuantConnect.Algorithm.CSharp.Alphas { /// /// Alpha Benchmark Strategy capitalizing on ETF rebalancing causing momentum during trending markets. /// Strategy by Prof. Shum, reposted by Ernie Chan. /// Source: http://epchan.blogspot.com/2012/10/a-leveraged-etfs-strategy.html /// /// /// /// public class RebalancingLeveragedETFAlpha : QCAlgorithm, IRegressionAlgorithmDefinition { private readonly List Groups = new List(); public override void Initialize() { SetStartDate(2017, 6, 1); SetEndDate(2018, 8, 1); SetCash(100000); var underlying = new List { "SPY", "QLD", "DIA", "IJR", "MDY", "IWM", "QQQ", "IYE", "EEM", "IYW", "EFA", "GAZB", "SLV", "IEF", "IYM", "IYF", "IYH", "IYR", "IYC", "IBB", "FEZ", "USO", "TLT" }; var ultraLong = new List { "SSO", "UGL", "DDM", "SAA", "MZZ", "UWM", "QLD", "DIG", "EET", "ROM", "EFO", "BOIL", "AGQ", "UST", "UYM", "UYG", "RXL", "URE", "UCC", "BIB", "ULE", "UCO", "UBT" }; var ultraShort = new List { "SDS", "GLL", "DXD", "SDD", "MVV", "TWM", "QID", "DUG", "EEV", "REW", "EFU", "KOLD", "ZSL", "PST", "SMN", "SKF", "RXD", "SRS", "SCC", "BIS", "EPV", "SCO", "TBT" }; for (var i = 0; i < underlying.Count; i++) { Groups.Add(new ETFGroup(AddEquity(underlying[i]).Symbol, AddEquity(ultraLong[i]).Symbol, AddEquity(ultraShort[i]).Symbol)); } // Manually curated universe SetUniverseSelection(new ManualUniverseSelectionModel()); // Select the demonstration alpha model SetAlpha(new RebalancingLeveragedETFAlphaModel(Groups)); // Select our default model types SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel()); // Equally weigh securities in portfolio, based on insights SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel()); // Set Immediate Execution Model SetExecution(new ImmediateExecutionModel()); // Set Null Risk Management Model SetRiskManagement(new NullRiskManagementModel()); } /// /// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm. /// public bool CanRunLocally { get; } = false; /// /// This is used by the regression test system to indicate which languages this algorithm is written in. /// public List Languages { get; } = new() { Language.CSharp, Language.Python }; /// /// Data Points count of all timeslices of algorithm /// public long DataPoints => 0; /// /// Data Points count of the algorithm history /// public int AlgorithmHistoryDataPoints => 0; /// /// Final status of the algorithm /// public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed; /// /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm /// public Dictionary ExpectedStatistics => new Dictionary { {"Total Orders", "2465"}, {"Average Win", "0.26%"}, {"Average Loss", "-0.24%"}, {"Compounding Annual Return", "7.848%"}, {"Drawdown", "17.500%"}, {"Expectancy", "0.035"}, {"Net Profit", "9.233%"}, {"Sharpe Ratio", "0.492"}, {"Loss Rate", "50%"}, {"Win Rate", "50%"}, {"Profit-Loss Ratio", "1.06"}, {"Alpha", "0.585"}, {"Beta", "-24.639"}, {"Annual Standard Deviation", "0.19"}, {"Annual Variance", "0.036"}, {"Information Ratio", "0.387"}, {"Tracking Error", "0.19"}, {"Treynor Ratio", "-0.004"}, {"Total Fees", "$9029.33"} }; } /// /// If the underlying ETF has experienced a return >= 1% since the previous day's close up to the current time at 14:15, /// then buy it's ultra ETF right away, and exit at the close. If the return is <= -1%, sell it's ultra-short ETF. /// class RebalancingLeveragedETFAlphaModel : AlphaModel { private DateTime _date; private readonly List _etfGroups; /// /// Create a new leveraged ETF rebalancing alpha /// public RebalancingLeveragedETFAlphaModel(List etfGroups) { _etfGroups = etfGroups; _date = DateTime.MinValue; Name = "RebalancingLeveragedETFAlphaModel"; } /// /// Scan to see if the returns are greater than 1% at 2.15pm to emit an insight. /// public override IEnumerable Update(QCAlgorithm algorithm, Slice data) { // Initialize: var insights = new List(); var magnitude = 0.0005; // Paper suggests leveraged ETF's rebalance from 2.15pm - to close // giving an insight period of 105 minutes. var period = TimeSpan.FromMinutes(105); if (algorithm.Time.Date != _date) { _date = algorithm.Time.Date; // Save yesterday's price and reset the signal. foreach (var group in _etfGroups) { var history = algorithm.History(group.Underlying, 1, Resolution.Daily); group.YesterdayClose = history.Select(x => x.Close).FirstOrDefault(); } } // Check if the returns are > 1% at 14.15 if (algorithm.Time.Hour == 14 && algorithm.Time.Minute == 15) { foreach (var group in _etfGroups) { if (group.YesterdayClose == 0) continue; var returns = (algorithm.Portfolio[group.Underlying].Price - group.YesterdayClose) / group.YesterdayClose; if (returns > 0.01m) { insights.Add(Insight.Price(group.UltraLong, period, InsightDirection.Up, magnitude)); } else if (returns < -0.01m) { insights.Add(Insight.Price(group.UltraShort, period, InsightDirection.Down, magnitude)); } } } return insights; } } class ETFGroup { public Symbol Underlying; public Symbol UltraLong; public Symbol UltraShort; public decimal YesterdayClose; /// /// Group the underlying ETF and it's ultra ETFs /// /// The underlying indexETF /// The long-leveraged version of underlying ETF /// The short-leveraged version of the underlying ETF public ETFGroup(Symbol underlying, Symbol ultraLong, Symbol ultraShort) { Underlying = underlying; UltraLong = ultraLong; UltraShort = ultraShort; } } }