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