Overall Statistics Total Trades10Average Win12.46%Average Loss-0.19%Compounding Annual Return19.748%Drawdown13.000%Expectancy52.447Net Profit58.126%Sharpe Ratio1.118Loss Rate20%Win Rate80%Profit-Loss Ratio65.81Alpha0.143Beta-0.02Annual Standard Deviation0.126Annual Variance0.016Information Ratio0.288Tracking Error0.167Treynor Ratio-7.138Total Fees\$74.00
```using MathNet.Numerics;
using QuantConnect.Indicators;
using System;
using System.Linq;

//Copied from this forum:

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: **

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;
private bool _processDataFlag = false;

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

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
}
_annualizedExponentialSlope = new Dictionary<Symbol, AnnualizedExponentialSlopeIndicator>();
_movingAverageSlow = new Dictionary<Symbol, ExponentialMovingAverage>();
_movingAverageFast = new Dictionary<QuantConnect.Symbol, ExponentialMovingAverage>();
_averageTrueRange = new Dictionary<QuantConnect.Symbol, AverageTrueRange>();

SetBenchmark(benchmark.Symbol);
//SetBenchmark(d => 1m);

//Set brokermodel
SetCash(100000);
SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);

//Set moving average on SPY and benchmark

_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));

//trade only on wednesdays at opening after 10 minutes
Schedule.On(DateRules.EveryDay(),
TimeRules.At(16, 0), () =>
{
_processDataFlag = true;
});

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>
{
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)
{
if (!_processDataFlag)
return;

#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
//if (slice.FutureChains.Count == 0)
//{
//    var message = string.Format("Not slice.FutureChains.Count: {0}", slice.FutureChains.Count.ToString());
//    Log(message);
//    throw new Exception(message);
//}

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)
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))
{

var consolidator = new QuoteBarConsolidator(TimeSpan.FromDays(1));
consolidator.DataConsolidated += OnQuoteBarDataConsolidated;

_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)*60*24, Resolution.Minute);
foreach (var bar in history)
{
if (bar.EndTime.Hour == 16 && bar.EndTime < Time && bar.EndTime.Minute == 30) //avoid looking forward bias and only once per hour
{
_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));
}
}
}
}
}
#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)
{
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(Math.Abs(3 * _averageTrueRange[_currentContract.Symbol]) > Portfolio[_currentContract.Symbol].AveragePrice)
{
Log(string.Format("3*_averageTrueRange[{0}].Current.Value > Portfolio[{1}].AveragePrice={2}", _currentContract.Symbol, _averageTrueRange[_currentContract.Symbol], Portfolio[_currentContract.Symbol].AveragePrice));
Liquidate(_currentContract.Symbol);
}
}
}
///Plotting
if (IsPlotting)
{
//foreach (var chain in slice.FutureChains)
//{
//    foreach (var contract in chain.Value)
//    {
//             contract.Symbol.Value,
//             contract.BidPrice,
//             contract.LastPrice,
//             contract.OpenInterest));
//    }
//}
}
_processDataFlag = false;
}
private void OnQuoteBarDataConsolidated(object sender, QuoteBar quoteBar)
{
Log(quoteBar.ToString());
//if(_annualizedExponentialSlope.ContainsKey(quoteBar.Symbol))
//{
//    Log(_annualizedExponentialSlope[quoteBar.Symbol].ToDetailedString());
//}
//_annualizedExponentialSlope[quoteBar.Symbol].Update(new IndicatorDataPoint(quoteBar.Symbol, quoteBar.EndTime, quoteBar.Close));
//_movingAverageSlow[quoteBar.Symbol].Update(new IndicatorDataPoint(quoteBar.Symbol, quoteBar.EndTime, quoteBar.Close));
//_movingAverageFast[quoteBar.Symbol].Update(new IndicatorDataPoint(quoteBar.Symbol, quoteBar.EndTime, quoteBar.Close));
//_averageTrueRange[quoteBar.Symbol].Update(new TradeBar(quoteBar.EndTime, quoteBar.Symbol, quoteBar.Open, quoteBar.High, quoteBar.Low, quoteBar.Close, 0));
}
{
}

/// <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.RemovedSecurities.Count > 0)
{
Log("Securities removed: " + string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value)));
}

{
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.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));

}

///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);
}
}
}
}
}```