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