| Overall Statistics |
|
Total Trades 1898 Average Win 0.63% Average Loss -0.38% Compounding Annual Return 18.064% Drawdown 8.900% Expectancy 0.246 Net Profit 134.423% Sharpe Ratio 1.752 Loss Rate 53% Win Rate 47% Profit-Loss Ratio 1.66 Alpha 0.169 Beta 0.086 Annual Standard Deviation 0.098 Annual Variance 0.01 Information Ratio 1.547 Tracking Error 0.098 Treynor Ratio 1.985 Total Fees $124215.06 |
using System;
using QuantConnect.Algorithm;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
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
{
/**
* The script appears to be using the current price, but the RSI value from the last
bar. Add consolidators for all of the Index tickers. Add a set to keep track of
which tickers have been updated by the consolidator and don't do any calculations
until all the tickers have been updated.
*/
private SimpleMovingAverage uvxyAverage;
private SimpleMovingAverage uvxyLongAverage;
private bool positionOpen = false;
private SpreadSlippageModel slippageModel = new SpreadSlippageModel();
//Index tickers
private String[] indexTickers = {"SPY", "DIA", "IWM", "QQQ"};
private Dictionary<string,RelativeStrengthIndex> trendMap = new Dictionary<string,RelativeStrengthIndex>();
private String volIndex = "UVXY";
private String volTicker = "XIV";
private bool closeEOD = false;
private String maTicker;
private bool allUpdated = false;
private HashSet<String> completedSet = new HashSet<String>();
//Initialize the data and resolution you require for your strategy:
public override void Initialize()
{
maTicker = volIndex;
//Start and End Date range for the backtest:
SetStartDate(2013, 1, 1);
SetEndDate(DateTime.Now.Date.AddDays(-1));
//Cash allocation
SetCash(1000000);
foreach( String indexTicker in indexTickers ) {
AddSecurity(SecurityType.Equity, indexTicker, Resolution.Minute);
Securities[indexTicker].SlippageModel = slippageModel;
var consolidator = new TradeBarConsolidator(TimeSpan.FromMinutes(5));
consolidator.DataConsolidated += FiveMinuteHandler;
RelativeStrengthIndex trend = new RelativeStrengthIndex(3, MovingAverageType.Simple);
RegisterIndicator(indexTicker, trend, consolidator);
var history = History(indexTicker, TimeSpan.FromDays(5), Resolution.Minute);
foreach (var tradeBar in history)
{
trend.Update(tradeBar.EndTime, tradeBar.Close);
}
trendMap[indexTicker] = trend;
}
AddSecurity(SecurityType.Equity, volIndex, Resolution.Minute);
AddSecurity(SecurityType.Equity, volTicker, Resolution.Minute);
var fiveMinuteConsolidator2 = new TradeBarConsolidator(TimeSpan.FromMinutes(5));
fiveMinuteConsolidator2.DataConsolidated += FiveMinuteHandler;
SetBrokerageModel(new InteractiveBrokersBrokerageModel());
uvxyAverage = new SimpleMovingAverage(20);
uvxyLongAverage = new SimpleMovingAverage(50);
var tradeBarHistory = History(maTicker, TimeSpan.FromDays(5), Resolution.Minute);
foreach (var tradeBar in tradeBarHistory)
{
uvxyAverage.Update(tradeBar.EndTime, tradeBar.Close);
uvxyLongAverage.Update(tradeBar.EndTime, tradeBar.Close);
}
RegisterIndicator(maTicker, uvxyAverage, fiveMinuteConsolidator2);
RegisterIndicator(maTicker, uvxyLongAverage, fiveMinuteConsolidator2);
}
private void FiveMinuteHandler(object sender, TradeBar consolidatedBar) {
DateTime time = consolidatedBar.Time;
Log( "UVXY Ready: " + uvxyAverage.IsReady + " uvxyLongAverageReady: " + uvxyLongAverage.IsReady);
if( uvxyAverage.IsReady && uvxyLongAverage.IsReady) {
Log("Short Avg: " + uvxyAverage + " Long Avg: " + uvxyLongAverage );
foreach(KeyValuePair<string, RelativeStrengthIndex> entry in trendMap)
{decimal prc = Securities[entry.Key].Price;
if(entry.Value > 0 ) {
decimal amt = (prc-entry.Value)/ entry.Value;
Log("RSI for: " + entry.Key + " is: " + entry.Value + " last: " + prc + " propValue: " + amt);
} else {
Log("RSI value is zero for: " + entry.Key + " last: " + prc);
}
}
if( uvxyAverage < uvxyLongAverage && ! positionOpen) {
if( time.Hour < 15 || (time.Hour == 15 && time.Minute <= 60) ) {
decimal lowest = 100000;
decimal highest= 0;
String lowestTicker = "";
String highestTicker = "";
foreach(KeyValuePair<string, RelativeStrengthIndex> entry in trendMap)
{
if(entry.Value > 0 ) {
decimal price = Securities[entry.Key].Price;
decimal amount = (price-entry.Value)/ entry.Value;
//decimal amount = entry.Value;
if(amount < lowest) {
lowest = amount;
lowestTicker = entry.Key;
}
if( amount > highest ) {
highest = amount;
highestTicker = entry.Key;
}
}
}
Log("Trade triggered, selecting: " + lowestTicker);
positionOpen = true;
decimal size = 1.0m;
SetHoldings(lowestTicker, size);
}
} else {
if( uvxyAverage > uvxyLongAverage && positionOpen ) {
if( time.Hour < 15 || (time.Hour == 15 && time.Minute <= 60) ) {
positionOpen =false;
Liquidate();
}
}
}
if( consolidatedBar.Time.Hour == 15 && consolidatedBar.Time.Minute >= 55 ) {
if( closeEOD ) {
positionOpen = false;
Liquidate();
}
}
} else {
Console.WriteLine("Warming up");
}
}
//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(data.ContainsKey(volIndex) ) {
Plot(volIndex, "Price", data[volIndex].Price);
Plot(volIndex, uvxyAverage, uvxyLongAverage);
}
if (!Portfolio.HoldStock)
{
}
}
}
}namespace QuantConnect {
public class SpreadSlippageModel : ISlippageModel
{
private readonly decimal _slippagePercent;
/// <summary>
/// Initializes a new instance of the <see cref="ConstantSlippageModel"/> class
/// </summary>
/// <param name="slippagePercent">The slippage percent for each order. Percent is ranged 0 to 1.</param>
public SpreadSlippageModel()
{
}
/// <summary>
/// Slippage Model. Return a decimal cash slippage approximation on the order.
/// </summary>
public decimal GetSlippageApproximation(Security asset, Order order)
{
return 0.01m;
}
}
}