| Overall Statistics |
|
Total Trades 179 Average Win 1.33% Average Loss -1.17% Compounding Annual Return 21.810% Drawdown 20.100% Expectancy 0.714 Net Profit 114.521% Sharpe Ratio 1.051 Probabilistic Sharpe Ratio 47.700% Loss Rate 20% Win Rate 80% Profit-Loss Ratio 1.14 Alpha 0.062 Beta 0.729 Annual Standard Deviation 0.151 Annual Variance 0.023 Information Ratio 0.265 Tracking Error 0.095 Treynor Ratio 0.219 Total Fees $386.54 Estimated Strategy Capacity $14000000.00 Lowest Capacity Asset KBE TDP0JIUCTNJ9 |
using QuantConnect.Algorithm.Framework;
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.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Securities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QuantConnect.Algorithm.CSharp.Test
{
public class SectorMomentum : QCAlgorithm
{
List<string> _symbols = new List<string>()
{
"VNQ", // Vanguard Real Estate Index Fund
"XLK", // Technology Select Sector SPDR Fund
"XLE", // Energy Select Sector SPDR Fund
"XLV", // Health Care Select Sector SPDR Fund
"XLF", // Financial Select Sector SPDR Fund
"KBE", // SPDR S&P Bank ETF
"VAW", // Vanguard Materials ETF
"XLY", // Consumer Discretionary Select Sector SPDR Fund
"XLP", // Consumer Staples Select Sector SPDR Fund
"VGT", // Vanguard Information Technology ETFm
};
//private Dictionary<Symbol, Momentum> _momentums = new Dictionary<Symbol, Momentum>();
public override void Initialize()
{
SetStartDate(2018, 1, 1);
SetEndDate(DateTime.Now);
SetCash(100000);
int period = 3 * 21;
SetWarmup(period);
var symbols = new List<Symbol>();
foreach (var symbol in _symbols)
{
var equity = AddEquity(symbol, Resolution.Daily);
//_momentums.Add(symbol, MOM(symbol, period, Resolution.Daily));
symbols.Add(equity.Symbol);
}
AddUniverseSelection(new ManualUniverseSelectionModel(symbols));
AddAlpha(new SectorMomentumAlpha(this));
AddRiskManagement(new MaximumDrawdownPercentPerSecurity(0.1m));
AddRiskManagement(new MaximumUnrealizedProfitPercentPerSecurity(0.2m));
SetPortfolioConstruction(new SectorMomentumPortfolioConstruction(this));
SetExecution(new SectorMomentumExecutionModel());
}
#region Framework
public class SectorMomentumExecutionModel : ImmediateExecutionModel
{
public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets)
{
if (targets.Length > 0)
{
foreach (var target in targets)
{
algorithm.SetHoldings(target.Symbol, target.Quantity);
}
}
}
}
public class SectorMomentumPortfolioConstruction : SymbolDataNotifier, IPortfolioConstructionModel
{
public SectorMomentumPortfolioConstruction(QCAlgorithm algo) : base(algo){ }
public IEnumerable<IPortfolioTarget> CreateTargets(QCAlgorithm algorithm, Insight[] insights)
{
List<IPortfolioTarget> portfolioTargets = new List<IPortfolioTarget>();
if(insights.Length > 0)
{
var top3 = SymbolData.OrderByDescending(x => x.Momentum.Current.Value).Take(3);
foreach (var securityData in algorithm.Portfolio)
{
var security = securityData.Value;
if(security.Invested && !top3.Any(x => security.Symbol.Value == x.Symbol.Value))
{
portfolioTargets.Add(new PortfolioTarget(security.Symbol, 0));
}
}
foreach(var insight in insights)
{
PortfolioTarget target = new PortfolioTarget(insight.Symbol, 0.33m);
portfolioTargets.Add(target);
}
}
return portfolioTargets;
}
}
public class SectorMomentumAlpha : SymbolDataNotifier, IAlphaModel
{
public SectorMomentumAlpha(QCAlgorithm algo) : base(algo) { }
public IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
if (algorithm.IsWarmingUp || SymbolData.Count < 3 || !TimeToRebalance(algorithm)) return new List<Insight>();
List<Insight> insights = new List<Insight>();
algorithm.Log("Rebalancing");
var top3 = SymbolData.OrderByDescending(x => x.Momentum.Current.Value).Take(3);
foreach(var symbol in top3)
{
decimal percentage = 1m / top3.Count();
insights.Add(new Insight(symbol.Symbol, new TimeSpan(30, 0, 0, 0, 0), InsightType.Price, InsightDirection.Up));
}
ResetRebalanceTime(algorithm);
return insights;
}
}
#endregion
#region Utilities
public class SymbolDataNotifier : INotifiedSecurityChanges
{
private List<SectorMomentumSymbolData> _symbolData = new List<SectorMomentumSymbolData>();
protected IReadOnlyCollection<SectorMomentumSymbolData> SymbolData { get { return _symbolData; } }
private DateTime _lastRebalance;
public SymbolDataNotifier(QCAlgorithm algo) => _lastRebalance = algo.Time;
protected bool TimeToRebalance(QCAlgorithm algo) => _lastRebalance.AddMonths(1) <= algo.Time;
protected void ResetRebalanceTime(QCAlgorithm algo) => _lastRebalance = algo.Time;
public void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
if (changes == null)
return;
foreach (var added in changes.AddedSecurities)
{
SectorMomentumSymbolData symbolData = new SectorMomentumSymbolData(added.Symbol, algorithm);
_symbolData.Add(symbolData);
}
foreach (var removed in changes.RemovedSecurities)
{
if (_symbolData.Any(x => x.Symbol.Value == removed.Symbol.Value))
{
var symbolData = _symbolData.Single(x => x.Symbol.Value == removed.Symbol.Value);
_symbolData.Remove(symbolData);
}
}
}
}
public class SectorMomentumSymbolData
{
public Symbol Symbol { get; private set; }
public Momentum Momentum { get; private set; }
public SectorMomentumSymbolData(Symbol symbol, QCAlgorithm algo)
{
Symbol = symbol;
Momentum = algo.MOM(Symbol, 3 * 21, Resolution.Daily);
}
}
#endregion
}
}