| Overall Statistics |
|
Total Trades 342 Average Win 0.89% Average Loss -1.01% Compounding Annual Return 3.243% Drawdown 18.900% Expectancy 0.050 Net Profit 4.992% Sharpe Ratio 0.247 Loss Rate 44% Win Rate 56% Profit-Loss Ratio 0.88 Alpha -0.005 Beta 0.474 Annual Standard Deviation 0.153 Annual Variance 0.023 Information Ratio -0.342 Tracking Error 0.156 Treynor Ratio 0.08 Total Fees $2373.48 |
using System;
using System.Linq;
using MathNet.Numerics;
namespace QuantConnect
{
public class GoldStatArbAlgorithm : QCAlgorithm
{
private PairCalculation _pair;
public override void Initialize()
{
SetStartDate(2006, 05, 23); //Set Start Date
SetEndDate(2007, 11, 30); //Set End Date
SetCash(100000); //Set Strategy Cash
var gld = AddEquity("GLD", Resolution.Daily);
var gdx = AddEquity("GDX", Resolution.Daily);
_pair = new PairCalculation();
}
public override void OnData(Slice data)
{
if (!data.ContainsKey("GLD") || !data.ContainsKey("GDX")) return;
if (!_pair.Update(data["GLD"], data["GDX"])) return;
var zScore = _pair.ZScore;
if (Math.Abs(zScore) > .5m && !Portfolio.Invested)
{
MarketOrder("GLD", -1000 * Math.Sign(zScore));
MarketOrder("GDX", (int)Math.Round(1000 * _pair.Beta));
}
if (Portfolio.Invested)
{
if (zScore * Math.Sign(Portfolio["GLD"].Quantity) < 0)
{
Liquidate();
}
}
}
}
public class PairCalculation
{
private int _tradingSize = 30;
private int _trainingSize = 90;
private RollingWindow<IBaseData> _xWindow;
private RollingWindow<IBaseData> _yWindow;
private int _period = 5;
private SimpleMovingAverage _sma;
private StandardDeviation _std;
public decimal ZScore { get; private set; }
public decimal Beta { get; private set; }
public PairCalculation()
{
_xWindow = new RollingWindow<IBaseData>(_trainingSize);
_yWindow = new RollingWindow<IBaseData>(_trainingSize);
_sma = new SimpleMovingAverage(_period);
_std = new StandardDeviation(_period);
}
public bool Update(IBaseData dataOne, IBaseData dataTwo)
{
var time = dataOne.EndTime;
var spread = dataOne.Price - Beta * dataTwo.Price;
_xWindow.Add(dataOne);
_yWindow.Add(dataTwo);
if (!_xWindow.IsReady || !_yWindow.IsReady) return false;
//
if (_xWindow.Samples == 90)//_xWindow.Samples % _tradingSize == 0)
{
var x = _xWindow.OrderBy(t => t.EndTime).Select(a => (double)a.Price).ToArray();
var y = _yWindow.OrderBy(t => t.EndTime).Select(a => (double)a.Price).ToArray();
var ols = Fit.Line(x, y);
Beta = 1 / (decimal)ols.Item2;
var spreadWindow = Enumerable.Zip(_xWindow, _yWindow,
(a, b) => new IndicatorDataPoint(a.EndTime, a.Price - Beta * b.Price))
.OrderBy(t => t.EndTime);
_sma.Reset();
_std.Reset();
foreach (var input in spreadWindow)
{
_sma.Update(input);
_std.Update(input);
}
ZScore = (spreadWindow.Last() - _sma) / _std;
}
else
{
_sma.Update(time, spread);
_std.Update(time, spread);
ZScore = (spread - _sma) / _std;
}
return true;
}
}
}