Overall Statistics |
Total Trades 8 Average Win 0.82% Average Loss -0.01% Compounding Annual Return 91.694% Drawdown 0.100% Expectancy 21.111 Net Profit 0.794% Sharpe Ratio 8.555 Loss Rate 67% Win Rate 33% Profit-Loss Ratio 65.33 Alpha 0.206 Beta 0.225 Annual Standard Deviation 0.047 Annual Variance 0.002 Information Ratio -3.079 Tracking Error 0.15 Treynor Ratio 1.776 Total Fees $8.00 |
/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Data; using QuantConnect.Orders; namespace QuantConnect.Algorithm.CSharp { /// <summary> /// In this algorithm we submit/update/cancel each order type /// </summary> public class OrderTicketDemoAlgorithm : QCAlgorithm { private const string Symbol = "SPY"; private readonly List<OrderTicket> _openMarketOnOpenOrders = new List<OrderTicket>(); private readonly List<OrderTicket> _openMarketOnCloseOrders = new List<OrderTicket>(); private readonly List<OrderTicket> _openLimitOrders = new List<OrderTicket>(); private readonly List<OrderTicket> _openStopMarketOrders = new List<OrderTicket>(); private readonly List<OrderTicket> _openStopLimitOrders = new List<OrderTicket>(); /// <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(2013, 10, 7); //Set Start Date SetEndDate(2013, 10, 11); //Set End Date SetCash(100000); //Set Strategy Cash // Find more symbols here: http://quantconnect.com/data AddSecurity(SecurityType.Equity, Symbol, Resolution.Minute); } /// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> /// <param name="data">Slice object keyed by symbol containing the stock data</param> public override void OnData(Slice data) { // MARKET ORDERS MarketOrders(); // LIMIT ORDERS LimitOrders(); // STOP MARKET ORDERS StopMarketOrders(); // STOP LIMIT ORDERS StopLimitOrders(); // MARKET ON OPEN ORDERS MarketOnOpenOrders(); // MARKET ON CLOSE ORDERS MarketOnCloseOrders(); } /// <summary> /// MarketOrders are the only orders that are processed synchronously by default, so /// they'll fill by the next line of code. This behavior equally applies to live mode. /// You can opt out of this behavior by specifying the 'asynchronous' parameter as true. /// </summary> private void MarketOrders() { if (TimeIs(7, 9, 31)) { Log("Submitting MarketOrder"); // submit a market order to buy 10 shares, this function returns an OrderTicket object // we submit the order with asynchronous:false, so it block until it is filled var newTicket = MarketOrder(Symbol, 10, asynchronous: false); if (newTicket.Status != OrderStatus.Filled) { Log("Synchronous market order was not filled synchronously!"); Quit(); } // we can also submit the ticket asynchronously. In a backtest, we'll still perform // the fill before the next time events for your algorithm. here we'll submit the order // asynchronously and try to cancel it, sometimes it will, sometimes it will be filled // first. newTicket = MarketOrder(Symbol, 10, asynchronous: true); var response = newTicket.Cancel("Attempt to cancel async order"); if (response.IsSuccess) { Log("Successfully canceled async market order: " + newTicket.OrderId); } else { Log("Unable to cancel async market order: " + response.ErrorCode); } } } /// <summary> /// LimitOrders are always processed asynchronously. Limit orders are used to /// set 'good' entry points for an order. For example, you may wish to go /// long a stock, but want a good price, so can place a LimitOrder to buy with /// a limit price below the current market price. Likewise the opposite is true /// when selling, you can place a LimitOrder to sell with a limit price above the /// current market price to get a better sale price. /// You can submit requests to update or cancel the LimitOrder at any time. /// The 'LimitPrice' for an order can be retrieved from the ticket using the /// OrderTicket.Get(OrderField) method, for example: /// <code> /// var currentLimitPrice = orderTicket.Get(OrderField.LimitPrice); /// </code> /// </summary> private void LimitOrders() { if (TimeIs(7, 12, 0)) { Log("Submitting LimitOrder"); // submit a limit order to buy 10 shares at .1% below the bar's close var close = Securities[Symbol].Close; var newTicket = LimitOrder(Symbol, 10, close * .999m); _openLimitOrders.Add(newTicket); // submit another limit order to sell 10 shares at .1% above the bar's close newTicket = LimitOrder(Symbol, 10, close * 1.001m); _openLimitOrders.Add(newTicket); } // when we submitted new limit orders we placed them into this list, // so while there's two entries they're still open and need processing if (_openLimitOrders.Count == 2) { var openOrders = _openLimitOrders; // check if either is filled and cancel the other var longOrder = openOrders[0]; var shortOrder = openOrders[1]; if (CheckPairOrdersForFills(longOrder, shortOrder)) { _openLimitOrders.Clear(); return; } // if niether order has filled, bring in the limits by a penny var newLongLimit = longOrder.Get(OrderField.LimitPrice) + 0.01m; var newShortLimit = shortOrder.Get(OrderField.LimitPrice) - 0.01m; Log("Updating limits - Long: " + newLongLimit.ToString("0.00") + " Short: " + newShortLimit.ToString("0.00")); longOrder.Update(new UpdateOrderFields { // we could change the quantity, but need to specify it //Quantity = LimitPrice = newLongLimit, Tag = "Update #" + (longOrder.UpdateRequests.Count + 1) }); shortOrder.Update(new UpdateOrderFields { LimitPrice = newShortLimit, Tag = "Update #" + (shortOrder.UpdateRequests.Count + 1) }); } } /// <summary> /// StopMarketOrders work in the opposite way that limit orders do. /// When placing a long trade, the stop price must be above current /// market price. In this way it's a 'stop loss' for a short trade. /// When placing a short trade, the stop price must be below current /// market price. In this way it's a 'stop loss' for a long trade. /// You can submit requests to update or cancel the StopMarketOrder at any time. /// The 'StopPrice' for an order can be retrieved from the ticket using the /// OrderTicket.Get(OrderField) method, for example: /// <code> /// var currentStopPrice = orderTicket.Get(OrderField.StopPrice); /// </code> /// </summary> private void StopMarketOrders() { if (TimeIs(7, 12 + 4, 0)) { Log("Submitting StopMarketOrder"); // a long stop is triggered when the price rises above the value // so we'll set a long stop .25% above the current bar's close var close = Securities[Symbol].Close; var stopPrice = close * 1.0025m; var newTicket = StopMarketOrder(Symbol, 10, stopPrice); _openStopMarketOrders.Add(newTicket); // a short stop is triggered when the price falls below the value // so we'll set a short stop .25% below the current bar's close stopPrice = close * .9975m; newTicket = StopMarketOrder(Symbol, -10, stopPrice); _openStopMarketOrders.Add(newTicket); } // when we submitted new stop market orders we placed them into this list, // so while there's two entries they're still open and need processing if (_openStopMarketOrders.Count == 2) { // check if either is filled and cancel the other var longOrder = _openStopMarketOrders[0]; var shortOrder = _openStopMarketOrders[1]; if (CheckPairOrdersForFills(longOrder, shortOrder)) { _openStopMarketOrders.Clear(); return; } // if niether order has filled, bring in the stops by a penny var newLongStop = longOrder.Get(OrderField.StopPrice) - 0.01m; var newShortStop = shortOrder.Get(OrderField.StopPrice) + 0.01m; Log("Updating stops - Long: " + newLongStop.ToString("0.00") + " Short: " + newShortStop.ToString("0.00")); longOrder.Update(new UpdateOrderFields { // we could change the quantity, but need to specify it //Quantity = StopPrice = newLongStop, Tag = "Update #" + (longOrder.UpdateRequests.Count + 1) }); shortOrder.Update(new UpdateOrderFields { StopPrice = newShortStop, Tag = "Update #" + (shortOrder.UpdateRequests.Count + 1) }); } } /// <summary> /// StopLimitOrders work as a combined stop and limit order. First, the /// price must pass the stop price in the same way a StopMarketOrder works, /// but then we're also gauranteed a fill price at least as good as the /// limit price. This order type can be beneficial in gap down scenarios /// where a StopMarketOrder would have triggered and given the not as beneficial /// gapped down price, whereas the StopLimitOrder could protect you from /// getting the gapped down price through prudent placement of the limit price. /// You can submit requests to update or cancel the StopLimitOrder at any time. /// The 'StopPrice' or 'LimitPrice' for an order can be retrieved from the ticket /// using the OrderTicket.Get(OrderField) method, for example: /// <code> /// var currentStopPrice = orderTicket.Get(OrderField.StopPrice); /// var currentLimitPrice = orderTicket.Get(OrderField.LimitPrice); /// </code> /// </summary> private void StopLimitOrders() { if (TimeIs(8, 12, 1)) { Log("Submitting StopLimitOrder"); // a long stop is triggered when the price rises above the value // so we'll set a long stop .25% above the current bar's close // now we'll also be setting a limit, this means we are gauranteed // to get at least the limit price for our fills, so make the limit // price a little softer than the stop price var close = Securities[Symbol].Close; var stopPrice = close * 1.001m; var limitPrice = close - 0.03m; var newTicket = StopLimitOrder(Symbol, 10, stopPrice, limitPrice); _openStopLimitOrders.Add(newTicket); // a short stop is triggered when the price falls below the value // so we'll set a short stop .25% below the current bar's close // now we'll also be setting a limit, this means we are gauranteed // to get at least the limit price for our fills, so make the limit // price a little softer than the stop price stopPrice = close * .999m; limitPrice = close + 0.03m; newTicket = StopLimitOrder(Symbol, -10, stopPrice, limitPrice); _openStopLimitOrders.Add(newTicket); } // when we submitted new stop limit orders we placed them into this list, // so while there's two entries they're still open and need processing if (_openStopLimitOrders.Count == 2) { // check if either is filled and cancel the other var longOrder = _openStopLimitOrders[0]; var shortOrder = _openStopLimitOrders[1]; if (CheckPairOrdersForFills(longOrder, shortOrder)) { _openStopLimitOrders.Clear(); return; } // if niether order has filled, bring in the stops/limits in by a penny var newLongStop = longOrder.Get(OrderField.StopPrice) - 0.01m; var newLongLimit = longOrder.Get(OrderField.LimitPrice) + 0.01m; var newShortStop = shortOrder.Get(OrderField.StopPrice) + 0.01m; var newShortLimit = shortOrder.Get(OrderField.LimitPrice) - 0.01m; Log("Updating stops - Long: " + newLongStop.ToString("0.00") + " Short: " + newShortStop.ToString("0.00")); Log("Updating limits - Long: " + newLongLimit.ToString("0.00") + " Short: " + newShortLimit.ToString("0.00")); longOrder.Update(new UpdateOrderFields { // we could change the quantity, but need to specify it //Quantity = StopPrice = newLongStop, LimitPrice = newLongLimit, Tag = "Update #" + (longOrder.UpdateRequests.Count + 1) }); shortOrder.Update(new UpdateOrderFields { StopPrice = newShortStop, LimitPrice = newShortLimit, Tag = "Update #" + (shortOrder.UpdateRequests.Count + 1) }); } } /// <summary> /// MarketOnCloseOrders are always executed at the next market's closing /// price. The only properties that can be updated are the quantity and /// order tag properties. /// </summary> private void MarketOnCloseOrders() { if (TimeIs(9, 12, 0)) { Log("Submitting MarketOnCloseOrder"); // open a new position or triple our existing position var qty = Portfolio[Symbol].Quantity; qty = qty == 0 ? 100 : 2*qty; var newTicket = MarketOnCloseOrder(Symbol, qty); _openMarketOnCloseOrders.Add(newTicket); } if (_openMarketOnCloseOrders.Count == 1 && Time.Minute == 59) { var ticket = _openMarketOnCloseOrders[0]; // check for fills if (ticket.Status == OrderStatus.Filled) { _openMarketOnCloseOrders.Clear(); return; } var quantity = ticket.Quantity + 1; Log("Updating quantity - New Quantity: " + quantity); // we can update the quantity and tag ticket.Update(new UpdateOrderFields { Quantity = quantity, Tag = "Update #" + (ticket.UpdateRequests.Count + 1) }); } if (TimeIs(EndDate.Day, 12 + 3, 45)) { Log("Submitting MarketOnCloseOrder to liquidate end of algorithm"); MarketOnCloseOrder(Symbol, -Portfolio[Symbol].Quantity, "Liquidate end of algorithm"); } } /// <summary> /// MarketOnOpenOrders are always executed at the next market's opening /// price. The only properties that can be updated are the quantity and /// order tag properties. /// </summary> private void MarketOnOpenOrders() { if (TimeIs(8, 12 + 2, 0)) { Log("Submitting MarketOnOpenOrder"); // its EOD, let's submit a market on open order to short even more! var newTicket = MarketOnOpenOrder(Symbol, 50); _openMarketOnOpenOrders.Add(newTicket); } if (_openMarketOnOpenOrders.Count == 1 && Time.Minute == 59) { var ticket = _openMarketOnOpenOrders[0]; // check for fills if (ticket.Status == OrderStatus.Filled) { _openMarketOnOpenOrders.Clear(); return; } var quantity = ticket.Quantity + 1; Log("Updating quantity - New Quantity: " + quantity); // we can update the quantity and tag ticket.Update(new UpdateOrderFields { Quantity = quantity, Tag = "Update #" + (ticket.UpdateRequests.Count + 1) }); } } public override void OnOrderEvent(OrderEvent orderEvent) { var order = Transactions.GetOrderById(orderEvent.OrderId); Console.WriteLine("{0}: {1}: {2}", Time, order.Type, orderEvent); } private bool CheckPairOrdersForFills(OrderTicket longOrder, OrderTicket shortOrder) { if (longOrder.Status == OrderStatus.Filled) { Log(shortOrder.OrderType + ": Cancelling short order, long order is filled."); shortOrder.Cancel("Long filled."); return true; } if (shortOrder.Status == OrderStatus.Filled) { Log(longOrder.OrderType + ": Cancelling long order, short order is filled."); longOrder.Cancel("Short filled"); return true; } return false; } private bool TimeIs(int day, int hour, int minute) { return Time.Day == day && Time.Hour == hour && Time.Minute == minute; } } }