| Overall Statistics |
|
Total Trades 358 Average Win 1.60% Average Loss -1.39% Compounding Annual Return 18.422% Drawdown 31.900% Expectancy 0.392 Net Profit 168.360% Sharpe Ratio 0.806 Probabilistic Sharpe Ratio 24.252% Loss Rate 35% Win Rate 65% Profit-Loss Ratio 1.15 Alpha 0.059 Beta 0.876 Annual Standard Deviation 0.175 Annual Variance 0.031 Information Ratio 0.465 Tracking Error 0.103 Treynor Ratio 0.161 Total Fees $1261.33 Estimated Strategy Capacity $13000000.00 Lowest Capacity Asset KBE TDP0JIUCTNJ9 |
#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Storage;
using QuantConnect.Data.Custom.AlphaStreams;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
namespace QuantConnect.Algorithm.CSharp
{
public class WellDressedVioletChinchilla : QCAlgorithm
{
private List<BrainSentimentIndicatorUniverse> _brainUniverseData;
private Dictionary<Symbol, IEnumerable<ETFConstituentData>> _etfConstituentsDataBySymbol = new();
private Dictionary<Symbol, decimal?> _scoreBySector = new();
private int _numEtfs = 3;
private Dictionary<DateTime, decimal> _portfolioValues = new();
private bool _rebalance = false;
public override void Initialize()
{
SetStartDate(2017, 1, 1);
SetEndDate(2022, 11, 1);
SetCash(100000);
AddUniverse<BrainSentimentIndicatorUniverse>(
"BrainSentimentIndicatorUniverse", Resolution.Daily,
altCoarse =>
{
_brainUniverseData = altCoarse.ToList();
return Enumerable.Empty<Symbol>();
});
// Sector ETFs from https://www.cnbc.com/sector-etfs/
var tickers = new[]
{
"XLE", // Energy Select Sector SPDR Fund
"XLF", // Financial Select Sector SPDR Fund
"XLU", // Utilities Select Sector SPDR Fund
"XLI", // Industrial Select Sector SPDR Fund
"GDX", // VanEck Gold Miners ETF
"XLK", // Technology Select Sector SPDR Fund
"XLV", // Health Care Select Sector SPDR Fund
"XLY", // Consumer Discretionary Select Sector SPDR Fund
"XLP", // Consumer Staples Select Sector SPDR Fund
"XLB", // Materials Select Sector SPDR Fund
"XOP", // Spdr S&P Oil & Gas Exploration & Production Etf
"IYR", // iShares U.S. Real Estate ETF
"XHB", // Spdr S&P Homebuilders Etf
"ITB", // iShares U.S. Home Construction ETF
"VNQ", // Vanguard Real Estate Index Fund ETF Shares
"GDXJ",// VanEck Junior Gold Miners ETF
"IYE", // iShares U.S. Energy ETF
"OIH", // VanEck Oil Services ETF
"XME", // SPDR S&P Metals & Mining ETF
"XRT", // Spdr S&P Retail Etf
"SMH", // VanEck Semiconductor ETF
"IBB", // iShares Biotechnology ETF
"KBE", // SPDR S&P Bank ETF
"KRE", // SPDR S&P Regional Banking ETF
"XTL" // SPDR S&P Telecom ETF
};
foreach (var ticker in tickers)
{
var etfSymbol = AddEquity(ticker, Resolution.Daily).Symbol;
AddUniverse(Universe.ETF(etfSymbol, Market.USA, UniverseSettings,
constituents =>
{
_etfConstituentsDataBySymbol[etfSymbol] = constituents;
return Enumerable.Empty<Symbol>();
})
);
}
// Schedule the rebalance
Schedule.On(DateRules.MonthStart(GetParameter("rebalance-day", 0)),
TimeRules.Midnight,
() =>
{
_rebalance = true;
});
}
public override void OnData(Slice data)
{
// Record net portfolio value
_portfolioValues[Time] = Portfolio.TotalPortfolioValue;
// Rebalance?
if (!_rebalance)
{
return;
}
_rebalance = false;
// Calculate sector sentiment
foreach (var kvp in _etfConstituentsDataBySymbol)
{
var etfSymbol = kvp.Key;
var etfConstituentsData = kvp.Value;
var sectorSymbols = etfConstituentsData.Select(etfConstituentsData => etfConstituentsData.Symbol).ToHashSet();
var etfWeightBySymbol = sectorSymbols.ToDictionary(
sectorSymbol => sectorSymbol,
sectorSymbol => etfConstituentsData.Where(etfConstituentsData => etfConstituentsData.Symbol == sectorSymbol).FirstOrDefault().Weight
);
_scoreBySector[etfSymbol] = _brainUniverseData
.Where(brainSentiment => sectorSymbols.Contains(brainSentiment.Symbol))
.Select(brainSentiment => brainSentiment.Sentiment30Days * etfWeightBySymbol[brainSentiment.Symbol])
.Sum();
}
// Select target ETFs
var targetSymbols = _scoreBySector.OrderByDescending(x => x.Value).Take(_numEtfs).Select(kvp => kvp.Key);;
// Liquidate ETFs that are no longer targeted
SetHoldings(Portfolio.Where(kvp => kvp.Value.Invested && !targetSymbols.Contains(kvp.Key)).Select(kvp => new PortfolioTarget(kvp.Key, 0)).ToList());
// Rebalance targeted ETFs
var weight = 1.0m / _numEtfs;
SetHoldings(targetSymbols.Select(symbol => new PortfolioTarget(symbol, weight)).ToList());
}
public override void OnEndOfAlgorithm()
{
// Save daily portfolio values to ObjectStore
ObjectStore.SaveJson<Dictionary<DateTime, decimal>>($"{ProjectId}/portfolioValues", _portfolioValues);
}
}
}