/*
* 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;
}
}
}