Overall Statistics |
Total Trades 150 Average Win 0.03% Average Loss -0.02% Compounding Annual Return 0.080% Drawdown 0.400% Expectancy 0.276 Net Profit 0.400% Sharpe Ratio 0.369 Probabilistic Sharpe Ratio 4.567% Loss Rate 54% Win Rate 46% Profit-Loss Ratio 1.78 Alpha -0 Beta 0.006 Annual Standard Deviation 0.001 Annual Variance 0 Information Ratio -0.878 Tracking Error 0.156 Treynor Ratio 0.095 Total Fees $150.00 Estimated Strategy Capacity $300000000.00 Lowest Capacity Asset GSM UEP8E0RQCFOL |
using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Data; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Market; using QuantConnect.Indicators; using System.Collections.Concurrent; namespace QuantConnect { /// <summary> /// Example structure for structuring an algorithm with indicator and consolidator data for many tickers. /// </summary> /// <meta name="tag" content="consolidating data" /> /// <meta name="tag" content="indicators" /> /// <meta name="tag" content="using data" /> /// <meta name="tag" content="strategy example" /> public class TrendMovingAverageAlgorithm : QCAlgorithm { private SecurityChanges _changes = SecurityChanges.None; /// <summary> /// This is the period of bars we'll be creating /// </summary> public readonly TimeSpan BarPeriod = TimeSpan.FromHours(24); /// <summary> /// This is the period of our slow sma indicators /// </summary> //public readonly int SlowSimpleMovingAveragePeriod = 200; /// <summary> /// This is the period of our fast sma indicators /// </summary> //public readonly int FastSimpleMovingAveragePeriod = 50; /// <summary> /// This is the number of consolidated bars we'll hold in symbol data for reference /// </summary> public readonly int RollingWindowSize = 100; /// <summary> /// Holds all of our data keyed by each symbol /// </summary> public readonly ConcurrentDictionary<string, SymbolData> Data = new ConcurrentDictionary<string, SymbolData>(); /// <summary> /// Contains all of our equity symbols /// </summary> public readonly IReadOnlyList<string> EquitySymbols = new List<string> { "DNN", "IBM", "AAPL", "GSM", "OXY", "M" }; /// <summary> /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. /// </summary> /// <seealso cref="QCAlgorithm.SetStartDate(System.DateTime)"/> /// <seealso cref="QCAlgorithm.SetEndDate(System.DateTime)"/> /// <seealso cref="QCAlgorithm.SetCash(decimal)"/> public override void Initialize() { SetStartDate(2017, 01, 01); SetEndDate(2021, 12, 31); SetCash(100000); //UniverseSettings.Leverage = 2.0m; // initialize our equity data foreach (var symbol in EquitySymbols) { var equity = AddEquity(symbol); //Data.Add(symbol, new SymbolData(equity.Symbol, BarPeriod, RollingWindowSize)); Data[symbol] = new SymbolData(equity.Symbol, BarPeriod, RollingWindowSize); AddSecurity(SecurityType.Equity, symbol, Resolution.Daily); } // loop through all our symbols and request data subscriptions and initialize indicatora foreach (var kvp in Data) { // this is required since we're using closures below, for more information // see: http://stackoverflow.com/questions/14907987/access-to-foreach-variable-in-closure-warning var symbolData = kvp.Value; // define a consolidator to consolidate data for this symbol on the requested period var consolidator = (IDataConsolidator)new TradeBarConsolidator(BarPeriod); // define our indicator //symbolData.SlowSMA = new SimpleMovingAverage(CreateIndicatorName(symbolData.Symbol, "SlowSMA" + SlowSimpleMovingAveragePeriod, Resolution.Daily), SlowSimpleMovingAveragePeriod); //symbolData.FastSMA = new SimpleMovingAverage(CreateIndicatorName(symbolData.Symbol, "FastSMA" + FastSimpleMovingAveragePeriod, Resolution.Daily), FastSimpleMovingAveragePeriod); // wire up our consolidator to update the indicator consolidator.DataConsolidated += (sender, baseData) => { // 'bar' here is our newly consolidated data var bar = (IBaseDataBar)baseData; // update the indicator //symbolData.SlowSMA.Update(bar.Time, bar.Close); //symbolData.FastSMA.Update(bar.Time, bar.Close); // we're also going to add this bar to our rolling window so we have access to it later symbolData.Bars.Add(bar); }; // we need to add this consolidator so it gets auto updates SubscriptionManager.AddConsolidator(symbolData.Symbol, consolidator); } // Set requested data resolution UniverseSettings.Resolution = Resolution.Daily; //SetUniverseSelection(new ManualUniverseSelectionModel(EquitySymbols.Select(x=>new Symbol(SecurityIdentifier.GenerateBase(null, x, Market.USA), x)).ToList())); // Use ShareClassMeanReversionAlphaModel to establish insights //SetAlpha(new ShareClassMeanReversionAlphaModel(symbols)); // Equally weigh securities in portfolio, based on insights SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel()); // Set Immediate Execution Model SetExecution(new ImmediateExecutionModel()); // Set Null Risk Management Model SetRiskManagement(new NullRiskManagementModel()); } /// <summary> /// Event fired each time the we add/remove securities from the data feed /// </summary> /// <param name="changes">Object containing AddedSecurities and RemovedSecurities</param> public override void OnSecuritiesChanged(SecurityChanges changes) { _changes = changes; } /// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> /// <param name="data">TradeBars IDictionary object with your stock data</param> public override void OnData(Slice data) { if (_changes == SecurityChanges.None) return; // loop through each symbol in our structure foreach (var symbolData in Data.Values) { // this check proves that this symbol was JUST updated prior to this OnData function being called if(symbolData.WasJustUpdated(Time)) { //symbolData.Update(); if (symbolData.IsReady) { if(data.Bars.ContainsKey(symbolData.Symbol.Value)) { //Debug($"{symbolData.Symbol.Value} - Close:{data.Bars[symbolData.Symbol.Value].Close}, SMA50:{symbolData.FastSMA}, SMA200:{symbolData.SlowSMA} - IsReady"); if (!Portfolio[symbolData.Symbol].Invested) { //Debug($"{symbolData.Symbol.Value} - Close:{data.Bars[symbolData.Symbol.Value].Close}, SMA50:{symbolData.FastSMA}, SMA200:{symbolData.SlowSMA} - MA Trigger"); //if (symbolData.IsMATrigger()) //data.Bars[symbolData.Symbol.Value].Close)) if (symbolData.IsTrigger()) { //Debug($"{symbolData.Symbol.Value} - Open Trade"); MarketOrder(symbolData.Symbol, 10); symbolData.OpenTrade(); Debug($"{symbolData.Symbol.Value} - Open:{data.Bars[symbolData.Symbol.Value].Close} | SL:{symbolData.CurrentTradeStopLoss} | TP:{symbolData.CurrentTradeMaxPrice}"); } } else { //Debug($"{symbolData.Symbol.Value} - MaxPrice:{symbolData.MaxPrice}"); //check for close if (symbolData.CheckForClose()) //data.Bars[symbolData.Symbol.Value].Close)) { Debug($"{symbolData.Symbol.Value} - Close Trade {data.Bars[symbolData.Symbol.Value].Close}"); Liquidate(symbolData.Symbol); } } //Debug($"{symbolData.Symbol.Value} - IndicatorsData:{symbolData.IndicatorsData}"); //if (symbolData.LastStage != "init") // Debug($"{symbolData.Symbol.Value} - LastStage:{symbolData.LastStage}"); } } } } } /// <summary> /// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets). /// </summary> /// <remarks>Method is called 10 minutes before closing to allow user to close out position.</remarks> /*public override void OnEndOfDay(Symbol symbol) { int i = 0; foreach (var kvp in Data.OrderBy(x => x.Value.Symbol)) { // we have too many symbols to plot them all, so plot ever other if (kvp.Value.IsReady && ++i%2 == 0) { Plot(kvp.Value.Symbol.ToString(), kvp.Value.SlowSMA); Plot(kvp.Value.Symbol.ToString(), kvp.Value.FastSMA); } } }*/ /// <summary> /// Contains data pertaining to a symbol in our algorithm /// </summary> public class SymbolData { /// <summary> /// This symbol the other data in this class is associated with /// </summary> public readonly Symbol Symbol; /// <summary> /// A rolling window of data, data needs to be pumped into Bars by using Bars.Update( tradeBar ) and /// can be accessed like: /// mySymbolData.Bars[0] - most first recent piece of data /// mySymbolData.Bars[5] - the sixth most recent piece of data (zero based indexing) /// </summary> public readonly RollingWindow<IBaseDataBar> Bars; /// <summary> /// The period used when populating the Bars rolling window. /// </summary> public readonly TimeSpan BarPeriod; /// <summary> /// The slow simple moving average indicator for our symbol /// </summary> //public SimpleMovingAverage SlowSMA; //public readonly RollingWindow<decimal> SlowSMAWindow; /// <summary> /// The fast simple moving average indicator for our symbol /// </summary> //public SimpleMovingAverage FastSMA; //public readonly RollingWindow<decimal> FastSMAWindow; public string LastStage; public decimal MaxPrice; public decimal CurrentTradeMaxPrice; public decimal CurrentTradeStopLoss; public string IndicatorsData; /// <summary> /// Initializes a new instance of SymbolData /// </summary> public SymbolData(Symbol symbol, TimeSpan barPeriod, int windowSize) { Symbol = symbol; BarPeriod = barPeriod; Bars = new RollingWindow<IBaseDataBar>(windowSize); //SlowSMAWindow = new RollingWindow<decimal>(windowSize); //FastSMAWindow = new RollingWindow<decimal>(windowSize); } public void Update() { //SlowSMAWindow.Add(SlowSMA); //FastSMAWindow.Add(FastSMA); } public void OpenTrade() { CurrentTradeMaxPrice = GetMaxPrice(); CurrentTradeStopLoss = Bars.Skip(1).Take(4).Min(x=>x.Low)*(decimal)0.95; } public bool CheckForClose() //decimal close) { MaxPrice = CurrentTradeMaxPrice; var close = Bars[1].Close; var tp = close > MaxPrice || ((MaxPrice - close) <= close*(decimal)0.005); var sl = close < CurrentTradeStopLoss; return tp || sl; } private decimal GetMaxPrice() //int period) { return Bars.Skip(1).Max(x=>x.Close); } //public bool IsMATrigger() //decimal close) public bool IsTrigger() { /*var ma1 = FastSMA; var ma1_30 = FastSMAWindow[30]; var ma1_60 = FastSMAWindow[60]; var ma1_90 = FastSMAWindow[90]; var ma2 = SlowSMA; var ma2_30 = SlowSMAWindow[30]; var ma2_60 = SlowSMAWindow[60]; var ma2_90 = SlowSMAWindow[90]; var treshhold1 = Bars[1].Close * (decimal)0.001; var treshhold2 = Bars[1].Close * (decimal)0.01; IndicatorsData = $"ma1:{ma1}|ma1_30:{ma1_30}|ma1_60:{ma1_60}|ma1_90:{ma1_90}|ma2:{ma2}|ma2_30:{ma2_30}|ma2_60:{ma2_60}|ma2_90:{ma2_90}|treshhold1:{treshhold1}|treshhold2:{treshhold2}"; var ma2UpTrend = (ma2 - ma2_30) > treshhold2; LastStage = ma2UpTrend ? "stage1" : "init"; ma2UpTrend = ma2UpTrend ? (ma2_30 - ma2_60) > treshhold2 : false; LastStage = ma2UpTrend ? "stage2" : LastStage; ma2UpTrend = ma2UpTrend ? (ma2_60 - ma2_90) > treshhold2 : false; LastStage = ma2UpTrend ? "stage3" : LastStage; ma2UpTrend = ma2UpTrend ? (ma1 - ma2) > treshhold2*2 : false; LastStage = ma2UpTrend ? "stage4" : LastStage; //ma2UpTrend = ma2UpTrend ? (ma2 - close) > treshhold2 : false; ma2UpTrend = ma2UpTrend ? (Bars.Skip(1).Take(3).Min(x=>x.Low) <= ma2) : false; LastStage = ma2UpTrend ? "stage5" : LastStage; ma2UpTrend = ma2UpTrend ? (Bars[1].Close > ma2 && ((Bars[1].Close - ma2) <= treshhold2)) : false; LastStage = ma2UpTrend ? "stage6" : LastStage; return ma2UpTrend;*/ var treshhold = Bars[1].Close * (decimal)0.04; return (Bars[1].Close - Bars[2].Close) > treshhold; } /// <summary> /// Returns true if all the data in this instance is ready (indicators, rolling windows, ect...) /// </summary> public bool IsReady { //get { return Bars.IsReady && SlowSMA.IsReady && FastSMA.IsReady && SlowSMAWindow.IsReady && FastSMAWindow.IsReady; } get { return Bars.IsReady; } } /// <summary> /// Returns true if the most recent trade bar time matches the current time minus the bar's period; this /// indicates that Update() was just called on this instance. /// </summary> /// <param name="current">The current algorithm time</param> /// <returns>True if this instance was just updated with new data, false otherwise</returns> public bool WasJustUpdated(DateTime current) { return Bars.Count > 0 && Bars[0].Time == current - BarPeriod; } } } }