| Overall Statistics |
|
Total Trades 17 Average Win 7.40% Average Loss -3.20% Compounding Annual Return 17.412% Drawdown 15.200% Expectancy 1.210 Net Profit 37.915% Sharpe Ratio 1.043 Probabilistic Sharpe Ratio 48.234% Loss Rate 33% Win Rate 67% Profit-Loss Ratio 2.32 Alpha 0.173 Beta -0.087 Annual Standard Deviation 0.147 Annual Variance 0.022 Information Ratio -0.265 Tracking Error 0.288 Treynor Ratio -1.756 Total Fees $31.76 Estimated Strategy Capacity $740000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
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.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QuantConnect.Algorithm.CSharp.Custom
{
public class BtfdAlgo : QCAlgorithm
{
//private static Symbol[] _symbolTickers = new [] { "IVV", "IYY", "IJR" };
private static Symbol[] _symbols = new Symbol[] { QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA) };
public override void Initialize()
{
int startYear = int.Parse(GetParameter("START_YEAR"));
int endYear = int.Parse(GetParameter("END_YEAR"));
int days = int.Parse(GetParameter("AVERAGE_DAYS"));
decimal returnPercentage = decimal.Parse(GetParameter("SELL_SIGNAL_PERCENT"));
SetStartDate(new DateTime(startYear, 1, 1));
SetEndDate(new DateTime(endYear, 1, 1));
SetCash(100000);
UniverseSettings.Resolution = Resolution.Daily;
SetWarmup(new TimeSpan(days, 0, 0, 0));
EnableAutomaticIndicatorWarmUp = true;
SetUniverseSelection(new ManualUniverseSelectionModel(_symbols));
AddAlpha(new BtfdAlpha());
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
SetExecution(new ImmediateExecutionModel());
AddRiskManagement(new TakeProfitRiskManagement(returnPercentage));
AddRiskManagement(new MaximumDrawdownPercentPerSecurity());
}
private class BtfdAlpha : IAlphaModel, INotifiedSecurityChanges
{
private readonly Dictionary<Symbol, SymbolData> _symbols = new Dictionary<Symbol, SymbolData>();
private readonly int _smaDays;
public BtfdAlpha(int smaDays = 365)
{
_smaDays = smaDays;
}
public void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
foreach (var added in changes.AddedSecurities)
{
if (!_symbols.ContainsKey(added.Symbol))
{
_symbols.Add(added.Symbol, new SymbolData(added.Symbol, _smaDays, algorithm));
}
}
foreach (var removed in changes.RemovedSecurities)
{
if (_symbols.ContainsKey(removed.Symbol))
{
_symbols.Remove(removed.Symbol);
}
}
}
public IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
List<Insight> insights = new List<Insight>();
foreach (var symbol in data.Keys)
{
// First, check if already invested
if (!algorithm.Securities[symbol].Invested)
{
// Not invested, check to buy
SymbolData symbolData = _symbols[symbol];
var price = data[symbol].Close;
var currentSma = symbolData.SMA.Current.Value;
if (price < currentSma)
{
// Prediction is to buy
Insight insight = new Insight(symbol, new TimeSpan(30,0,0,0,0), InsightType.Price, InsightDirection.Up);
insights.Add(insight);
}
}
}
return insights;
}
}
private class TakeProfitRiskManagement : RiskManagementModel
{
private Dictionary<Symbol, decimal> _purchasePrices = new Dictionary<Symbol, decimal>();
private decimal _returnPercentage;
public TakeProfitRiskManagement(decimal returnPercentage = 0.1m)
{
_returnPercentage = returnPercentage;
}
public override IEnumerable<IPortfolioTarget> ManageRisk(QCAlgorithm algorithm, IPortfolioTarget[] targets)
{
ManagePurchasePrices(algorithm);
List<PortfolioTarget> portfolioTargets = new List<PortfolioTarget>();
foreach(var security in algorithm.ActiveSecurities)
{
Symbol symbol = security.Key;
if(algorithm.Securities[symbol].Invested)
{
decimal purchasePrice = _purchasePrices[symbol];
decimal currentPrice = algorithm.Securities[symbol].Price;
var currentReturn = ((currentPrice - purchasePrice) / purchasePrice) * 100.0m;
if(currentReturn >= _returnPercentage)
{
PortfolioTarget newTarget = new PortfolioTarget(symbol, 0);
portfolioTargets.Add(newTarget);
}
}
}
return portfolioTargets;
}
private void ManagePurchasePrices(QCAlgorithm algorithm)
{
foreach (var security in algorithm.ActiveSecurities)
{
Symbol symbol = security.Key;
if (algorithm.Securities[symbol].Invested)
{
// If we havn't recorded the purchase price, we add it
if (!_purchasePrices.ContainsKey(symbol))
{
decimal purchasePrice = algorithm.Securities[symbol].Price;
_purchasePrices.Add(symbol, purchasePrice);
}
}
else
{
if (!algorithm.Securities[symbol].Invested)
{
// If the price is not invested, we remove it from the prices dictionary
if (_purchasePrices.ContainsKey(symbol))
{
_purchasePrices.Remove(symbol);
}
}
}
}
}
}
private class SymbolData
{
private readonly Symbol _symbol;
public Symbol Symbol { get { return _symbol; } }
private readonly SimpleMovingAverage _sma;
public SimpleMovingAverage SMA { get { return _sma; } }
public SymbolData(Symbol symbol, int days, QCAlgorithm algo)
{
_symbol = symbol;
_sma = new SimpleMovingAverage(days);
algo.RegisterIndicator(symbol, _sma, Resolution.Daily);
}
}
}
}