| Overall Statistics |
|
Total Trades 20 Average Win 0.25% Average Loss -0.48% Compounding Annual Return -0.720% Drawdown 2.300% Expectancy -0.235 Net Profit -1.140% Sharpe Ratio -0.553 Loss Rate 50% Win Rate 50% Profit-Loss Ratio 0.53 Alpha -0.008 Beta 0.009 Annual Standard Deviation 0.013 Annual Variance 0 Information Ratio -0.903 Tracking Error 0.107 Treynor Ratio -0.799 Total Fees $109.47 |
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Data.Consolidators;
using System.Collections;
using QuantConnect.Securities;
using QuantConnect.Models;
namespace QuantConnect.Algorithm.Examples
{
/// <summary>
/// Algorithm that detects over night gaps
/// </summary>
public class GapAlgorithm : QCAlgorithm
{
//parameters go here
const decimal StopLossPercent = 0.012m;
const decimal TakeProfitPercent = 1.0m;
//const decimal StopLossPercent = 0.025m;
//const decimal TakeProfitPercent = 0.08m;
private OrderTicket CurrentOrder;
private OrderTicket StopLoss;
private OrderTicket ProfitTarget;
// these are open/close minute bars
// we'll set the open at the beginning of each day to detect gaps
TradeBar open;
// we'll set the close at the end of each day
TradeBar close;
private RateOfChangePercent ROCP_1_CFO ;
//private RateOfChange ROCPROCPyear;
//private ROCPRateOfChangePercent ROCPROCPyear;
//Define required variables:
//int quantity = 1;
decimal price = 0;
decimal price_1 = 0;
decimal price_01 = 1;
private string symbol="SPY";
private string symbol_1="SPY";
private decimal gapChange=0.0m;
private decimal ROCP_CFO=0.0m;
RollingWindow<TradeBar> _window = new RollingWindow<TradeBar>(2);
//Set the consolidator period:
private TimeSpan _barPeriod = TimeSpan.FromDays(1);
//Consolidator Class:
private Consolidator _consolidator;
//Initialize the data and resolution you require for your strategy:
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
/// </summary>
public override void Initialize()
{
SetStartDate(2004, 01, 01);
//SetStartDate(2009, 01, 01);//
// SetStartDate(1998, 01, 01);
//SetStartDate(2002, 07, 03);
//SetStartDate(2016, 01, 03);
//SetEndDate(2016, 02, 01);
SetEndDate(2005, 08, 01);
//SetEndDate(DateTime.Now.Date.AddDays(-1));
// SetEndDate(2016, 11, 05);
AddSecurity(SecurityType.Equity, symbol, Resolution.Minute);
AddSecurity(SecurityType.Equity, symbol_1, Resolution.Minute);
ROCP_1_CFO = new RateOfChangePercent("SPY", 5); // 252 trading days in a US year
// RegisterIndicator(symbol, Resolution.Minute, Field.Close);
RegisterIndicator(symbol, ROCP_1_CFO, Resolution.Minute, Field.Close);
//Resolution.Minute Resolution.Hour Tick Second
//Setup Consolidator bar bar
_consolidator = new Consolidator(_barPeriod);
}
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="data">TradeBars IDictionary object with your stock data</param>
public void OnData(TradeBars data)
{
//Date gets updated until the consolidator period and then returns true:
if (_consolidator.Update(data["SPY"]))
{
var bar = _consolidator.Bar;
_window.Add(bar);
if (!_window.IsReady) return;
//Log("T: " + bar.Time.ToShortTimeString() + " O: " + bar.Open.ToString("C") + " H: " + bar.High.ToString("C") + " L: " + bar.Low.ToString("C") + " C: " + bar.Close.ToString("C"));
//Log("T: " + bar.Time.ToShortTimeString() + " O: " + bar.Open.ToString("C") + " H: " + bar.High.ToString("C") + " L: " + bar.Low.ToString("C") + " C: " + bar.Close.ToString("C"));
//Log("T: " + bar.Time.ToShortTimeString() + " O: " + bar.Open.ToString("C") + " H: " + bar.High.ToString("C") + " L: " + bar.Low.ToString("C") + " C: " + bar.Close.ToString("C"));
//Console.WriteLine(" _window[0].High", _window[0].High);
//Log( " H: " + _window[0].High.ToString("C") );
}
// populate our opening price variable
if (open == null || open.Time.Date != Time.Date)
{
// when TryGetValue succeeds it will populate the 'open'
// variable with our first minute bar of the day (at 9:31, the bar that spans from 9:30->9:31)
// if it fails then 'open' will have a value of null
data.TryGetValue("SPY", out open);
if (open != null && close != null && open.Time.Date != close.Time.Date)
{
// The close of yesterday is greater than the open today.
// Gap_Down = Close[1] > Open[0]
//bool gapDown = close.Close > open.Open;
//decimal gapChange = open.Open/close.Close - 1m;
gapChange = open.Open/close.Close - 1m;
}
}
if (!_window.IsReady) return;
price = data[symbol].Close;
price_1 = data[symbol_1].Close;
//Get fresh cash balance: Set purchase quantity to equivalent 10% of portfolio.
decimal cash = Portfolio.Cash;
int holdings = Portfolio[symbol_1].Quantity;
ROCP_CFO = price / open.Open-1.0m;
//if (ROCP_CFO.IsReady && gapChange > 0.003m && ROCP_CFO > 0.0005m && Time.TimeOfDay == new TimeSpan(9, 37, 0) )
//if (ROCP_CFO.IsReady && gapChange > 0.003m && ROCP_CFO > 0.25m && Time.TimeOfDay == new TimeSpan(9, 37, 0) )
if (( gapChange > 0.003m && Time.TimeOfDay == new TimeSpan(9, 36, 0) )
&& ( ROCP_CFO>0.001m && price > _window[0].High ) && ((holdings < 0 || holdings == 0)))
{
Console.WriteLine(Time + " - GapUp: " + gapChange.ToString("0.000") );
Console.WriteLine(Time + " - CFO: " + ROCP_CFO);
//quantity = Convert.ToInt32((cash * 1.0m)/ price_1);
// Calculate quantity based on available cash
var quantity = (int) (Portfolio.Cash / price_1);
//Console.WriteLine(Time + " -Quantity: " + quantity);
//Now go long: Short-EMA crossed above long-EMA by sufficient margin
//SetHoldings(symbol_1, 1.0m);
price_01=price_1;
Log( "Pre_order: " + quantity);
Log( "Pre_order: " + holdings);
CurrentOrder = Order(symbol_1, quantity);
//Log(Time.ToShortDateString() + "> Go Long > Holdings: " + holdings.ToString() + " Quantity:" + quantity.ToString() + " Samples: " + ROCP_CFO.Samples);
// Set StopLoss order
StopLoss = StopMarketOrder(symbol_1, -quantity, price_1 * (1m - StopLossPercent));
// Set Profit Target
ProfitTarget = LimitOrder(symbol_1, -quantity, price_1 * (1m + TakeProfitPercent));
Log( "ROCP_1_CFO: " + ROCP_1_CFO );
Log( "holdings: " + holdings );
Log( "PT order id: " + ProfitTarget.OrderId);
Log( "StopLoss order id: " + StopLoss.OrderId);
}
if (Time.TimeOfDay == new TimeSpan(9, 40, 0) && ((holdings != 0)))
{
// Set StopLoss order
//StopLoss = StopMarketOrder(symbol_1, -Convert.ToInt32(0.5m* holdings), price_1 * (1m - StopLossPercent));
// Set Profit Target
//ProfitTarget = LimitOrder(symbol_1, -Convert.ToInt32(0.5m* holdings), price_1 * (1m + TakeProfitPercent));
Log( "Stop loss : " + holdings);
// Set StopLoss order
// StopLoss = StopMarketOrder(symbol_1, -1* holdings, price_01 * (1m - StopLossPercent));
// Set Profit Target
// ProfitTarget = LimitOrder(symbol_1, -1* holdings, price_01 * (1m + TakeProfitPercent));
Log( "holdings: " + holdings );
// Log( "PT order id: " + ProfitTarget.OrderId);
// Log( "StopLoss order id: " + StopLoss.OrderId);
}
//if (( gapChange < -0.003m && Time.TimeOfDay == new TimeSpan(9, 36, 0) )
//&& ( Math.Abs(ROCP_CFO) > 0.003m && price < Math.Min (_window[0].Low, _window[1].Low)) && (( holdings == 0)))
//{
// Console.WriteLine(Time + " - GapUp: " + gapChange.ToString("0.000") );
// Console.WriteLine(Time + " - CFO: " + ROCP_CFO);
//Now go long: Short-EMA crossed above long-EMA by sufficient margin
// CurrentOrder = Order(symbol_1, Math.Sign(ROCP_CFO) * quantity);
// Log(Time.ToShortDateString() + "> Go Long > Holdings: " + holdings.ToString() + " Quantity:" + quantity.ToString() + " Samples: " + ROCP_CFO.Samples);
// Set StopLoss order
// StopLoss = StopMarketOrder(symbol, -1* Math.Sign(ROCP_CFO) * quantity, price * (1m - StopLossPercent));
// Set Profit Target
// ProfitTarget = LimitOrder(symbol, -1*Math.Sign(ROCP_CFO) * quantity, price * (1m + TakeProfitPercent));
// Log( "ROCP_1_CFO: " + ROCP_1_CFO );
// }
//if ( ( ROCP_1_CFO > 0.25m && price > _window[0].Close ) && ((holdings < 0 || holdings == 0)))
//{
// Console.WriteLine(Time + " - GapUp: " + gapChange.ToString("0.000") );
// Console.WriteLine(Time + " - CFO: " + ROCP_CFO);
//Now go long: Short-EMA crossed above long-EMA by sufficient margin
// Order(symbol, Math.Abs(holdings) + quantity);
//Log(Time.ToShortDateString() + "> Go Long > Holdings: " + holdings.ToString() + " Quantity:" + quantity.ToString() + " Samples: " + ROCP_CFO.Samples);
//}
// Log( "Outside H: " + _window[0].High.ToString("C") );
// we get our last minute bar at 4:00, market is closed,
// save it into our 'close' variable
if (Time.TimeOfDay.TotalHours == 16)
{
// when TryGetValue succeeds it will populate the 'close'
// variable with our final minute bar of the day (at $:00)
// if it fails then 'close' will have a value of null
data.TryGetValue("SPY", out close);
}
// at 3:58 liquidate
if (Portfolio.Invested && Time.TimeOfDay == new TimeSpan(15, 58, 0) && (holdings != 0))
{
Console.WriteLine(Time + " EOD order: " + holdings );
Log( "3:58pm holdings: " + holdings );
Liquidate();
}
}
// 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 (ProfitTarget == null || StopLoss == null)
{
return;
}
var filledOrderId = orderEvent.OrderId;
// If the ProfitTarget order was filled, close the StopLoss order
if (ProfitTarget.OrderId == filledOrderId)
{
Console.WriteLine(Time +" ProfitTarget is filled " +ProfitTarget.OrderId + "Filled Order is " + filledOrderId );
Log( "ProfitTarget is filled " +ProfitTarget.OrderId );
StopLoss.Cancel();
}
// If the StopLoss order was filled, close the ProfitTarget
if (StopLoss.OrderId == filledOrderId)
{
Console.WriteLine(Time + " StopLoss Order is filled " +StopLoss.OrderId + "Filled Order is " + filledOrderId );
Log( "StopLoss Order is filled " +StopLoss.OrderId );
ProfitTarget.Cancel();
}
}
}
}using System;
using System.Collections;
using System.Collections.Generic;
using QuantConnect.Securities;
using QuantConnect.Models;
namespace QuantConnect
{
/*
* TimeSpanConsolidator Helper Routine: Assemble generic timespan bar lengths: e.g. 10 minutes:
*
* 1. Setup the new Consolidator class with the timespan period:
* var _consolidator = new Consolidator(TimeSpan.FromMinutes(10));
*
* 2. Add in the data with the update routine. It will return true when bar ready
* if (_consolidator.Update(data["MSFT"])) { UseBar }
*/
public class Consolidator
{
private TradeBar _resultBar;
private TradeBar _workingBar;
private DateTime _start;
private TimeSpan _period;
//Result:
public TradeBar Bar
{
get
{
return _resultBar;
}
}
//Constructor: Set the period we'd like to scan
public Consolidator(TimeSpan span)
{
this._period = span;
this._resultBar = new TradeBar();
this._workingBar = new TradeBar(new DateTime(), "", Decimal.Zero, Decimal.MinValue, Decimal.MaxValue, 0, 0);
}
//Submit this bar, return true if we've started a new one.
public bool Update(TradeBar newBar)
{
//Intialize:
if (_start == new DateTime())
{
_start = newBar.Time;
}
//While we're less than end date, keep adding to this bar:
if (newBar.Time < (_start + _period))
{
//Building bar:
AddToBar(newBar);
return false;
}
else
{
//Completed bar: start new one:
_resultBar = _workingBar;
//Create a new bar:
_workingBar = new TradeBar(newBar.Time, newBar.Symbol, Decimal.Zero, Decimal.MinValue, Decimal.MaxValue, 0, 0);
//Start of this bar:
_start = newBar.Time;
AddToBar(newBar);
return true;
}
}
//Add to a tradebar
private void AddToBar(TradeBar newBar)
{
//Add this data to working bar:
if (_workingBar.Time == new DateTime()) _workingBar.Time = newBar.Time;
if (_workingBar.Symbol == "") _workingBar.Symbol = newBar.Symbol;
if (_workingBar.Open == Decimal.Zero) _workingBar.Open = newBar.Open;
if (newBar.High > _workingBar.High) _workingBar.High = newBar.High;
if (newBar.Low < _workingBar.Low) _workingBar.Low = newBar.Low;
_workingBar.Close = newBar.Close;
_workingBar.Volume = newBar.Volume;
}
}
}