Overall Statistics |
Total Trades 537 Average Win 0.37% Average Loss -0.27% Compounding Annual Return 4.288% Drawdown 8.500% Expectancy 0.064 Net Profit 4.324% Sharpe Ratio 0.353 Loss Rate 55% Win Rate 45% Profit-Loss Ratio 1.34 Alpha -0.196 Beta 14.718 Annual Standard Deviation 0.114 Annual Variance 0.013 Information Ratio 0.212 Tracking Error 0.114 Treynor Ratio 0.003 Total Fees $537.00 |
using System; using System.Linq; using QuantConnect.Indicators; using QuantConnect.Data.Market; using System.Collections.Concurrent; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators.CandlestickPatterns; using System.Collections.Generic; namespace QuantConnect.Algorithm.CSharp { public class BasicTemplateAlgorithm : QCAlgorithm { // tolerance to prevent bouncing const decimal Tolerance = 0.01m; private const int Count = 10; // use Buffer+Count to leave a little in cash private const decimal TargetPercent = -0.1m; private SecurityChanges _changes = SecurityChanges.None; // holds our coarse fundamental indicators by symbol private readonly ConcurrentDictionary<Symbol, SelectionData> _averages = new ConcurrentDictionary<Symbol, SelectionData>(); public BollingerBands _eqBB; public BollingerBands _irBB; // class used to improve readability of the coarse selection function private class SelectionData { public ExponentialMovingAverage Fast; public ExponentialMovingAverage Slow; public RelativeStrengthIndex _shortRSI; public EveningDojiStar _eveDoji; public SelectionData(QCAlgorithm algorithm, Symbol symbol) { _shortRSI = new RelativeStrengthIndex(14); _eveDoji = new EveningDojiStar(symbol, 0.3m); } // updates the RSI and Technical indicators, returning true when they're ready public bool RSIWarmUp(QCAlgorithm algorithm, Symbol symbol, int periods) { var bars = algorithm.History<TradeBar>(symbol, periods); if (_shortRSI.Samples > 1) { return false; } foreach (var bar in bars) { _shortRSI.Update(bar.EndTime, bar.Close); } return true; } public bool DojiStarWarmUp(QCAlgorithm algorithm, Symbol symbol, int periods) { var bars = algorithm.History<TradeBar>(symbol, periods); if (_eveDoji.Samples > 1) { return false; } foreach (var bar in bars) { _eveDoji.Update(bar); } return true; } } public override void Initialize() { UniverseSettings.Leverage = 2.0m; UniverseSettings.Resolution = Resolution.Daily; SetStartDate(2018, 01, 01); SetEndDate(2019, 01, 01); //SetEndDate(DateTime.Now.Date.AddDays(-1)); SetCash(10 * 1000); SetWarmup(200); AddEquity("SPY", Resolution.Daily); AddEquity("TLT", Resolution.Daily); _eqBB = BB("SPY", 200, 1, MovingAverageType.Simple, Resolution.Daily); _irBB = BB("TLT", 200, 1, MovingAverageType.Simple, Resolution.Daily); //rebalance to simple long portfolio (75/25, 50/50, or 25/75) at the beginning of the week based on broad market technical info Schedule.On(DateRules.WeekStart("SPY"), TimeRules.AfterMarketOpen("SPY", 10), () => { RebalanceLong(); }); //trades a short portfolio on margin with an RSI screen and various CandlestickPatterns AddUniverse(coarse => { return (from cf in coarse // grab th SelectionData instance for this symbol let avg = _averages.GetOrAdd(cf.Symbol, sym => new SelectionData(this, cf.Symbol)) // Previous code wasn't returning True from Update method, so it was an endless loop // This will use a historical data call to warm up the indicator instead where avg.RSIWarmUp(this, cf.Symbol, 14) where avg.DojiStarWarmUp(this, cf.Symbol, 14) where cf.Symbol.Value != "SPY" where cf.Symbol.Value != "TLT" //filter by RSI where avg._shortRSI > 80 where cf.Price > 5.0m where cf.DollarVolume > 1 * 1000000 //where avg._eveDoji.Current.Value < 0 // sort symbols with a larger delta by percentage between the two averages orderby avg._shortRSI descending // we only need to return the symbol and return 'Count' symbols select cf.Symbol).Take(Count); }); } public override void OnSecuritiesChanged(SecurityChanges changes) { _changes = changes; if (_changes == SecurityChanges.None) return; // liquidate securities removed from our universe foreach (var security in _changes.RemovedSecurities) { if (security.Invested && (security.Symbol.Value != "SPY" || security.Symbol.Value != "TLT")) { Liquidate(security.Symbol); } } // we'll simply go short each security we added to the universe foreach (var security in _changes.AddedSecurities) { Log(security.Symbol.Value + " Added"); if ((security.Symbol.Value != "SPY" || security.Symbol.Value != "TLT")) { SetHoldings(security.Symbol, TargetPercent); } } } public void RebalanceLong() { if (Securities["SPY"].Price > _eqBB.MiddleBand && Securities["TLT"].Price < _irBB.MiddleBand) { SetHoldings("SPY", 0.75); SetHoldings("TLT", 0.25); } else if (Securities["SPY"].Price < _eqBB.MiddleBand && Securities["TLT"].Price > _irBB.MiddleBand) { SetHoldings("SPY", 0.25); SetHoldings("TLT", 0.75); } else { SetHoldings("SPY", 0.50); SetHoldings("TLT", 0.50); } } } }