| 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;
}
}
}
}