/*
* 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.Linq;
using QuantConnect.Orders;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using System.Collections.Generic;
using QuantConnect.Securities.Option;
using static QuantConnect.StringExtensions;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Orders.TimeInForces;
namespace QuantConnect.Algorithm
{
public partial class QCAlgorithm
{
private int _maxOrders = 10000;
private bool _isMarketOnOpenOrderWarningSent;
private bool _isMarketOnOpenOrderRestrictedForFuturesWarningSent;
private bool _isGtdTfiForMooAndMocOrdersValidationWarningSent;
private bool _isOptionsOrderOnStockSplitWarningSent;
///
/// Transaction Manager - Process transaction fills and order management.
///
[DocumentationAttribute(TradingAndOrders)]
public SecurityTransactionManager Transactions { get; set; }
///
/// Buy Stock (Alias of Order)
///
/// string Symbol of the asset to trade
/// int Quantity of the asset to trade
///
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Buy(Symbol symbol, int quantity)
{
return Order(symbol, (decimal)Math.Abs(quantity));
}
///
/// Buy Stock (Alias of Order)
///
/// string Symbol of the asset to trade
/// double Quantity of the asset to trade
///
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Buy(Symbol symbol, double quantity)
{
return Order(symbol, Math.Abs(quantity).SafeDecimalCast());
}
///
/// Buy Stock (Alias of Order)
///
/// string Symbol of the asset to trade
/// decimal Quantity of the asset to trade
///
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Buy(Symbol symbol, decimal quantity)
{
return Order(symbol, Math.Abs(quantity));
}
///
/// Buy Stock (Alias of Order)
///
/// string Symbol of the asset to trade
/// float Quantity of the asset to trade
///
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Buy(Symbol symbol, float quantity)
{
return Order(symbol, (decimal)Math.Abs(quantity));
}
///
/// Sell stock (alias of Order)
///
/// string Symbol of the asset to trade
/// int Quantity of the asset to trade
///
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Sell(Symbol symbol, int quantity)
{
return Order(symbol, (decimal)Math.Abs(quantity) * -1);
}
///
/// Sell stock (alias of Order)
///
/// String symbol to sell
/// Quantity to order
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Sell(Symbol symbol, double quantity)
{
return Order(symbol, Math.Abs(quantity).SafeDecimalCast() * -1m);
}
///
/// Sell stock (alias of Order)
///
/// String symbol
/// Quantity to sell
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Sell(Symbol symbol, float quantity)
{
return Order(symbol, (decimal)Math.Abs(quantity) * -1m);
}
///
/// Sell stock (alias of Order)
///
/// String symbol to sell
/// Quantity to sell
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Sell(Symbol symbol, decimal quantity)
{
return Order(symbol, Math.Abs(quantity) * -1);
}
///
/// Issue an order/trade for asset: Alias wrapper for Order(string, int);
///
/// Symbol to order
/// Quantity to order
///
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Order(Symbol symbol, double quantity)
{
return Order(symbol, quantity.SafeDecimalCast());
}
///
/// Issue an order/trade for asset
///
/// Symbol to order
/// Quantity to order
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Order(Symbol symbol, int quantity)
{
return MarketOrder(symbol, (decimal)quantity);
}
///
/// Issue an order/trade for asset
///
/// Symbol to order
/// Quantity to order
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Order(Symbol symbol, decimal quantity)
{
return MarketOrder(symbol, quantity);
}
///
/// Wrapper for market order method: submit a new order for quantity of symbol using type order.
///
/// Symbol of the MarketType Required.
/// Number of shares to request.
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
///
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Order(Symbol symbol, decimal quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
return MarketOrder(symbol, quantity, asynchronous, tag, orderProperties);
}
///
/// Market order implementation: Send a market order and wait for it to be filled.
///
/// Symbol of the MarketType Required.
/// Number of shares to request.
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOrder(Symbol symbol, int quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
return MarketOrder(symbol, (decimal)quantity, asynchronous, tag, orderProperties);
}
///
/// Market order implementation: Send a market order and wait for it to be filled.
///
/// Symbol of the MarketType Required.
/// Number of shares to request.
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOrder(Symbol symbol, double quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
return MarketOrder(symbol, quantity.SafeDecimalCast(), asynchronous, tag, orderProperties);
}
///
/// Market order implementation: Send a market order and wait for it to be filled.
///
/// Symbol of the MarketType Required.
/// Number of shares to request.
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOrder(Symbol symbol, decimal quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
var security = Securities[symbol];
return MarketOrder(security, quantity, asynchronous, tag, orderProperties);
}
///
/// Market order implementation: Send a market order and wait for it to be filled.
///
/// Symbol of the MarketType Required.
/// Number of shares to request.
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOrder(Security security, decimal quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
// check the exchange is open before sending a market order, if it's not open then convert it into a market on open order.
// For futures and FOPs, market orders can be submitted on extended hours, so we let them through.
if ((security.Type != SecurityType.Future && security.Type != SecurityType.FutureOption) && !security.Exchange.ExchangeOpen)
{
var mooTicket = MarketOnOpenOrder(security.Symbol, quantity, tag, orderProperties);
if (!_isMarketOnOpenOrderWarningSent)
{
var anyNonDailySubscriptions = security.Subscriptions.Any(x => x.Resolution != Resolution.Daily);
if (mooTicket.SubmitRequest.Response.IsSuccess && !anyNonDailySubscriptions)
{
Debug("Warning: all market orders sent using daily data, or market orders sent after hours are automatically converted into MarketOnOpen orders.");
_isMarketOnOpenOrderWarningSent = true;
}
}
return mooTicket;
}
var request = CreateSubmitOrderRequest(OrderType.Market, security, quantity, tag, orderProperties ?? DefaultOrderProperties?.Clone());
//Add the order and create a new order Id.
var ticket = SubmitOrderRequest(request);
// Wait for the order event to process, only if the exchange is open and the order is valid
if (ticket.Status != OrderStatus.Invalid && !asynchronous)
{
Transactions.WaitForOrder(ticket.OrderId);
}
return ticket;
}
///
/// Market on open order implementation: Send a market order when the exchange opens
///
/// The symbol to be ordered
/// The number of shares to required
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOnOpenOrder(Symbol symbol, double quantity, string tag = "", IOrderProperties orderProperties = null)
{
return MarketOnOpenOrder(symbol, quantity.SafeDecimalCast(), tag, orderProperties);
}
///
/// Market on open order implementation: Send a market order when the exchange opens
///
/// The symbol to be ordered
/// The number of shares to required
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOnOpenOrder(Symbol symbol, int quantity, string tag = "", IOrderProperties orderProperties = null)
{
return MarketOnOpenOrder(symbol, (decimal)quantity, tag, orderProperties);
}
///
/// Market on open order implementation: Send a market order when the exchange opens
///
/// The symbol to be ordered
/// The number of shares to required
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOnOpenOrder(Symbol symbol, decimal quantity, string tag = "", IOrderProperties orderProperties = null)
{
var properties = orderProperties ?? DefaultOrderProperties?.Clone();
InvalidateGoodTilDateTimeInForce(properties);
var security = Securities[symbol];
var request = CreateSubmitOrderRequest(OrderType.MarketOnOpen, security, quantity, tag, properties);
return SubmitOrderRequest(request);
}
///
/// Market on close order implementation: Send a market order when the exchange closes
///
/// The symbol to be ordered
/// The number of shares to required
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOnCloseOrder(Symbol symbol, int quantity, string tag = "", IOrderProperties orderProperties = null)
{
return MarketOnCloseOrder(symbol, (decimal)quantity, tag, orderProperties);
}
///
/// Market on close order implementation: Send a market order when the exchange closes
///
/// The symbol to be ordered
/// The number of shares to required
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOnCloseOrder(Symbol symbol, double quantity, string tag = "", IOrderProperties orderProperties = null)
{
return MarketOnCloseOrder(symbol, quantity.SafeDecimalCast(), tag, orderProperties);
}
///
/// Market on close order implementation: Send a market order when the exchange closes
///
/// The symbol to be ordered
/// The number of shares to required
/// Place a custom order property or tag (e.g. indicator data).
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket MarketOnCloseOrder(Symbol symbol, decimal quantity, string tag = "", IOrderProperties orderProperties = null)
{
var properties = orderProperties ?? DefaultOrderProperties?.Clone();
InvalidateGoodTilDateTimeInForce(properties);
var security = Securities[symbol];
var request = CreateSubmitOrderRequest(OrderType.MarketOnClose, security, quantity, tag, properties);
return SubmitOrderRequest(request);
}
///
/// Send a limit order to the transaction handler:
///
/// String symbol for the asset
/// Quantity of shares for limit order
/// Limit price to fill this order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket LimitOrder(Symbol symbol, int quantity, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
return LimitOrder(symbol, (decimal)quantity, limitPrice, tag, orderProperties);
}
///
/// Send a limit order to the transaction handler:
///
/// String symbol for the asset
/// Quantity of shares for limit order
/// Limit price to fill this order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket LimitOrder(Symbol symbol, double quantity, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
return LimitOrder(symbol, quantity.SafeDecimalCast(), limitPrice, tag, orderProperties);
}
///
/// Send a limit order to the transaction handler:
///
/// String symbol for the asset
/// Quantity of shares for limit order
/// Limit price to fill this order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket LimitOrder(Symbol symbol, decimal quantity, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
var security = Securities[symbol];
var request = CreateSubmitOrderRequest(OrderType.Limit, security, quantity, tag, limitPrice: limitPrice, properties: orderProperties ?? DefaultOrderProperties?.Clone());
return SubmitOrderRequest(request);
}
///
/// Create a stop market order and return the newly created order id; or negative if the order is invalid
///
/// String symbol for the asset we're trading
/// Quantity to be traded
/// Price to fill the stop order
/// Optional string data tag for the order
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket StopMarketOrder(Symbol symbol, int quantity, decimal stopPrice, string tag = "", IOrderProperties orderProperties = null)
{
return StopMarketOrder(symbol, (decimal)quantity, stopPrice, tag, orderProperties);
}
///
/// Create a stop market order and return the newly created order id; or negative if the order is invalid
///
/// String symbol for the asset we're trading
/// Quantity to be traded
/// Price to fill the stop order
/// Optional string data tag for the order
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket StopMarketOrder(Symbol symbol, double quantity, decimal stopPrice, string tag = "", IOrderProperties orderProperties = null)
{
return StopMarketOrder(symbol, quantity.SafeDecimalCast(), stopPrice, tag, orderProperties);
}
///
/// Create a stop market order and return the newly created order id; or negative if the order is invalid
///
/// String symbol for the asset we're trading
/// Quantity to be traded
/// Price to fill the stop order
/// Optional string data tag for the order
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket StopMarketOrder(Symbol symbol, decimal quantity, decimal stopPrice, string tag = "", IOrderProperties orderProperties = null)
{
var security = Securities[symbol];
var request = CreateSubmitOrderRequest(OrderType.StopMarket, security, quantity, tag, stopPrice: stopPrice, properties: orderProperties ?? DefaultOrderProperties?.Clone());
return SubmitOrderRequest(request);
}
///
/// Create a trailing stop order and return the newly created order id; or negative if the order is invalid.
/// It will calculate the stop price using the trailing amount and the current market price.
///
/// Trading asset symbol
/// Quantity to be traded
/// The trailing amount to be used to update the stop price
/// Whether the is a percentage or an absolute currency value
/// Optional string data tag for the order
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket TrailingStopOrder(Symbol symbol, int quantity, decimal trailingAmount, bool trailingAsPercentage,
string tag = "", IOrderProperties orderProperties = null)
{
return TrailingStopOrder(symbol, (decimal)quantity, trailingAmount, trailingAsPercentage, tag, orderProperties);
}
///
/// Create a trailing stop order and return the newly created order id; or negative if the order is invalid.
/// It will calculate the stop price using the trailing amount and the current market price.
///
/// Trading asset symbol
/// Quantity to be traded
/// The trailing amount to be used to update the stop price
/// Whether the is a percentage or an absolute currency value
/// Optional string data tag for the order
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket TrailingStopOrder(Symbol symbol, double quantity, decimal trailingAmount, bool trailingAsPercentage,
string tag = "", IOrderProperties orderProperties = null)
{
return TrailingStopOrder(symbol, quantity.SafeDecimalCast(), trailingAmount, trailingAsPercentage, tag, orderProperties);
}
///
/// Create a trailing stop order and return the newly created order id; or negative if the order is invalid.
/// It will calculate the stop price using the trailing amount and the current market price.
///
/// Trading asset symbol
/// Quantity to be traded
/// The trailing amount to be used to update the stop price
/// Whether the is a percentage or an absolute currency value
/// Optional string data tag for the order
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket TrailingStopOrder(Symbol symbol, decimal quantity, decimal trailingAmount, bool trailingAsPercentage,
string tag = "", IOrderProperties orderProperties = null)
{
var security = Securities[symbol];
var stopPrice = Orders.TrailingStopOrder.CalculateStopPrice(security.Price, trailingAmount, trailingAsPercentage,
quantity > 0 ? OrderDirection.Buy : OrderDirection.Sell);
return TrailingStopOrder(symbol, quantity, stopPrice, trailingAmount, trailingAsPercentage, tag, orderProperties);
}
///
/// Create a trailing stop order and return the newly created order id; or negative if the order is invalid
///
/// Trading asset symbol
/// Quantity to be traded
/// Initial stop price at which the order should be triggered
/// The trailing amount to be used to update the stop price
/// Whether the is a percentage or an absolute currency value
/// Optional string data tag for the order
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket TrailingStopOrder(Symbol symbol, int quantity, decimal stopPrice, decimal trailingAmount, bool trailingAsPercentage,
string tag = "", IOrderProperties orderProperties = null)
{
return TrailingStopOrder(symbol, (decimal)quantity, stopPrice, trailingAmount, trailingAsPercentage, tag, orderProperties);
}
///
/// Create a trailing stop order and return the newly created order id; or negative if the order is invalid
///
/// Trading asset symbol
/// Quantity to be traded
/// Initial stop price at which the order should be triggered
/// The trailing amount to be used to update the stop price
/// Whether the is a percentage or an absolute currency value
/// Optional string data tag for the order
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket TrailingStopOrder(Symbol symbol, double quantity, decimal stopPrice, decimal trailingAmount, bool trailingAsPercentage,
string tag = "", IOrderProperties orderProperties = null)
{
return TrailingStopOrder(symbol, quantity.SafeDecimalCast(), stopPrice, trailingAmount, trailingAsPercentage, tag, orderProperties);
}
///
/// Create a trailing stop order and return the newly created order id; or negative if the order is invalid
///
/// Trading asset symbol
/// Quantity to be traded
/// Initial stop price at which the order should be triggered
/// The trailing amount to be used to update the stop price
/// Whether the is a percentage or an absolute currency value
/// Optional string data tag for the order
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket TrailingStopOrder(Symbol symbol, decimal quantity, decimal stopPrice, decimal trailingAmount, bool trailingAsPercentage,
string tag = "", IOrderProperties orderProperties = null)
{
var security = Securities[symbol];
var request = CreateSubmitOrderRequest(
OrderType.TrailingStop,
security,
quantity,
tag,
stopPrice: stopPrice,
trailingAmount: trailingAmount,
trailingAsPercentage: trailingAsPercentage,
properties: orderProperties ?? DefaultOrderProperties?.Clone());
return SubmitOrderRequest(request);
}
///
/// Send a stop limit order to the transaction handler:
///
/// String symbol for the asset
/// Quantity of shares for limit order
/// Stop price for this order
/// Limit price to fill this order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket StopLimitOrder(Symbol symbol, int quantity, decimal stopPrice, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
return StopLimitOrder(symbol, (decimal)quantity, stopPrice, limitPrice, tag, orderProperties);
}
///
/// Send a stop limit order to the transaction handler:
///
/// String symbol for the asset
/// Quantity of shares for limit order
/// Stop price for this order
/// Limit price to fill this order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket StopLimitOrder(Symbol symbol, double quantity, decimal stopPrice, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
return StopLimitOrder(symbol, quantity.SafeDecimalCast(), stopPrice, limitPrice, tag, orderProperties);
}
///
/// Send a stop limit order to the transaction handler:
///
/// String symbol for the asset
/// Quantity of shares for limit order
/// Stop price for this order
/// Limit price to fill this order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket StopLimitOrder(Symbol symbol, decimal quantity, decimal stopPrice, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
var security = Securities[symbol];
var request = CreateSubmitOrderRequest(OrderType.StopLimit, security, quantity, tag, stopPrice: stopPrice, limitPrice: limitPrice, properties: orderProperties ?? DefaultOrderProperties?.Clone());
return SubmitOrderRequest(request);
}
///
/// Send a limit if touched order to the transaction handler:
///
/// String symbol for the asset
/// Quantity of shares for limit order
/// Trigger price for this order
/// Limit price to fill this order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket LimitIfTouchedOrder(Symbol symbol, int quantity, decimal triggerPrice, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
return LimitIfTouchedOrder(symbol, (decimal)quantity, triggerPrice, limitPrice, tag, orderProperties);
}
///
/// Send a limit if touched order to the transaction handler:
///
/// String symbol for the asset
/// Quantity of shares for limit order
/// Trigger price for this order
/// Limit price to fill this order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket LimitIfTouchedOrder(Symbol symbol, double quantity, decimal triggerPrice, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
return LimitIfTouchedOrder(symbol, quantity.SafeDecimalCast(), triggerPrice, limitPrice, tag, orderProperties);
}
///
/// Send a limit if touched order to the transaction handler:
///
/// String symbol for the asset
/// Quantity of shares for limit order
/// Trigger price for this order
/// Limit price to fill this order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket LimitIfTouchedOrder(Symbol symbol, decimal quantity, decimal triggerPrice, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
var security = Securities[symbol];
var request = CreateSubmitOrderRequest(OrderType.LimitIfTouched, security, quantity, tag, triggerPrice: triggerPrice, limitPrice: limitPrice, properties: orderProperties ?? DefaultOrderProperties?.Clone());
return SubmitOrderRequest(request);
}
///
/// Send an exercise order to the transaction handler
///
/// String symbol for the option position
/// Quantity of options contracts
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// The order ticket instance.
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket ExerciseOption(Symbol optionSymbol, int quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
var option = (Option)Securities[optionSymbol];
// SubmitOrderRequest.Quantity indicates the change in holdings quantity, therefore manual exercise quantities must be negative
// PreOrderChecksImpl confirms that we don't hold a short position, so we're lenient here and accept +/- quantity values
var request = CreateSubmitOrderRequest(OrderType.OptionExercise, option, -Math.Abs(quantity), tag, orderProperties ?? DefaultOrderProperties?.Clone());
//Initialize the exercise order parameters
var preOrderCheckResponse = PreOrderChecks(request);
if (preOrderCheckResponse.IsError)
{
return OrderTicket.InvalidSubmitRequest(Transactions, request, preOrderCheckResponse);
}
//Add the order and create a new order Id.
var ticket = Transactions.AddOrder(request);
// Wait for the order event to process, only if the exchange is open
if (!asynchronous)
{
Transactions.WaitForOrder(ticket.OrderId);
}
return ticket;
}
// Support for option strategies trading
///
/// Buy Option Strategy (Alias of Order)
///
/// Specification of the strategy to trade
/// Quantity of the strategy to trade
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// Sequence of order tickets
[DocumentationAttribute(TradingAndOrders)]
public List Buy(OptionStrategy strategy, int quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
return Order(strategy, Math.Abs(quantity), asynchronous, tag, orderProperties);
}
///
/// Sell Option Strategy (alias of Order)
///
/// Specification of the strategy to trade
/// Quantity of the strategy to trade
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// Sequence of order tickets
[DocumentationAttribute(TradingAndOrders)]
public List Sell(OptionStrategy strategy, int quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
return Order(strategy, Math.Abs(quantity) * -1, asynchronous, tag, orderProperties);
}
///
/// Issue an order/trade for buying/selling an option strategy
///
/// Specification of the strategy to trade
/// Quantity of the strategy to trade
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// Sequence of order tickets
[DocumentationAttribute(TradingAndOrders)]
public List Order(OptionStrategy strategy, int quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
return GenerateOptionStrategyOrders(strategy, quantity, asynchronous, tag, orderProperties);
}
///
/// Issue a combo market order/trade for multiple assets
///
/// The list of legs the order consists of
/// The total quantity for the order
/// Send the order asynchronously (false). Otherwise we'll block until it fills
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// Sequence of order tickets, one for each leg
[DocumentationAttribute(TradingAndOrders)]
public List ComboMarketOrder(List legs, int quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
return SubmitComboOrder(legs, quantity, 0, asynchronous, tag, orderProperties);
}
///
/// Issue a combo leg limit order/trade for multiple assets, each having its own limit price.
///
/// The list of legs the order consists of
/// The total quantity for the order
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// Sequence of order tickets, one for each leg
/// If not every leg has a defined limit price
[DocumentationAttribute(TradingAndOrders)]
public List ComboLegLimitOrder(List legs, int quantity, string tag = "", IOrderProperties orderProperties = null)
{
if (legs.Any(x => x.OrderPrice == null || x.OrderPrice == 0))
{
throw new ArgumentException("ComboLegLimitOrder requires a limit price for each leg");
}
return SubmitComboOrder(legs, quantity, 0, asynchronous: true, tag, orderProperties);
}
///
/// Issue a combo limit order/trade for multiple assets.
/// A single limit price is defined for the combo order and will fill only if the sum of the assets price compares properly to the limit price, depending on the direction.
///
/// The list of legs the order consists of
/// The total quantity for the order
/// The compound limit price to use for a ComboLimit order. This limit price will compared to the sum of the assets price in order to fill the order.
/// String tag for the order (optional)
/// The order properties to use. Defaults to
/// Sequence of order tickets, one for each leg
/// If the order type is neither ComboMarket, ComboLimit nor ComboLegLimit
[DocumentationAttribute(TradingAndOrders)]
public List ComboLimitOrder(List legs, int quantity, decimal limitPrice, string tag = "", IOrderProperties orderProperties = null)
{
if (limitPrice == 0)
{
throw new ArgumentException("ComboLimitOrder requires a limit price");
}
if (legs.Any(x => x.OrderPrice != null && x.OrderPrice != 0))
{
throw new ArgumentException("ComboLimitOrder does not support limit prices for individual legs");
}
return SubmitComboOrder(legs, quantity, limitPrice, asynchronous: true, tag, orderProperties);
}
private List GenerateOptionStrategyOrders(OptionStrategy strategy, int strategyQuantity, bool asynchronous, string tag, IOrderProperties orderProperties)
{
// if the option strategy canonical is set let's use it to make sure we target the right option, for example SPXW for SPX underlying,
// it could be null if the user created the option strategy manually and just set the underlying, in which case we use the default option target by using 'null'
var targetOption = strategy.CanonicalOption != null ? strategy.CanonicalOption.Canonical.ID.Symbol : null;
// setting up the tag text for all orders of one strategy
tag ??= $"{strategy.Name} ({strategyQuantity.ToStringInvariant()})";
var legs = new List(strategy.UnderlyingLegs);
// WHY: the option strategy doesn't specify the option style (and in consequence the symbol), so we figure it out at runtime
foreach (var optionLeg in strategy.OptionLegs)
{
Leg leg = null;
// search for both american/european style -- much better than looping through all securities
foreach (var optionStyle in new[] { OptionStyle.American, OptionStyle.European })
{
var option = QuantConnect.Symbol.CreateOption(strategy.Underlying, targetOption, strategy.Underlying.ID.Market, optionStyle, optionLeg.Right, optionLeg.Strike, optionLeg.Expiration);
if (Securities.ContainsKey(option))
{
// we found it, we add it a break/stop searching
leg = new Leg { Symbol = option, OrderPrice = optionLeg.OrderPrice, Quantity = optionLeg.Quantity };
break;
}
}
if (leg == null)
{
throw new InvalidOperationException("Couldn't find the option contract in algorithm securities list. " +
Invariant($"Underlying: {strategy.Underlying}, option {optionLeg.Right}, strike {optionLeg.Strike}, ") +
Invariant($"expiration: {optionLeg.Expiration}")
);
}
legs.Add(leg);
}
return SubmitComboOrder(legs, strategyQuantity, 0, asynchronous, tag, orderProperties);
}
private List SubmitComboOrder(List legs, decimal quantity, decimal limitPrice, bool asynchronous, string tag, IOrderProperties orderProperties)
{
CheckComboOrderSizing(legs, quantity);
var orderType = OrderType.ComboMarket;
if (limitPrice != 0)
{
orderType = OrderType.ComboLimit;
}
// we create a unique Id so the algorithm and the brokerage can relate the combo orders with each other
var groupOrderManager = new GroupOrderManager(Transactions.GetIncrementGroupOrderManagerId(), legs.Count, quantity, limitPrice);
List orderTickets = new(capacity: legs.Count);
List submitRequests = new(capacity: legs.Count);
foreach (var leg in legs)
{
var security = Securities[leg.Symbol];
if (leg.OrderPrice.HasValue)
{
// limit price per leg!
limitPrice = leg.OrderPrice.Value;
orderType = OrderType.ComboLegLimit;
}
var request = CreateSubmitOrderRequest(
orderType,
security,
((decimal)leg.Quantity).GetOrderLegGroupQuantity(groupOrderManager),
tag,
orderProperties ?? DefaultOrderProperties?.Clone(),
groupOrderManager: groupOrderManager,
limitPrice: limitPrice);
// we execture pre order checks for all requests before submitting, so that if anything fails we are not left with half submitted combo orders
var response = PreOrderChecks(request);
if (response.IsError)
{
orderTickets.Add(OrderTicket.InvalidSubmitRequest(Transactions, request, response));
return orderTickets;
}
submitRequests.Add(request);
}
foreach (var request in submitRequests)
{
//Add the order and create a new order Id.
orderTickets.Add(Transactions.AddOrder(request));
}
// Wait for the order event to process, only if the exchange is open
if (!asynchronous)
{
foreach (var ticket in orderTickets)
{
if (ticket.Status.IsOpen())
{
Transactions.WaitForOrder(ticket.OrderId);
}
}
}
return orderTickets;
}
///
/// Will submit an order request to the algorithm
///
/// The request to submit
/// Will run order prechecks, which include making sure the algorithm is not warming up, security is added and has data among others
/// The order ticket
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket SubmitOrderRequest(SubmitOrderRequest request)
{
var response = PreOrderChecks(request);
if (response.IsError)
{
return OrderTicket.InvalidSubmitRequest(Transactions, request, response);
}
//Add the order and create a new order Id.
return Transactions.AddOrder(request);
}
///
/// Perform pre-order checks to ensure we have sufficient capital,
/// the market is open, and we haven't exceeded maximum realistic orders per day.
///
/// OrderResponse. If no error, order request is submitted.
private OrderResponse PreOrderChecks(SubmitOrderRequest request)
{
var response = PreOrderChecksImpl(request);
if (response.IsError)
{
Error(response.ErrorMessage);
}
return response;
}
///
/// Perform pre-order checks to ensure we have sufficient capital,
/// the market is open, and we haven't exceeded maximum realistic orders per day.
///
/// OrderResponse. If no error, order request is submitted.
private OrderResponse PreOrderChecksImpl(SubmitOrderRequest request)
{
if (IsWarmingUp)
{
return OrderResponse.WarmingUp(request);
}
//Most order methods use security objects; so this isn't really used.
// todo: Left here for now but should review
Security security;
if (!Securities.TryGetValue(request.Symbol, out security))
{
return OrderResponse.MissingSecurity(request);
}
//Ordering 0 is useless.
if (request.Quantity == 0)
{
return OrderResponse.ZeroQuantity(request);
}
if (Math.Abs(request.Quantity) < security.SymbolProperties.LotSize)
{
return OrderResponse.Error(request, OrderResponseErrorCode.OrderQuantityLessThanLotSize,
Invariant($"Unable to {request.OrderRequestType.ToLower()} order with id {request.OrderId} which ") +
Invariant($"quantity ({Math.Abs(request.Quantity)}) is less than lot ") +
Invariant($"size ({security.SymbolProperties.LotSize}).")
);
}
if (!security.IsTradable)
{
return OrderResponse.Error(request, OrderResponseErrorCode.NonTradableSecurity,
$"The security with symbol '{request.Symbol}' is marked as non-tradable."
);
}
var price = security.Price;
//Check the exchange is open before sending a exercise orders
if (request.OrderType == OrderType.OptionExercise && !security.Exchange.ExchangeOpen)
{
return OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen,
$"{request.OrderType} order and exchange not open."
);
}
//Check the exchange is open before sending a market on open order for futures
if ((security.Type == SecurityType.Future || security.Type == SecurityType.FutureOption) && request.OrderType == OrderType.MarketOnOpen)
{
if (!_isMarketOnOpenOrderRestrictedForFuturesWarningSent)
{
Debug("Warning: Market-On-Open orders are not allowed for futures and future options. Consider using limit orders during extended market hours.");
_isMarketOnOpenOrderRestrictedForFuturesWarningSent = true;
}
return OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen,
$"{request.OrderType} orders not supported for {security.Type}."
);
}
if (price == 0)
{
return OrderResponse.Error(request, OrderResponseErrorCode.SecurityPriceZero, request.Symbol.GetZeroPriceMessage());
}
// check quote currency existence/conversion rate on all orders
var quoteCurrency = security.QuoteCurrency.Symbol;
if (!Portfolio.CashBook.TryGetValue(quoteCurrency, out var quoteCash))
{
return OrderResponse.Error(request, OrderResponseErrorCode.QuoteCurrencyRequired,
$"{request.Symbol.Value}: requires {quoteCurrency} in the cashbook to trade."
);
}
if (security.QuoteCurrency.ConversionRate == 0m)
{
return OrderResponse.Error(request, OrderResponseErrorCode.ConversionRateZero,
$"{request.Symbol.Value}: requires {quoteCurrency} to have a non-zero conversion rate. This can be caused by lack of data."
);
}
// need to also check base currency existence/conversion rate on forex orders
if (security.Type == SecurityType.Forex || security.Type == SecurityType.Crypto)
{
var baseCurrency = ((IBaseCurrencySymbol)security).BaseCurrency.Symbol;
if (!Portfolio.CashBook.TryGetValue(baseCurrency, out var baseCash))
{
return OrderResponse.Error(request, OrderResponseErrorCode.ForexBaseAndQuoteCurrenciesRequired,
$"{request.Symbol.Value}: requires {baseCurrency} and {quoteCurrency} in the cashbook to trade."
);
}
if (baseCash.ConversionRate == 0m)
{
return OrderResponse.Error(request, OrderResponseErrorCode.ForexConversionRateZero,
$"{request.Symbol.Value}: requires {baseCurrency} and {quoteCurrency} to have non-zero conversion rates. This can be caused by lack of data."
);
}
}
//Make sure the security has some data:
if (!security.HasData)
{
return OrderResponse.Error(request, OrderResponseErrorCode.SecurityHasNoData,
"There is no data for this symbol yet, please check the security.HasData flag to ensure there is at least one data point."
);
}
// We've already processed too many orders: max 10k
if (!LiveMode && Transactions.OrdersCount > _maxOrders)
{
Status = AlgorithmStatus.Stopped;
return OrderResponse.Error(request, OrderResponseErrorCode.ExceededMaximumOrders,
Invariant($"You have exceeded maximum number of orders ({_maxOrders}), for unlimited orders upgrade your account.")
);
}
if (request.OrderType == OrderType.OptionExercise)
{
if (!security.Type.IsOption())
{
return OrderResponse.Error(request, OrderResponseErrorCode.NonExercisableSecurity,
$"The security with symbol '{request.Symbol}' is not exercisable."
);
}
if ((security as Option).Style == OptionStyle.European && UtcTime.Date < security.Symbol.ID.Date.ConvertToUtc(security.Exchange.TimeZone).Date)
{
return OrderResponse.Error(request, OrderResponseErrorCode.EuropeanOptionNotExpiredOnExercise,
$"Cannot exercise European style option with symbol '{request.Symbol}' before its expiration date."
);
}
if (security.Holdings.IsShort)
{
return OrderResponse.Error(request, OrderResponseErrorCode.UnsupportedRequestType,
$"The security with symbol '{request.Symbol}' has a short option position. Only long option positions are exercisable."
);
}
if (Math.Abs(request.Quantity) > security.Holdings.Quantity)
{
return OrderResponse.Error(request, OrderResponseErrorCode.UnsupportedRequestType,
$"Cannot exercise more contracts of '{request.Symbol}' than is currently available in the portfolio. "
);
}
}
if (request.OrderType == OrderType.MarketOnOpen)
{
if (security.Exchange.Hours.IsMarketAlwaysOpen)
{
throw new InvalidOperationException($"Market never closes for this symbol {security.Symbol}, can no submit a {nameof(OrderType.MarketOnOpen)} order.");
}
if (security.Exchange.Hours.IsOpen(security.LocalTime, false))
{
return OrderResponse.Error(request, OrderResponseErrorCode.MarketOnOpenNotAllowedDuringRegularHours, $"Cannot submit a {nameof(OrderType.MarketOnOpen)} order while the market is open.");
}
}
else if (request.OrderType == OrderType.MarketOnClose)
{
if (security.Exchange.Hours.IsMarketAlwaysOpen)
{
throw new InvalidOperationException($"Market never closes for this symbol {security.Symbol}, can no submit a {nameof(OrderType.MarketOnClose)} order.");
}
var nextMarketClose = security.Exchange.Hours.GetNextMarketClose(security.LocalTime, false);
// Enforce MarketOnClose submission buffer
var latestSubmissionTimeUtc = nextMarketClose
.ConvertToUtc(security.Exchange.TimeZone)
.Subtract(Orders.MarketOnCloseOrder.SubmissionTimeBuffer);
if (UtcTime > latestSubmissionTimeUtc)
{
// Tell user the required buffer on these orders, also inform them it can be changed for special cases.
// Default buffer is 15.5 minutes because with minute data a user will receive the 3:44->3:45 bar at 3:45,
// if the latest time is 3:45 it is already too late to submit one of these orders
return OrderResponse.Error(request, OrderResponseErrorCode.MarketOnCloseOrderTooLate,
$"MarketOnClose orders must be placed within {Orders.MarketOnCloseOrder.SubmissionTimeBuffer} before market close." +
" Override this TimeSpan buffer by setting Orders.MarketOnCloseOrder.SubmissionTimeBuffer in QCAlgorithm.Initialize()."
);
}
}
if (request.OrderType == OrderType.ComboMarket && request.LimitPrice != 0)
{
// just in case some validation
throw new ArgumentException("Can not set a limit price using market combo orders");
}
// Check for splits. Option are selected before the security price is split-adjusted, so in this time step
// we don't allow option orders to make sure they are properly filtered using the right security price.
if (request.SecurityType.IsOption() &&
CurrentSlice != null &&
CurrentSlice.Splits.Count > 0 &&
CurrentSlice.Splits.TryGetValue(request.Symbol.Underlying, out _))
{
if (!_isOptionsOrderOnStockSplitWarningSent)
{
Debug("Warning: Options orders are not allowed when a split occurred for its underlying stock");
_isOptionsOrderOnStockSplitWarningSent = true;
}
return OrderResponse.Error(request, OrderResponseErrorCode.OptionOrderOnStockSplit,
"Options orders are not allowed when a split occurred for its underlying stock");
}
// passes all initial order checks
return OrderResponse.Success(request);
}
///
/// Liquidate your portfolio holdings
///
/// Specific asset to liquidate, defaults to all
/// Flag to indicate if the symbols should be liquidated asynchronously
/// Custom tag to know who is calling this
/// Order properties to use
[DocumentationAttribute(TradingAndOrders)]
public List Liquidate(Symbol symbol = null, bool asynchronous = false, string tag = null, IOrderProperties orderProperties = null)
{
IEnumerable toLiquidate;
if (symbol != null)
{
toLiquidate = Securities.ContainsKey(symbol)
? new[] { symbol } : Enumerable.Empty();
}
else
{
toLiquidate = Securities.Keys.OrderBy(x => x.Value);
}
return Liquidate(toLiquidate, asynchronous, tag, orderProperties);
}
///
/// Liquidate your portfolio holdings
///
/// List of symbols to liquidate, defaults to all
/// Flag to indicate if the symbols should be liquidated asynchronously
/// Custom tag to know who is calling this
/// Order properties to use
[DocumentationAttribute(TradingAndOrders)]
public List Liquidate(IEnumerable symbols, bool asynchronous = false, string tag = null, IOrderProperties orderProperties = null)
{
var orderTickets = new List();
if (!Settings.LiquidateEnabled)
{
Debug("Liquidate() is currently disabled by settings. To re-enable please set 'Settings.LiquidateEnabled' to true");
return orderTickets;
}
tag ??= "Liquidated";
foreach (var symbolToLiquidate in symbols)
{
// get open orders
var orders = Transactions.GetOpenOrders(symbolToLiquidate);
// get quantity in portfolio
var quantity = 0m;
var holdings = Portfolio[symbolToLiquidate];
if (holdings.Invested)
{
// invested flag might filter some quantity that's less than lot size
quantity = holdings.Quantity;
}
// if there is only one open market order that would close the position, do nothing
if (orders.Count == 1 && quantity != 0 && orders[0].Quantity == -quantity && orders[0].Type == OrderType.Market)
{
continue;
}
// cancel all open orders
var marketOrdersQuantity = 0m;
foreach (var order in orders)
{
if (order.Type == OrderType.Market)
{
// pending market order
var ticket = Transactions.GetOrderTicket(order.Id);
if (ticket != null)
{
// get remaining quantity
marketOrdersQuantity += ticket.QuantityRemaining;
}
}
else
{
Transactions.CancelOrder(order.Id, tag);
}
}
// Liquidate at market price
if (quantity != 0)
{
// calculate quantity for closing market order
var ticket = Order(symbolToLiquidate, -quantity - marketOrdersQuantity, asynchronous: asynchronous, tag: tag, orderProperties: orderProperties);
orderTickets.Add(ticket);
}
}
return orderTickets;
}
///
/// Liquidate all holdings and cancel open orders. Called at the end of day for tick-strategies.
///
/// Symbol we wish to liquidate
/// Custom tag to know who is calling this.
/// Array of order ids for liquidated symbols
///
[Obsolete($"This method is obsolete, please use Liquidate(symbol: symbolToLiquidate, tag: tag) method")]
public List Liquidate(Symbol symbolToLiquidate, string tag)
{
return Liquidate(symbol: symbolToLiquidate, tag: tag).Select(x => x.OrderId).ToList();
}
///
/// Maximum number of orders for the algorithm
///
///
[DocumentationAttribute(TradingAndOrders)]
public void SetMaximumOrders(int max)
{
if (!_locked)
{
_maxOrders = max;
}
}
///
/// Sets holdings for a collection of targets.
/// The implementation will order the provided targets executing first those that
/// reduce a position, freeing margin.
///
/// The portfolio desired quantities as percentages
/// True will liquidate existing holdings
/// Tag the order with a short string.
/// The order properties to use. Defaults to
/// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
public List SetHoldings(List targets, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
List orderTickets = null;
//If they triggered a liquidate
if (liquidateExistingHoldings)
{
orderTickets = Liquidate(GetSymbolsToLiquidate(targets.Select(t => t.Symbol)), tag: tag, orderProperties: orderProperties);
}
orderTickets ??= new List();
foreach (var portfolioTarget in targets
// we need to create targets with quantities for OrderTargetsByMarginImpact
.Select(target => new PortfolioTarget(target.Symbol, CalculateOrderQuantity(target.Symbol, target.Quantity)))
.OrderTargetsByMarginImpact(this, targetIsDelta: true))
{
var tickets = SetHoldingsImpl(portfolioTarget.Symbol, portfolioTarget.Quantity, false, tag, orderProperties);
orderTickets.AddRange(tickets);
}
return orderTickets;
}
///
/// Alias for SetHoldings to avoid the M-decimal errors.
///
/// string symbol we wish to hold
/// double percentage of holdings desired
/// liquidate existing holdings if necessary to hold this stock
/// Tag the order with a short string.
/// The order properties to use. Defaults to
/// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
public List SetHoldings(Symbol symbol, double percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
return SetHoldings(symbol, percentage.SafeDecimalCast(), liquidateExistingHoldings, tag, orderProperties);
}
///
/// Alias for SetHoldings to avoid the M-decimal errors.
///
/// string symbol we wish to hold
/// float percentage of holdings desired
/// bool liquidate existing holdings if necessary to hold this stock
/// Tag the order with a short string.
/// The order properties to use. Defaults to
/// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
public List SetHoldings(Symbol symbol, float percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
return SetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings, tag, orderProperties);
}
///
/// Alias for SetHoldings to avoid the M-decimal errors.
///
/// string symbol we wish to hold
/// float percentage of holdings desired
/// bool liquidate existing holdings if necessary to hold this stock
/// Tag the order with a short string.
/// The order properties to use. Defaults to
/// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
public List SetHoldings(Symbol symbol, int percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
return SetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings, tag, orderProperties);
}
///
/// Automatically place a market order which will set the holdings to between 100% or -100% of *PORTFOLIO VALUE*.
/// E.g. SetHoldings("AAPL", 0.1); SetHoldings("IBM", -0.2); -> Sets portfolio as long 10% APPL and short 20% IBM
/// E.g. SetHoldings("AAPL", 2); -> Sets apple to 2x leveraged with all our cash.
/// If the market is closed, place a market on open order.
///
/// Symbol indexer
/// decimal fraction of portfolio to set stock
/// bool flag to clean all existing holdings before setting new faction.
/// Tag the order with a short string.
/// The order properties to use. Defaults to
/// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
public List SetHoldings(Symbol symbol, decimal percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
return SetHoldingsImpl(symbol, CalculateOrderQuantity(symbol, percentage), liquidateExistingHoldings, tag, orderProperties);
}
///
/// Set holdings implementation, which uses order quantities (delta) not percentage nor target final quantity
///
private List SetHoldingsImpl(Symbol symbol, decimal orderQuantity, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
List orderTickets = null;
//If they triggered a liquidate
if (liquidateExistingHoldings)
{
orderTickets = Liquidate(GetSymbolsToLiquidate([symbol]), tag: tag, orderProperties: orderProperties);
}
orderTickets ??= new List();
tag ??= "";
//Calculate total unfilled quantity for open market orders
var marketOrdersQuantity = Transactions.GetOpenOrderTickets(
ticket => ticket.Symbol == symbol
&& (ticket.OrderType == OrderType.Market
|| ticket.OrderType == OrderType.MarketOnOpen))
.Aggregate(0m, (d, ticket) => d + ticket.QuantityRemaining);
//Only place trade if we've got > 1 share to order.
var quantity = orderQuantity - marketOrdersQuantity;
if (Math.Abs(quantity) > 0)
{
Security security;
if (!Securities.TryGetValue(symbol, out security))
{
Error($"{symbol} not found in portfolio. Request this data when initializing the algorithm.");
return orderTickets;
}
//Check whether the exchange is open to send a market order. If not, send a market on open order instead
OrderTicket ticket;
if (security.Exchange.ExchangeOpen)
{
ticket = MarketOrder(symbol, quantity, false, tag, orderProperties);
}
else
{
ticket = MarketOnOpenOrder(symbol, quantity, tag, orderProperties);
}
orderTickets.Add(ticket);
}
return orderTickets;
}
///
/// Returns the symbols in the portfolio to be liquidated, excluding the provided symbols.
///
/// The list of symbols to exclude from liquidation.
/// A list of symbols to liquidate.
private List GetSymbolsToLiquidate(IEnumerable symbols)
{
var targetSymbols = new HashSet(symbols);
var symbolsToLiquidate = Portfolio.Keys
.Where(symbol => !targetSymbols.Contains(symbol))
.OrderBy(symbol => symbol.Value)
.ToList();
return symbolsToLiquidate;
}
///
/// Calculate the order quantity to achieve target-percent holdings.
///
/// Security object we're asking for
/// Target percentage holdings
/// Order quantity to achieve this percentage
[DocumentationAttribute(TradingAndOrders)]
public decimal CalculateOrderQuantity(Symbol symbol, double target)
{
return CalculateOrderQuantity(symbol, target.SafeDecimalCast());
}
///
/// Calculate the order quantity to achieve target-percent holdings.
///
/// Security object we're asking for
/// Target percentage holdings, this is an unleveraged value, so
/// if you have 2x leverage and request 100% holdings, it will utilize half of the
/// available margin
/// Order quantity to achieve this percentage
[DocumentationAttribute(TradingAndOrders)]
public decimal CalculateOrderQuantity(Symbol symbol, decimal target)
{
var percent = PortfolioTarget.Percent(this, symbol, target, true);
if (percent == null)
{
return 0;
}
return percent.Quantity;
}
///
/// Obsolete implementation of Order method accepting a OrderType. This was deprecated since it
/// was impossible to generate other orders via this method. Any calls to this method will always default to a Market Order.
///
/// Symbol we want to purchase
/// Quantity to buy, + is long, - short.
/// Order Type
/// Don't wait for the response, just submit order and move on.
/// Custom data for this order
/// The order properties to use. Defaults to
/// The order ticket instance.
[Obsolete("This Order method has been made obsolete, use Order(string, int, bool, string) method instead. Calls to the obsolete method will only generate market orders.")]
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Order(Symbol symbol, int quantity, OrderType type, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null)
{
return Order(symbol, quantity, asynchronous, tag, orderProperties);
}
///
/// Obsolete method for placing orders.
///
/// Symbol we want to order
/// The quantity to order
/// The order type
/// The order ticket instance.
[Obsolete("This Order method has been made obsolete, use the specialized Order helper methods instead. Calls to the obsolete method will only generate market orders.")]
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Order(Symbol symbol, decimal quantity, OrderType type)
{
return Order(symbol, quantity);
}
///
/// Obsolete method for placing orders.
///
/// Symbol we want to order
/// The quantity to order
/// The order type
/// The order ticket instance.
[Obsolete("This Order method has been made obsolete, use the specialized Order helper methods instead. Calls to the obsolete method will only generate market orders.")]
[DocumentationAttribute(TradingAndOrders)]
public OrderTicket Order(Symbol symbol, int quantity, OrderType type)
{
return Order(symbol, (decimal)quantity);
}
///
/// Determines if the exchange for the specified symbol is open at the current time.
///
/// The symbol
/// True if the exchange is considered open at the current time, false otherwise
[DocumentationAttribute(TradingAndOrders)]
[DocumentationAttribute(SecuritiesAndPortfolio)]
public bool IsMarketOpen(Symbol symbol)
{
if (Securities.TryGetValue(symbol, out var security))
{
return security.IsMarketOpen(false);
}
return symbol.IsMarketOpen(UtcTime, false);
}
private SubmitOrderRequest CreateSubmitOrderRequest(OrderType orderType, Security security, decimal quantity, string tag,
IOrderProperties properties, decimal stopPrice = 0m, decimal limitPrice = 0m, decimal triggerPrice = 0m, decimal trailingAmount = 0m,
bool trailingAsPercentage = false, GroupOrderManager groupOrderManager = null)
{
return new SubmitOrderRequest(orderType, security.Type, security.Symbol, quantity, stopPrice, limitPrice, triggerPrice, trailingAmount,
trailingAsPercentage, UtcTime, tag, properties, groupOrderManager);
}
private static void CheckComboOrderSizing(List legs, decimal quantity)
{
var greatestsCommonDivisor = Math.Abs(legs.Select(leg => leg.Quantity).GreatestCommonDivisor());
if (greatestsCommonDivisor != 1)
{
throw new ArgumentException(
"The global combo quantity should be used to increase or reduce the size of the order, " +
"while the leg quantities should be used to specify the ratio of the order. " +
"The combo order quantities should be reduced " +
$"from {quantity}x({string.Join(", ", legs.Select(leg => $"{leg.Quantity} {leg.Symbol}"))}) " +
$"to {quantity * greatestsCommonDivisor}x({string.Join(", ", legs.Select(leg => $"{leg.Quantity / greatestsCommonDivisor} {leg.Symbol}"))}).");
}
}
///
/// Resets the time-in-force to the default if the given one is a .
/// This is required for MOO and MOC orders, for which GTD is not supported.
///
private void InvalidateGoodTilDateTimeInForce(IOrderProperties orderProperties)
{
if (orderProperties.TimeInForce as GoodTilDateTimeInForce != null)
{
// Good-Til-Date(GTD) Time-In-Force is not supported for MOO and MOC orders
orderProperties.TimeInForce = TimeInForce.GoodTilCanceled;
if (!_isGtdTfiForMooAndMocOrdersValidationWarningSent)
{
Debug("Warning: Good-Til-Date Time-In-Force is not supported for MOO and MOC orders. " +
"The time-in-force will be reset to Good-Til-Canceled (GTC).");
_isGtdTfiForMooAndMocOrdersValidationWarningSent = true;
}
}
}
}
}