Overall Statistics
Total Trades
253
Average Win
1.21%
Average Loss
-0.62%
Compounding Annual Return
-0.786%
Drawdown
13.400%
Expectancy
-0.040
Net Profit
-2.171%
Sharpe Ratio
-0.002
Loss Rate
67%
Win Rate
33%
Profit-Loss Ratio
1.93
Alpha
0.007
Beta
-0.059
Annual Standard Deviation
0.123
Annual Variance
0.015
Information Ratio
-0.701
Tracking Error
0.18
Treynor Ratio
0.005
Total Fees
$288.51
namespace QuantConnect 
{   
    /*
    *   QuantConnect University: Full Basic Template:
    *
    *   The underlying QCAlgorithm class is full of helper methods which enable you to use QuantConnect.
    *   We have explained some of these here, but the full algorithm can be found at:
    *   https://github.com/QuantConnect/QCAlgorithm/blob/master/QuantConnect.Algorithm/QCAlgorithm.cs
    */
    public class BasicTemplateAlgorithm : QCAlgorithm
    {
    	public const string Symbol = "SPY";
    	public TheilSenLinearRegression theilSen;
    	
        //Initialize the data and resolution you require for your strategy:
        public override void Initialize() 
        {
			
            //Start and End Date range for the backtest:
            SetStartDate(2013, 1, 1);         
            SetEndDate(DateTime.Now.Date.AddDays(-1));
            
            //Cash allocation
            SetCash(25000);
            
            //Add as many securities as you like. All the data will be passed into the event handler:
            AddSecurity(SecurityType.Equity, Symbol, Resolution.Daily);
            
            var spyDailyClose = Identity(Symbol);
            theilSen = new TheilSenLinearRegression("lin_reg", 7, ts => (decimal)ts.TotalDays).Of(spyDailyClose);
            
            // plot current close and close predicted by theil sen at current time
            PlotIndicator(Symbol, true, spyDailyClose, theilSen);
            // plot slope as computed by theil sen
            PlotIndicator(Symbol + "_Slope", true, theilSen.Slope);
        }

        //Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol.
        public void OnData(TradeBars data) 
        {
        	if (!theilSen.IsReady) return;
        	
        	if (theilSen.Slope > 0.025m)
        	{
        		SetHoldings(Symbol, 1.0);
        	}
        	else if (theilSen.Slope < 0.025m)
        	{
        		SetHoldings(Symbol, -1.0);
        	}
        }
    }
}                        
namespace QuantConnect {
    /// <summary>
    /// The Theil-Sen estimator creates a line from the median slopes between all points in the set.
    /// The primary output value is the value estimated by the linear regression model at the current time.
    /// </summary>
    public class TheilSenLinearRegression : WindowIndicator<IndicatorDataPoint>
    {
        private readonly Func<TimeSpan, decimal> _timeSpanUnits;
        private DateTime _start;
        public IndicatorBase<IndicatorDataPoint> Slope { get; private set; }
        
        public TheilSenLinearRegression(string name, int period)
            : this(name, period, timeSpan => (decimal) timeSpan.TotalMinutes)
        {
        }

        public TheilSenLinearRegression(string name, int period, Func<TimeSpan, decimal> timeSpanUnits)
            : base(name, period)
        {
            _timeSpanUnits = timeSpanUnits;
            Slope = new FunctionalIndicator<IndicatorDataPoint>(name + "_Slope",
            		input => input,
            		ind => this.IsReady
            		);
        }

        protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
        {
            if (window.Count == 1)
            {
                _start = input.Time;
                Slope.Update(input.Time, 0m);
                return input.Value;
            }

			// this is an O(n²) operation, so keep the period relatively small
            var data = (from a in window
                        from b in window
                        where a != b
                        where a.Time != b.Time
                        select new
                        {
                            value = (a + b) / 2,
                            slope = (b.Value - a.Value) / _timeSpanUnits(b.Time - a.Time),
                            time = (a.Time.Ticks + b.Time.Ticks) / 2
                        }).ToList();

            // get the median slope
            Slope.Update(input.Time, data.Median(x => x.slope));

            // y = mx + b, where b= median(yi-m*xi)
            var intercept = window.Median(x => (x.Value - Slope * _timeSpanUnits(x.Time - _start)));
            return Slope * _timeSpanUnits(input.Time - _start) + intercept;
        }
    }
}