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