| Overall Statistics |
|
Total Trades 205 Average Win 11.52% Average Loss -1.82% Compounding Annual Return 37.488% Drawdown 40.600% Expectancy 2.627 Net Profit 3314.240% Sharpe Ratio 1.047 Probabilistic Sharpe Ratio 37.126% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 6.33 Alpha 0.247 Beta 0.517 Annual Standard Deviation 0.284 Annual Variance 0.081 Information Ratio 0.707 Tracking Error 0.283 Treynor Ratio 0.575 Total Fees $1673.14 Estimated Strategy Capacity $370000.00 Lowest Capacity Asset UGL U85WJOCE24BP |
#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 static class HelperMethods
{
public static void SetupRsi(this Dictionary<string,RelativeStrengthIndex> collection, QCAlgorithm algorithm, string ticker, int periods)
{
var indicator = new RelativeStrengthIndex(periods);
algorithm.RegisterIndicator(ticker, indicator, TimeSpan.FromDays(1));
collection.Add(ticker, indicator);
}
}
public class CasualBrownFish : QCAlgorithm
{
private enum PortfolioDesign
{
RiskOn = 1,
RiskOffRisingRates,
RiskOffFallingRates
}
private readonly List<string> _tickers = new() {"BND", "BIL", "TECL", "TQQQ", "UPRO", "TMF", "TLT", "UUP", "QID", "TBF", "UGL", "BTAL", "XLP", "SPY"};
private readonly List<string> _cumulativeReturnTickers = new() { "BND", "BIL", "TLT" };
private readonly Dictionary<string,Equity> _equities = new();
private DateTime _lastEvaluation;
private DateTime _nextEvaluation;
private PortfolioDesign _lastPortfolioDesign = 0;
// RSI fund surfers
private readonly Dictionary<string,RelativeStrengthIndex> _riskOnEquities = new();
private readonly Dictionary<string,RelativeStrengthIndex> _riskOffEquities = new();
// Rolling windows of close prices
private readonly Dictionary<string,RollingWindow<decimal>> _closePrices = new();
public override void Initialize()
{
var startDate = new DateTime(2011, 9, 13, 13, 0, 0);
SetStartDate(startDate);
SetCash(10000);
SetBenchmark("SPY");
// Load ticker data feeds
foreach (var ticker in _tickers)
{
_equities.Add(ticker, AddEquity(ticker, Resolution.Hour));
var consolidator = new TradeBarConsolidator(TimeSpan.FromDays(1));
consolidator.DataConsolidated += ConsolidationHandler;
SubscriptionManager.AddConsolidator(_equities[ticker].Symbol, consolidator);
}
// Warm up price history
WarmUpPriceData("BND", 60);
WarmUpPriceData("BIL", 60);
WarmUpPriceData("TLT", 20);
// Set up RSI indicators
_riskOnEquities.SetupRsi(this, "TECL", 10);
_riskOnEquities.SetupRsi(this, "TQQQ", 10);
_riskOnEquities.SetupRsi(this, "UPRO", 10);
_riskOnEquities.SetupRsi(this, "TMF", 10);
_riskOffEquities.SetupRsi(this, "QID", 20);
_riskOffEquities.SetupRsi(this, "TBF", 20);
// Schedule symphony rule evaluations
Schedule.On(DateRules.EveryDay("SPY"),
TimeRules.At(15, 30, 0), // 3:30 PM
EvaluateSymphony);
}
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// Slice object keyed by symbol containing the stock data
public override void OnData(Slice data)
{
}
private DateTime CalculateNextEvaluationDate()
{
var rebalanceDates = new List<DateTime>
{
new DateTime(this.Time.Year, 4, 1),
new DateTime(this.Time.Year, 7, 1),
new DateTime(this.Time.Year, 10, 1),
new DateTime(this.Time.Year + 1, 1, 1),
}.OrderBy(d => d);
var thisDate = this.Time;
var nextEvaluationDate = thisDate >= rebalanceDates.Last()
? rebalanceDates.Last()
: thisDate <= rebalanceDates.First()
? rebalanceDates.First()
: rebalanceDates.First(d => d >= thisDate);
return nextEvaluationDate;
}
private bool ShouldEvaluateSymphony()
{
if (_lastEvaluation == default)
{
return true;
}
return this.Time.Date > _nextEvaluation;
}
private void EvaluateSymphony()
{
// Check all indicators are ready
if (_closePrices.Values.All(x => x.IsReady == true) &&
_riskOnEquities.Values.All(x => x.IsReady == true) &&
_riskOffEquities.Values.All(x => x.IsReady == true) &&
ShouldEvaluateSymphony()) // Trade according to the date rules
{
// Symphony algorithm
if (CumulativeReturn("BND", 60) > CumulativeReturn("BIL", 60))
{
// Risk ON - select lowest 3 RSI
var equities = _riskOnEquities
.OrderByDescending(x => x.Value.Current.Value)
.TakeLast(3)
.Select(x => x.Key)
.ToList();
SetHoldings(equities.Select(x =>
new PortfolioTarget(x, 1m / equities.Count)
).ToList(), liquidateExistingHoldings: _lastPortfolioDesign != PortfolioDesign.RiskOn);
_lastPortfolioDesign = PortfolioDesign.RiskOn;
}
else
{
if (CumulativeReturn("TLT", 20) < CumulativeReturn("BIL", 20))
{
// Risk OFF, Rising Rates - UUP at 50%, select lowest RSI
var equity = _riskOffEquities
.OrderByDescending(x => x.Value.Current.Value)
.TakeLast(1)
.Select(x => x.Key)
.First();
SetHoldings(new List<PortfolioTarget>() {
new PortfolioTarget("UUP", 0.5m),
new PortfolioTarget(equity, 0.5m)
}, liquidateExistingHoldings: _lastPortfolioDesign != PortfolioDesign.RiskOffRisingRates);
_lastPortfolioDesign = PortfolioDesign.RiskOffRisingRates;
}
else
{
// Risk OFF, Falling Rates
SetHoldings(new List<PortfolioTarget>() {
new PortfolioTarget("UGL", 0.25m),
new PortfolioTarget("TMF", 0.25m),
new PortfolioTarget("BTAL", 0.25m),
new PortfolioTarget("XLP", 0.25m)
}, liquidateExistingHoldings: _lastPortfolioDesign != PortfolioDesign.RiskOffFallingRates);
_lastPortfolioDesign = PortfolioDesign.RiskOffFallingRates;
}
}
// Update last evaluation
_lastEvaluation = this.Time.Date;
_nextEvaluation = CalculateNextEvaluationDate();
}
}
private decimal CumulativeReturn(string ticker, int periods)
{
var returns = _closePrices[ticker][0] / _closePrices[ticker][1];
for (int i = 1; i < periods - 2; i++)
{
returns *= _closePrices[ticker][i] / _closePrices[ticker][i+1];
}
return returns - 1;
}
private void ConsolidationHandler(object sender, TradeBar data)
{
if (_cumulativeReturnTickers.Contains(data.Symbol.Value))
{
_closePrices[data.Symbol.Value].Add(data.Close);
}
}
private void WarmUpPriceData(string ticker, int periods)
{
_closePrices.Add(ticker, new RollingWindow<decimal>(periods));
var history = History<TradeBar>(ticker, periods, Resolution.Daily);
foreach (var tradeBar in history)
{
_closePrices[ticker].Add(tradeBar.Close);
}
}
}
}