Overall Statistics
Total Trades
1
Average Win
0%
Average Loss
0%
Compounding Annual Return
10.567%
Drawdown
18.800%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0.634
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0.124
Beta
-0.059
Annual Standard Deviation
0.184
Annual Variance
0.034
Information Ratio
0.016
Tracking Error
0.267
Treynor Ratio
-1.996
Total Fees
$5.04
using System;
using System.Linq;
using MathNet.Numerics;
using MathNet.Numerics.LinearAlgebra;

namespace QuantConnect.Indicators
{
    /// <summary>
    /// The Least Squares Moving Average (LSMA) first calculates a least squares regression line
    /// over the preceding time periods, and then projects it forward to the current period. In
    /// essence, it calculates what the value would be if the regression line continued.
    /// Source: https://rtmath.net/helpFinAnalysis/html/b3fab79c-f4b2-40fb-8709-fdba43cdb363.htm
    /// </summary>
    public class LeastSquaredMovingAverage : WindowIndicator<IndicatorDataPoint>
    {
        /// <summary>
        /// Array representing the time.
        /// </summary>
        private double[] t;

        /// <summary>
        /// Initializes a new instance of the <see cref="LeastSquaredMovingAverage"/> class.
        /// </summary>
        /// <param name="name">The name of this indicator</param>
        /// <param name="period">The number of data points to hold in the window</param>
        public LeastSquaredMovingAverage(string name, int period)
            : base(name, period)
        {
            t = Vector<double>.Build.Dense(period, i => i + 1).ToArray();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeastSquaredMovingAverage"/> class.
        /// </summary>
        /// <param name="period">The number of data points to hold in the window.</param>
        public LeastSquaredMovingAverage(int period)
            : this("LSMA" + period, period) { }

        /// <summary>
        /// Computes the next value of this indicator from the given state
        /// </summary>
        /// <param name="window"></param>
        /// <param name="input">The input given to the indicator</param>
        /// <returns>
        /// A new value for this indicator
        /// </returns>
        protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
        {
            // Until the windows is ready, the indicator returns the input value.
            decimal output = input;
            if (IsReady)
            {
                // Sort the windows by time, convert the observations ton double and transform it to a double array
                double[] series = window
                    .OrderBy(i => i.Time)
                    .Select(i => Convert.ToDouble(i.Value))
                    .ToArray<double>();
                // Fit OLS
                Tuple<double, double> ols = Fit.Line(x: t, y: series);
                var alfa = (decimal)ols.Item1;
                var beta = (decimal)ols.Item2;
                // Make the projection.
                output = alfa + beta * (Period);
            }
            return output;
        }
    }
}                        
namespace QuantConnect
{
    public partial class TestingLSMA : QCAlgorithm
    {
        #region Fields
        private static string symbol = "SPY";
        LeastSquaredMovingAverage LSMA;
        #endregion

        #region QCAlgorithm Methods
        public override void Initialize()
        {
            SetStartDate(2010, 01, 1);
            SetEndDate(2012, 12, 31);

            SetCash(100000);

            AddSecurity(SecurityType.Equity, symbol, Resolution.Daily);
            var close = Identity(symbol);

            LSMA = new LeastSquaredMovingAverage(20);
            RegisterIndicator(symbol, LSMA, Resolution.Daily, Field.Close);
            
            var chart = new Chart("Plot");
            chart.AddSeries(new Series(close.Name));
            chart.AddSeries(new Series(LSMA.Name));
            
            PlotIndicator("Plot", close);
            PlotIndicator("Plot", true, LSMA);
        }

        public void OnData(TradeBars data)
        {
            if (!Portfolio[symbol].Invested)
            {
                SetHoldings(symbol, 1);
            }
        }
        #endregion
    }
}