Overall Statistics
Total Trades
536
Average Win
2.16%
Average Loss
-1.37%
Compounding Annual Return
1.208%
Drawdown
32.200%
Expectancy
0.065
Net Profit
16.909%
Sharpe Ratio
0.158
Loss Rate
59%
Win Rate
41%
Profit-Loss Ratio
1.57
Alpha
0.02
Beta
-0.019
Annual Standard Deviation
0.125
Annual Variance
0.016
Information Ratio
-0.067
Tracking Error
0.247
Treynor Ratio
-1.067
Total Fees
$2576.96
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;
        decimal _prev;
        #endregion

        #region QCAlgorithm Methods
        public override void Initialize()
        {
            SetStartDate(2000, 1, 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 && _prev != 0 && LSMA < _prev)
            {
                Liquidate(symbol);
            }
            
            if(!Portfolio[symbol].Invested && _prev != 0 && LSMA > _prev)
            {
                SetHoldings(symbol, 1);
            }
            
            _prev = LSMA;
        }
        #endregion
    }
}