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