Overall Statistics |
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $0.00 |
using MathNet.Numerics; using QuantConnect.Indicators; using System; using System.Linq; //Copied from this forum: //href https://www.quantconnect.com/forum/discussion/695/adjusted-slope--exponential-slope----annualized-slope--r-squuared--adjusted-slope/p1 namespace QuantConnect.Algorithm.CSharp.Helpers { public class AnnualizedExponentialSlopeIndicator : WindowIndicator<IndicatorDataPoint> { public AnnualizedExponentialSlopeIndicator(int period) : base("AESI(" + period + ")", period) { } public AnnualizedExponentialSlopeIndicator(string name, int period) : base(name, period) { } protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input) { if (window.Count < 3) return 0m; var xVals = new double[window.Count]; var yVals = new double[window.Count]; // load input data for regression for (int i = 0; i < window.Count; i++) { xVals[i] = i; // we want the log of our y values yVals[i] = Math.Log((double)window[window.Count - i - 1].Value); } //http://numerics.mathdotnet.com/Regression.html // solves y=a + b*x via linear regression var fit = Fit.Line(xVals, yVals); var intercept = fit.Item1; var slope = fit.Item2; // compute rsquared var rsquared = GoodnessOfFit.RSquared(xVals.Select(x => intercept + slope * x), yVals); // anything this small can be viewed as flat if (double.IsNaN(slope) || Math.Abs(slope) < 1e-25) return 0m; // trading days per year for us equities const int dayCount = 252; // annualize dy/dt var annualSlope = ((Math.Pow(Math.Exp(slope), dayCount)) - 1) * 100; // scale with rsquared //annualSlope = annualSlope * Math.Pow(rsquared, 2); annualSlope = annualSlope * rsquared; if (annualSlope >= (double)decimal.MaxValue || annualSlope <= (double)decimal.MinValue) { annualSlope = -1000; //Debug("Negative slope due to arithmic overflow"); } return Convert.ToDecimal(annualSlope); } } }
using MathNet.Numerics; using MathNet.Numerics.Statistics; using System; using System.Collections.Concurrent; using System.Linq; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; using QuantConnect.Securities; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect; using QuantConnect.Data.Fundamental; using QuantConnect.Brokerages; using QuantConnect.Algorithm.CSharp.Helpers; using QuantConnect.Data.Consolidators; namespace QuantConnect.Algorithm.CSharp { /* Momentum strategy according to ** Objective of the algorithm: ** ** Trade rules: ** The rules are: - Rule 00: ES futures ** Change history: ** 20180102_01 : Cannot get _spyMovingAverage to work. */ /// <summary> /// </summary> public class MomentumStrategyFuturesAlgorithm : QCAlgorithm { private const decimal Tolerance = 0.001m; private string[] roots = new[] { Futures.Indices.SP500EMini, // S&P 500 EMini futures //Futures.Metals.Gold, //^$TODO: do not add because current / previous contract goes wrong, futurechain to be added }; private HashSet<Symbol> _futureContracts = new HashSet<Symbol>(); private Dictionary<Symbol, AnnualizedExponentialSlopeIndicator> _annualizedExponentialSlope; private Dictionary<Symbol, ExponentialMovingAverage> _movingAverageSlow; private Dictionary<Symbol, ExponentialMovingAverage> _movingAverageFast; private Dictionary<Symbol, AverageTrueRange> _averageTrueRange; private FuturesContract _currentContract = null; private FuturesContract _previousContract = null; // S&P 500 EMini futures private const string RootSP500 = Futures.Indices.SP500EMini; public Symbol SP500 = QuantConnect.Symbol.Create(RootSP500, SecurityType.Future, Market.USA); // Gold futures //private const string RootGold = Futures.Metals.Gold; //public Symbol Gold = QuantConnect.Symbol.Create(RootGold, SecurityType.Future, Market.USA); private const string SP500IndexSymbol = "SPY"; /// Rule 10: Momentum is calculated based on 90 past days annualized exponential regression slope; private int _momentumWindow = 7; // Rule 05: If the stock is below its 100 days moving average, sell it; private int _movingAverageWindowSlow = 100; private int _movingAverageWindowFast = 10; // ATR private const int ATRWindowSize = 14; private ExponentialMovingAverage _spyMovingAverage; private decimal _spyPriceClose = 0; private decimal _vxxPriceClose = 0; private SecurityChanges _securityChanges = SecurityChanges.None; // Rule 08: If the SP500 is above the 200 days moving average we buy stocks, otherwise not; private int _trendfilter = 200; // Rule 04: If the stock is not in the top 100/ 20% ranking, sell it; private int _topNStockOfSp500 = 100; // Rule 06: If the stock gapped > 15%, sell it; private decimal _stockMaximumgap = 0.15m; // Look back period of stock gap private int _stockMaximumGapWindow = 60; // Rule 13: Trade maximum 30 stocks; private int _maxStockInPortfolio = 30; // Rule x: leverage increase if risk managable; private int LeverageFactor = 1; private bool _isDebugging = true; private bool _isPlotting = true; private bool _isPlotSpyMovingAverage = true; private int _isLogSpyMovingAveragePivot = 0; /// <summary> /// Helper to create AnnualizedExponentialSlopeIndicator /// </summary> /// <param name="symbol">symbol</param> /// <param name="period">period</param> /// <param name="resolution">resolution of data</param> /// <returns></returns> public AnnualizedExponentialSlopeIndicator AESI(string symbol, int period, Resolution? resolution = null, Func<IBaseData, decimal> selector = null) { var name = CreateIndicatorName(symbol, string.Format("AESI({0})", period), resolution); var aesi = new AnnualizedExponentialSlopeIndicator(name, period); RegisterIndicator(symbol, aesi, resolution, selector); return aesi; } /// <summary> /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. /// </summary> public override void Initialize() { //speed up execution //if (IsDebugging) //{ // _isPlotSpyMovingAverage = true; // _trendfilter = 100; // _topNStockOfSp500 = 20; // _stockMaximumGapWindow = 14; // _maxStockInPortfolio = 5; // _futureMovingAverageWindow = 100; // _momentumWindow = 90; //} //Set trading window SetStartDate(year: 2016, month: 1, day: 1); SetEndDate(year: 2017, month: 12, day: 30); //SetEndDate(DateTime.Now); foreach (var root in roots) { // set our expiry filter for this futures chain AddFuture(root, Resolution.Daily).SetFilter(TimeSpan.Zero, TimeSpan.FromDays(182)); Log(string.Format("AddFuture({0})", root)); } _annualizedExponentialSlope = new Dictionary<Symbol, AnnualizedExponentialSlopeIndicator>(); _movingAverageSlow = new Dictionary<Symbol, ExponentialMovingAverage>(); _movingAverageFast = new Dictionary<QuantConnect.Symbol, ExponentialMovingAverage>(); _averageTrueRange = new Dictionary<QuantConnect.Symbol, AverageTrueRange>(); var benchmark = AddEquity(SP500IndexSymbol); SetBenchmark(benchmark.Symbol); //SetBenchmark(d => 1m); //Set brokermodel SetCash(100000); SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin); //Set moving average on SPY and benchmark AddEquity(SP500IndexSymbol, Resolution.Daily); AddEquity("VXX", Resolution.Daily); var consolidator = new TradeBarConsolidator(TimeSpan.FromHours(1)); consolidator.DataConsolidated += OnTradeBarDataConsolidated; SubscriptionManager.AddConsolidator(SP500IndexSymbol, consolidator); _spyMovingAverage = new ExponentialMovingAverage(_trendfilter); RegisterIndicator(SP500IndexSymbol, _spyMovingAverage, consolidator); Log("Added new consolidator for " + SP500IndexSymbol); PlotIndicator(SP500IndexSymbol, _spyMovingAverage); //warm up https://www.quantconnect.com/forum/discussion/2557/dealing-with-futures-and-technical-indicators/p1 var history = History(SP500IndexSymbol, _trendfilter, Resolution.Daily); foreach (var bar in history) { //if(bar.EndTime < Time) //avoid looking forward bias _spyMovingAverage.Update(new IndicatorDataPoint(SP500IndexSymbol, bar.EndTime, bar.Close)); } //set warm up algorithm to avoid premature trading SetWarmUp(TimeSpan.FromDays(_trendfilter + 1)); if (IsDebugging) { Log("*** DEBUGGING: TAKE CARE OF PARAMETERS ***"); } } /// Is debugging set, speed up processing public bool IsDebugging { get { return _isDebugging; } } /// Is plotting set public bool IsPlotting { get { return _isPlotting; } } /// <summary> /// Raises the data event. /// </summary> /// <param name="data">Data.</param> public void OnData(TradeBars data) { foreach (var bar in data.Values) { if (data.ContainsKey(SP500IndexSymbol)) { _spyPriceClose = data[SP500IndexSymbol].Close; } if (data.ContainsKey("VXX")) { _vxxPriceClose = data["VXX"].Close; } } } /// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> /// <param name="slice">Slice object keyed by symbol containing the stock data</param> public override void OnData(Slice slice) { #region SecurityChanges // if we have no changes, do nothing if (_securityChanges != SecurityChanges.None) { // Liquidate removed securities that do not rank anymore // Rule 07: If the stock left the index, sell it; foreach (var security in _securityChanges.RemovedSecurities) { if (security.Invested) { Liquidate(security.Symbol); } //clean up dictionairies if (security.Type == SecurityType.Future) { if (_annualizedExponentialSlope.ContainsKey(security.Symbol)) { _annualizedExponentialSlope.Remove(security.Symbol); Log(string.Format("_annualizedExponentialSlope removed {0}", security.Symbol)); } if (_movingAverageSlow.ContainsKey(security.Symbol)) { _movingAverageSlow.Remove(security.Symbol); Log(string.Format("_movingAverageSlow removed {0}", security.Symbol)); } if (_movingAverageFast.ContainsKey(security.Symbol)) { _movingAverageFast.Remove(security.Symbol); Log(string.Format("_movingAverageFast removed {0}", security.Symbol)); } if (_averageTrueRange.ContainsKey(security.Symbol)) { _averageTrueRange.Remove(security.Symbol); Log(string.Format("_averageTrueRange removed {0}", security.Symbol)); } } } //indicate that_changes are processed _securityChanges = SecurityChanges.None; } #endregion SecurityChanges #region consolidator of future data into days foreach (var chain in slice.FutureChains) { //Log(string.Format("slice.FutureChain: {0}", chain.ToString())); // find the front contract expiring no earlier than in x days var firstContract = ( from futuresContract in chain.Value.OrderBy(x => x.Expiry) where futuresContract.Expiry > Time.Date.AddDays(15) select futuresContract ).FirstOrDefault(); //^TODO: find best contract if (firstContract == null) { Log("firstContract == null"); return; } //Log(string.Format("firstContract: {0}", firstContract.Symbol)); if(_currentContract == null || (_currentContract.Symbol != firstContract.Symbol)) { _previousContract = _currentContract; _currentContract = firstContract; Log(string.Format("CurrentContract: {0}, PreviousContract: {1}", _currentContract.Symbol, _previousContract == null ? "-" : _previousContract.Symbol.ToString())); } foreach (var contract in chain.Value) { if (!_futureContracts.Contains(contract.Symbol)) { _futureContracts.Add(contract.Symbol); var consolidator = new QuoteBarConsolidator(TimeSpan.FromHours(1)); consolidator.DataConsolidated += OnQuoteBarDataConsolidated; SubscriptionManager.AddConsolidator(contract.Symbol, consolidator); _annualizedExponentialSlope[contract.Symbol] = new AnnualizedExponentialSlopeIndicator(_momentumWindow); _movingAverageSlow[contract.Symbol] = new ExponentialMovingAverage(_movingAverageWindowSlow); _movingAverageFast[contract.Symbol] = new ExponentialMovingAverage(_movingAverageWindowFast); _averageTrueRange[contract.Symbol] = new AverageTrueRange(100); RegisterIndicator(contract.Symbol, _annualizedExponentialSlope[contract.Symbol], consolidator); RegisterIndicator(contract.Symbol, _movingAverageSlow[contract.Symbol], consolidator); RegisterIndicator(contract.Symbol, _movingAverageFast[contract.Symbol], consolidator); RegisterIndicator(contract.Symbol, _averageTrueRange[contract.Symbol], consolidator); Log("Added new consolidator(s) for " + contract.Symbol.Value); PlotIndicator("ES(AESI)", _annualizedExponentialSlope[contract.Symbol]); PlotIndicator("ES", _movingAverageSlow[contract.Symbol]); PlotIndicator("ES", _movingAverageFast[contract.Symbol]); PlotIndicator("ATR", _averageTrueRange[contract.Symbol]); //warm up https://www.quantconnect.com/forum/discussion/2557/dealing-with-futures-and-technical-indicators/p1 var history = History(contract.Symbol, (Math.Max(_annualizedExponentialSlope[contract.Symbol].Period, _movingAverageWindowSlow) + 2), Resolution.Daily); foreach (var bar in history) { //if (bar.EndTime < Time) //avoid looking forward bias //{ _annualizedExponentialSlope[contract.Symbol].Update(new IndicatorDataPoint(contract.Symbol, bar.EndTime, bar.Close)); _movingAverageSlow[contract.Symbol].Update(new IndicatorDataPoint(contract.Symbol, bar.EndTime, bar.Close)); _movingAverageFast[contract.Symbol].Update(new IndicatorDataPoint(contract.Symbol, bar.EndTime, bar.Close)); _averageTrueRange[contract.Symbol].Update(new TradeBar(bar.EndTime, contract.Symbol, bar.Open, bar.High, bar.Low, bar.Close, bar.Volume)); //} } Log(string.Format("_annualizedExponentialSlope.IsReady: {0}", _annualizedExponentialSlope[contract.Symbol].IsReady.ToString())); Log(string.Format("_movingAverageSlow.IsReady: {0}", _movingAverageSlow[contract.Symbol].IsReady.ToString())); Log(string.Format("_movingAverageFast.IsReady: {0}", _movingAverageFast[contract.Symbol].IsReady.ToString())); Log(string.Format("_averageTrueRange.IsReady: {0}", _averageTrueRange[contract.Symbol].IsReady.ToString())); } } } #endregion consolidator of future data into days if (IsWarmingUp) { //Log("IsWarmingUp"); return; } if (!Securities.ContainsKey(SP500IndexSymbol)) { Log(string.Format("!Securities.ContainsKey(SP500IndexSymbol:{0})", SP500IndexSymbol)); return; } //- Rule 08: If the SP500 is above the 200 days moving average we buy stocks, otherwise not; if (Securities[SP500IndexSymbol].Price <= _spyMovingAverage.Current.Value) { //$TODO: buy T-note/ gold/ silver? if (_isLogSpyMovingAveragePivot >= 0) { Log(string.Format("Spy in downtrend: {0} < {1}", Securities[SP500IndexSymbol].Price, _spyMovingAverage.Current.Value)); _isLogSpyMovingAveragePivot = -1; } } else { if (_isLogSpyMovingAveragePivot <= 0) { Log(string.Format("Spy in uptrend: {0} > {1}", Securities[SP500IndexSymbol].Price, _spyMovingAverage.Current.Value)); _isLogSpyMovingAveragePivot = 1; } } if (_currentContract != null) { if (!_annualizedExponentialSlope.ContainsKey(_currentContract.Symbol)) { Log(string.Format("!_annualizedExponentialSlope.ContainsKey({0})", _currentContract.Symbol)); return; } if (!_movingAverageSlow.ContainsKey(_currentContract.Symbol)) { Log(string.Format("!_movingAverageSlow.ContainsKey({0})", _currentContract.Symbol)); return; } if (!_movingAverageFast.ContainsKey(_currentContract.Symbol)) { Log(string.Format("!_movingAverageFast.ContainsKey({0})", _currentContract.Symbol)); return; } if (!_averageTrueRange.ContainsKey(_currentContract.Symbol)) { Log(string.Format("!_averageTrueRange.ContainsKey({0})", _currentContract.Symbol)); return; } //Contract is most likely to (be) expire soon; liquidate contract if (_previousContract != null && Portfolio[_previousContract.Symbol].Invested) { Log(string.Format("!Liquidate(_previousContract.Symbol={0})", _previousContract.Symbol)); Liquidate(_previousContract.Symbol); //$TODO: buy immediately new current contract or decide based on new market entry. } if (!Portfolio[_currentContract.Symbol].Invested) { //RULE xx: Only long in uptrend of market; if (Securities[SP500IndexSymbol].Price > _spyMovingAverage * (1 + Tolerance)) { if ((_annualizedExponentialSlope[_currentContract.Symbol] > 1 || _annualizedExponentialSlope[_currentContract.Symbol] < 0) && _movingAverageSlow[_currentContract.Symbol] > _currentContract.LastPrice && //$Todo: required? _movingAverageFast[_currentContract.Symbol] > _movingAverageSlow[_currentContract.Symbol] * (1 + Tolerance)) { Log(string.Format("_annualizedExponentialSlope[{0}]={1} > 0", _currentContract.Symbol, _annualizedExponentialSlope[_currentContract.Symbol])); //get position size // Do we have cash/ margin to trade? if (Portfolio.MarginRemaining <= 0) { Log(string.Format("Portfolio.MarginRemaining <= 0 for symbol {0}", _currentContract.Symbol)); return; } //decimal estimatedPortfolioCashBalance = Portfolio.Cash - _currentContract.LastPrice; //if (estimatedPortfolioCashBalance >= 0) //SetHoldings(_currentContract.Symbol, 1); //does not work MarketOrder(_currentContract.Symbol, 4); //Todo: limit order } } } else { //Rule xx: liquidate if below 3 ATR stop units below the purchased price if (_movingAverageSlow[_currentContract.Symbol].Current.Value < _currentContract.LastPrice * (1 + Tolerance)) { Log(string.Format("_movingAverage[{0}].Current.Value < _currentContract.LastPrice {1}", _currentContract.Symbol, _currentContract.LastPrice)); Liquidate(_currentContract.Symbol); //$TODO: replace by SP500 moving average. } //Todo: 10 consequent losing days. } } ///Plotting if (IsPlotting) { //foreach (var chain in slice.FutureChains) //{ // foreach (var contract in chain.Value) // { // Log(String.Format("{0},Bid={1} Ask={2} Last={3} OI={4}", // contract.Symbol.Value, // contract.BidPrice, // contract.AskPrice, // contract.LastPrice, // contract.OpenInterest)); // } //} } } private void OnQuoteBarDataConsolidated(object sender, QuoteBar quoteBar) { //Log(quoteBar.ToString()); //if(_annualizedExponentialSlope.ContainsKey(quoteBar.Symbol)) //{ // Log(_annualizedExponentialSlope[quoteBar.Symbol].ToDetailedString()); //} } private void OnTradeBarDataConsolidated(object sender, TradeBar tradeBar) { Log("****" + tradeBar.ToString()); } /// <summary> /// Portfolio risk weight /// - Rule 09: Calculate the position sizes, based on 10 basis points using ATR formula; /// - Rule 12: Position Size = (portfolioSize * 0, 001 / ATR) = #Shares; /// </summary> /// <param name="symbol"></param> /// <param name="atr"></param> /// <returns></returns> public decimal GetPositionSize(Symbol symbol, decimal atr) { if (atr == 0) return 0; decimal risk = this.Portfolio.TotalPortfolioValue * 0.001m; return (decimal)((risk / atr) * Securities[symbol].Price) / Portfolio.TotalPortfolioValue * 100; } /// <summary> /// Get the Average True Range (ATR) /// </summary> /// <param name="symbol"></param> /// <param name="windowSize"></param> /// <returns></returns> public decimal GetATR(Symbol symbol, int windowSize) { //validate that the security is in the universe if (!Securities.ContainsKey(symbol)) return 0; IEnumerable<Slice> slices = History(windowSize, Resolution.Daily); var window = slices.Get(symbol, Field.Close).ToList(); if (window.Count < 3) return 0m; var atr = ATR(symbol, windowSize, MovingAverageType.Exponential, Resolution.Daily); return atr.Current.Value; } /// <summary> /// Calculate Mean /// </summary> private decimal GetMean(Symbol symbol, int windowSize) { //validate that the security is in the universe if (!Securities.ContainsKey(symbol)) return 0; IEnumerable<Slice> slices = History(windowSize, Resolution.Daily); IEnumerable<decimal> close = slices.Get(symbol, Field.Close); var closes = close.ToDoubleArray(); return (decimal)(closes.Mean()); } /// <summary> /// Calculate Gap /// return np.max(np.abs(np.diff(close_data))/close_data[:-1]) // out[n] = (a[n+1] - a[n]) / a[n] /// </summary> private decimal GetGap(Symbol symbol, int windowSize) { //validate that the security is in the universe if (!Securities.ContainsKey(symbol)) return 0; IEnumerable<Slice> slices = History(windowSize, Resolution.Daily); var window = slices.Get(symbol, Field.Close).ToList(); //var closes = close.ToDoubleArray(); if (window.Count < 3) return 0m; var diff = new double[window.Count]; // load input data for regression for (int i = 0; i < window.Count - 1; i++) { diff[i] = (double)((window[i + 1] - window[i]) / (window[i] == 0 ? 1 : window[i])); } return (decimal)diff.MaximumAbsolute(); } // this event fires whenever we have changes to our universe public override void OnSecuritiesChanged(SecurityChanges changes) { _securityChanges = changes; if (changes.AddedSecurities.Count > 0) { Log("Securities added: " + string.Join(",", changes.AddedSecurities.Select(x => x.Symbol.Value))); } if (changes.RemovedSecurities.Count > 0) { Log("Securities removed: " + string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value))); } foreach (var change in changes.AddedSecurities) { var history = History<TradeBar>(change.Symbol, 1, Resolution.Daily); foreach (var data in history.OrderByDescending(x => x.Time).Take(1)) { Log("History: " + data.Symbol.Value + ": " + data.Time + " > " + data.Close); } } } // Fire plotting events once per day: public override void OnEndOfDay() { //warm up https://www.quantconnect.com/forum/discussion/2557/dealing-with-futures-and-technical-indicators/p1 var history = History(SP500IndexSymbol, 1, Resolution.Daily); foreach (var bar in history) { //if (bar.EndTime < Time) //{ _spyMovingAverage.Update(new IndicatorDataPoint(SP500IndexSymbol, bar.EndTime, bar.Close)); //} } //_spyMovingAverage.Update(new IndicatorDataPoint(SP500IndexSymbol, Time, _spyPriceClose)); //_spyMovingAverage.Update(new IndicatorDataPoint(SP500IndexSymbol, Time, Securities[SP500IndexSymbol].Close)); if (!_spyMovingAverage.IsReady) Log("*** !_spyMovingAverage.IsReady ***"); if (_spyMovingAverage.Current.Value == 0) Log("*** _spyMovingAverage.Current.Value == 0 ***"); if (_currentContract != null) { if (!_annualizedExponentialSlope.ContainsKey(_currentContract.Symbol)) Log(string.Format("_annualizedExponentialSlope does not contain {0}", _currentContract.Symbol)); if (!_movingAverageSlow.ContainsKey(_currentContract.Symbol)) Log(string.Format("_movingAverage does not contain {0}", _currentContract.Symbol)); if (!_annualizedExponentialSlope[_currentContract.Symbol].IsReady) Log(string.Format("{0} _annualizedExponentialSlope.IsReady: {1}", _currentContract.Symbol, _annualizedExponentialSlope[_currentContract.Symbol].IsReady.ToString())); if(!_movingAverageSlow[_currentContract.Symbol].IsReady) Log(string.Format("{0} _movingAverage.IsReady: {1}", _currentContract.Symbol, _movingAverageSlow[_currentContract.Symbol].IsReady.ToString())); } ///Plotting //Assuming daily mode,dont chart in a smaller res and kill quota if (IsPlotting) { Plot(SP500IndexSymbol, "Price", _spyPriceClose); Plot("VXX", "Price", _vxxPriceClose); if (_isPlotSpyMovingAverage) { Plot(SP500IndexSymbol, _spyMovingAverage); } if (_currentContract != null) { if (_annualizedExponentialSlope.ContainsKey(_currentContract.Symbol)) { Plot("ES(AESI)", _annualizedExponentialSlope[_currentContract.Symbol].Current.Value); } Plot("ES", "Price", _currentContract.LastPrice); if (_movingAverageSlow.ContainsKey(_currentContract.Symbol)) { Plot("ES", _movingAverageSlow[_currentContract.Symbol].Current.Value); } } else { Log("_currentContract == null"); } if (Portfolio.TotalPortfolioValue > 0) { var accountLeverage = Portfolio.TotalAbsoluteHoldingsCost / Portfolio.TotalPortfolioValue; Plot("Leverage", "Leverage", accountLeverage); } } } } }