| Overall Statistics |
|
Total Trades 668 Average Win 0.09% Average Loss -0.07% Compounding Annual Return 1.713% Drawdown 1.500% Expectancy 0.033 Net Profit 0.710% Sharpe Ratio 0.543 Loss Rate 54% Win Rate 46% Profit-Loss Ratio 1.24 Alpha 0.011 Beta 0.019 Annual Standard Deviation 0.022 Annual Variance 0 Information Ratio -0.345 Tracking Error 0.129 Treynor Ratio 0.645 Total Fees $1492.24 |
namespace QuantConnect {
//
// Make sure to change "BasicTemplateAlgorithm" to your algorithm class name, and that all
// files use "public partial class" if you want to split up your algorithm namespace into multiple files.
//
//public partial class BasicTemplateAlgorithm : QCAlgorithm, IAlgorithm
//{
// Extension functions can go here...(ones that need access to QCAlgorithm functions e.g. Debug, Log etc.)
//}
//public class Indicator
//{
// ...or you can define whole new classes independent of the QuantConnect Context
//}
class Utility{
/// <summary>
/// Returns the business date (EventDate + post days) after accouning for weekends; will not take into other exchange holidays
/// </summary>
/// <param name="EventDate"></param>
/// <param name="postDays"></param>
/// <returns></returns>
public static DateTime getPostBusinessDate(DateTime EventDate, int postDays)
{
DateTime applicableDate = EventDate;
//Checking if the event itself occurs on a weekend
if (postDays == 0)
while (applicableDate.DayOfWeek == DayOfWeek.Saturday || applicableDate.DayOfWeek == DayOfWeek.Sunday)
applicableDate = applicableDate.AddDays(1);
for (int tempCount = 0; tempCount < Math.Abs(postDays); tempCount++)
{
applicableDate = applicableDate.AddDays(Math.Sign(postDays));
while (applicableDate.DayOfWeek == DayOfWeek.Saturday || applicableDate.DayOfWeek == DayOfWeek.Sunday)
applicableDate = applicableDate.AddDays(Math.Sign(postDays));
}
return applicableDate;
}
public static List<DateTime> getDaysAroundEvent(List<DateTime> eventDates, int NbPreDays, int NbPostDays)
{
List<DateTime> _finalDates = new List<DateTime>();
foreach(DateTime eventDate in eventDates)
{
int count = -NbPreDays;
while(count<=NbPostDays)
{
_finalDates.Add(getPostBusinessDate(eventDate,count));
count++;
}
}
return _finalDates;
}
}
}using System;
using System.Collections;
using System.Collections.Generic;
using MathNet.Numerics.Statistics;
using MathNet.Numerics;
using System.Linq;
namespace QuantConnect {
//Returns the channel end values based on previous Trade bar
public class PolynomialRegression
{
private int _period;
public int period {get{return _period;}}
private double _deviation;
private int _power;
private int _samples;
private decimal _oneStdDev;
public decimal oneStdDev {get{return _oneStdDev;}}
private decimal _lastPredictedValue;
public decimal lastPredictedValue {get{return _lastPredictedValue;}}
private decimal _slope;
public decimal slope {get{return _slope;}}
private double[] _slopeCoefficients;
public double[] slopeCoefficients {get{return _slopeCoefficients;}}
private decimal _upBand;
public decimal upBand {get{return _upBand;}}
private decimal _lowBand;
public decimal lowBand {get{return _lowBand;}}
//Will check if the value from the indicator should be used or not
private bool _isReady;
public bool isReady {get{return _isReady;}}
private List<double> _yBuffer;
public List<double> yBuffer {get{return _yBuffer;}}
private List<double> _xBuffer;
public List<double> xBuffer {get{return _xBuffer;}}
public PolynomialRegression (int period, double deviation, int power)
{
this._period = period;
this._deviation = deviation;
this._power = power;
this._samples = 0;
this._yBuffer = new List<double>();
this._xBuffer = new List<double>();
//_xBuffer.Add(1);
}
public void AddSample(decimal price)
{
if (_samples<_period)
{
_slope = 1m;
_upBand = price;
_lowBand =price;
_isReady = false;
_samples++;
_yBuffer.Add((double)price);
_xBuffer.Add(_samples);
return;
}
else
{
_slopeCoefficients = Fit.Polynomial(_xBuffer.ToArray(), _yBuffer.ToArray(), _power);
_isReady = true;
double[] _errors = new double[_xBuffer.Count()];
double[] _modelledValues = new double[_yBuffer.Count()];
for (int _curCount = 0;_curCount<_xBuffer.Count();_curCount++)
{
//_modelledValues[_curCount] = Evaluate.Polynomial(_xBuffer[_curCount], _slopeCoefficients);
_modelledValues [_curCount] = _slopeCoefficients[0] + _slopeCoefficients[1]*_xBuffer[_curCount];
_errors[_curCount] = _modelledValues[_curCount] - _yBuffer[_curCount];
}
_oneStdDev = (decimal)ArrayStatistics.PopulationStandardDeviation(_errors); //Standard Devation of errors - Population uses N
_lastPredictedValue = (decimal)(_slopeCoefficients[0] + _slopeCoefficients[1]*_xBuffer.Last()); //Getting the last calculated value
_slope = (decimal)_slopeCoefficients[1];
_upBand = _lastPredictedValue + _oneStdDev*(decimal)_deviation;
_lowBand = _lastPredictedValue - _oneStdDev*(decimal)_deviation;
_yBuffer.Add((double)price); //Preparing for next band value
_yBuffer.RemoveAt(0);
}
}
}
//public class Indicator
//{
// ...or you can define whole new classes independent of the QuantConnect Context
//}
}
using System;
namespace QuantConnect
{
// Create 2 channels, operate only during Europe time, Mean Reversion only
public partial class PolynomialRegressionDemo : QCAlgorithm
{
//string symbol = "EURUSD";
// string symbol = "USDJPY";
// string symbol = "AUDUSD";
// string symbol = "USDCAD";
string symbol = "EURCHF";
// string symbol = "EURSEK";
//int europeStartHours = 3; //NY Time
//int usStartHours = 6;
//int usEndHours = 15;
bool isTradeTime;
DateTime[] ECBDays = new DateTime[] { new DateTime(2015,1,22),new DateTime(2015,3,5),new DateTime(2015,4,15),new DateTime(2015,6,3),new DateTime(2015,7,16),new DateTime(2015,9,3),new DateTime(2015,10,22),new DateTime(2015,12,3),new DateTime(2016,1,21),new DateTime(2016,3,10),new DateTime(2016,4,21)};
DateTime[] FedDays = new DateTime[]{ new DateTime(2015,1,28),new DateTime(2015,3,18),new DateTime(2015,4,29),new DateTime(2015,6,17),new DateTime(2015,7,29),new DateTime(2015,9,17),new DateTime(2015,10,28),new DateTime(2015,12,16),new DateTime(2016,1,27),new DateTime(2016,3,16),new DateTime(2016,4,27)};
DateTime[] NFPDays = new DateTime[]{ new DateTime(2015,1,9),new DateTime(2015,2,6),new DateTime(2015,3,6),new DateTime(2015,4,3),new DateTime(2015,5,8),new DateTime(2015,6,5),new DateTime(2015,7,2),new DateTime(2015,8,7),new DateTime(2015,9,4),new DateTime(2015,10,2),new DateTime(2015,11,6),new DateTime(2015,12,4),new DateTime(2016,1,8),new DateTime(2016,2,5),new DateTime(2016,3,4),new DateTime(2016,4,1),new DateTime(2016,5,6),new DateTime(2016,6,3)};
bool _excludeNFP = true;
int _NbNFPPreDays = 4;
int _NbNFPPostDays = 1;
bool _excludeECB = true;
int _NbECBPreDays =2;
int _NbECBPostDays =1;
bool _excludeFed = true;
int _NbFEDPreDays = 0;
int _NbFEDPostDays = 1;
bool isTradDate;
List <DateTime> _excludeDates;
DateTime _latestEventDate;
int _fastPeriod = 300; //Period in minutes
int _slowPeriod = 1200; //Period in minutes
double _fastNbDevBands = 2.0; //Number of deviations (bands)
double _slowNbDevBands = 2; //Number of deviations (band
int _notional = 100000;
double _leverage = 1;
private OrderTicket _orderCurrentOrder;
private OrderTicket _orderStopLoss;
private OrderTicket _orderProfitTarget;
decimal _fastHiBandPrev1, _fastLowBandPrev1;
decimal _prevClose, _entryPrice, _exitPrice;
decimal _stopPrice,_tpPrice;
decimal _NAV;
DateTime _entryTime, _exitTime; //Assumes only 1 trade at a time
char _buyorsell;
//int NbData;
//int _NbOpenOrders = 0;
PolynomialRegression prSlow;
PolynomialRegression prFast;
//Initialize the data and resolution you require for your strategy:
public override void Initialize()
{
SetTimeZone("America/New_York");
SetStartDate(2016,1, 1);
SetEndDate(2016,5, 31);
_excludeDates = new List<DateTime>();
if(_excludeFed==true)
_excludeDates.AddRange(Utility.getDaysAroundEvent(FedDays.ToList(),_NbFEDPreDays, _NbFEDPostDays));
if(_excludeNFP==true)
_excludeDates.AddRange(Utility.getDaysAroundEvent(NFPDays.ToList(),_NbNFPPreDays, _NbNFPPostDays));
if(_excludeECB==true)
_excludeDates.AddRange(Utility.getDaysAroundEvent(ECBDays.ToList(),_NbECBPreDays, _NbECBPostDays));
if(_excludeDates.Count()==0)
_excludeDates.Add(DateTime.Now.AddDays(1));
_excludeDates = _excludeDates.Distinct().ToList();
_excludeDates.Sort(); //Contains list of all sorted events which are to be avoided
_latestEventDate = _excludeDates.ElementAt(0);
AddSecurity(SecurityType.Forex,symbol, Resolution.Minute);
//prUpper time should be greater than prFast by at least 1 period
prSlow = new PolynomialRegression(_slowPeriod,_slowNbDevBands,1);
prFast = new PolynomialRegression(_fastPeriod,_fastNbDevBands,1);
//previous value of Fast channel
_fastHiBandPrev1=0;
_fastLowBandPrev1=0;
_prevClose=0;
//Cash allocation
SetCash(_notional/_leverage); //_leverage = 1 =>No Leverage;
//NbData=0;
var history = History(symbol, _slowPeriod + 10); //Buffer of 10 for the slow channel
foreach (var tradeBar in history)
{
prFast.AddSample( tradeBar.Close);
prSlow.AddSample(tradeBar.Close);
}
}
//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)
{
decimal price = data[symbol].Close;
var holdings = Portfolio[symbol].Quantity;
if(prSlow.isReady==false ) //Wait for upper channel to be ready
{
prFast.AddSample(price);
prSlow.AddSample(price);
return;
}
//if (!Portfolio.HoldStock)
//Time Filters
/*if(Time.Hour>=europeStartHours && Time.Hour<usStartHours)
isTradeTime = true;
else
isTradeTime = false;*/
isTradeTime = true;
if(Time.Date> _latestEventDate && _latestEventDate == _excludeDates.Last())
isTradDate=true;
else
{
if(Time.Date> _latestEventDate)
{ _latestEventDate = _excludeDates.ElementAt(_excludeDates.IndexOf(_latestEventDate)+1);
if(Time.Date == _latestEventDate) //event dates move in days while data comes in minutes
isTradDate = false;
else isTradDate= true;
}
else if(Time.Date == _latestEventDate)
isTradDate = false;
else if(Time.Date < _latestEventDate)
isTradDate = true;
}
if(Time.DayOfWeek == DayOfWeek.Sunday)
isTradDate = false;
if(isTradDate&& isTradeTime)
isTradeTime = true;
else
isTradeTime = false;
if(holdings==0) //No position
{
// if(price<prFast.lowBand & _prevClose>_fastLowBandPrev1 & prFast.slope>0 & prSlow.slope>0 & isTradeTime==true) //Fast channel line of Fast band is greater than price - buy
if(price<prFast.lowBand & _prevClose>_fastLowBandPrev1 & prFast.slope>0 & isTradeTime==true)
{
_stopPrice = price - prFast.oneStdDev * 2.0m;
_tpPrice = price + prFast.oneStdDev *3.0m;
_buyorsell = 'B'; _entryTime = Time;_entryPrice = price; //Recording Trade data
// Debug("Placing Buy Mkt Order now..");
_orderCurrentOrder = Order(symbol, _notional);
//Debug("Purchased " + symbol + " on " + Time.ToString() + " at Price: " + price.ToString("0.0000") + " SL: " + _stopPrice.ToString("0.0000") + " TP: " + _tpPrice.ToString("0.0000") );
_orderStopLoss = StopMarketOrder(symbol, -_notional, _stopPrice);
_orderProfitTarget = LimitOrder(symbol,-_notional, _tpPrice);
}
// else if(price>prFast.upBand & _prevClose<_fastHiBandPrev1 & prFast.slope<0 & prSlow.slope<0 & isTradeTime==true) //higher channel of Fast band is less than price - sell
else if(price>prFast.upBand & _prevClose<_fastHiBandPrev1 & prFast.slope<0 & isTradeTime==true) //higher channel of Fast band is less than price - sell
{
_stopPrice = price + prFast.oneStdDev*2.0m;
_tpPrice = price - prFast.oneStdDev*3.0m;
_buyorsell = 'S'; _entryTime = Time; _entryPrice = price; //Recording Trade data
// Debug("Placing Sell Mkt Order now..");
_orderCurrentOrder = Order(symbol, -_notional);
//Debug("Sold " + symbol + " on " + Time.ToString() + " at Price: " + price.ToString("0.0000") + " SL: " + _stopPrice.ToString("0.0000") + " TP: " + _tpPrice.ToString("0.0000"));
_orderStopLoss = StopMarketOrder(symbol, +_notional, _stopPrice);
_orderProfitTarget = LimitOrder(symbol,+_notional, _tpPrice);
}
}
_fastLowBandPrev1 = prFast.lowBand;
_fastHiBandPrev1 = prFast.upBand;
_prevClose = price;
prFast.AddSample(price);
prSlow.AddSample(price);
// Debug(i.ToString() + " Time " + Time.ToString() + "Price = " + price + " loBand = " + prFast.lowBand + " upBand = " + prFast.upBand);
// Debug ("Last =" + PolynomialRegression._yBuffer.Last().ToString());
//Debug("Time = " + Time.ToString() + "Price = " + price.ToString("0.0000"));
/* Plot("Custom","prUpper_upBand", prSlow.upBand);
Plot("Custom","prFast_upBand", prFast.upBand);
Plot("Custom","prFast_lowBand", prFast.lowBand);
Plot("Custom","prUpper_lowBand", prSlow.lowBand);
Plot("Custom",symbol, data[symbol].Close);
*/
}
// If the StopLoss or ProfitTarget is filled, cancel the other
// If you don't do this, then the ProfitTarget or StopLoss order will remain outstanding
// indefinitely, which will cause very bad behaviors in your algorithm
public override void OnOrderEvent(OrderEvent orderEvent)
{
// Ignore OrderEvents that are not closed
if (!orderEvent.Status.IsClosed()) { return; }
// Defensive check
if (_orderProfitTarget == null || _orderStopLoss == null) { return;}
//This is getting called recursively if an order is canceled
if(orderEvent.Status == OrderStatus.Canceled )
{
// Debug("Canceled outstanding order");
return;
}
var filledOrderId = orderEvent.OrderId;
// Debug("current order ID " +_orderCurrentOrder.OrderId + "Filled Order ID = " + filledOrderId );
if(_orderCurrentOrder.OrderId +3 == filledOrderId)
{
//Debug("Market Order checked");
return;
}
// If the ProfitTarget order was filled, close the StopLoss order
if (_orderProfitTarget.OrderId == filledOrderId)
{
// Debug("TP Order hit");
if(_orderStopLoss.Status != OrderStatus.Filled)
_orderStopLoss.Cancel();
else Liquidate();
}
// If the StopLoss order was filled, close the ProfitTarget
if (_orderStopLoss.OrderId == filledOrderId)
{
// Debug("SL Order hit");
if(_orderProfitTarget.Status != OrderStatus.Filled)
_orderProfitTarget.Cancel();
else Liquidate();
}
_exitPrice = orderEvent.FillPrice; _exitTime = Time;
_NAV = Portfolio.TotalPortfolioValue;
double _thisTradePnL = (double)(_exitPrice-_entryPrice)*_notional;
if(char.ToUpper(_buyorsell).CompareTo('S')==0 )
_thisTradePnL*= -1;
Debug( ",Entry Time," + _entryTime + "," + _entryTime.DayOfWeek + ","+ _entryPrice.ToString("0.0000") + "," + _buyorsell + "," + _exitPrice.ToString("0.0000") + "," + _exitTime + "," + _thisTradePnL.ToString("0") + "," + _NAV.ToString("0") );
}
}
}