/* * 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.ComponentModel; using System.Linq; using System.Threading; using Newtonsoft.Json; using QuantConnect.Interfaces; using QuantConnect.Securities; using QuantConnect.Securities.Positions; namespace QuantConnect.Orders { /// /// Order struct for placing new trade /// public abstract class Order { private volatile int _incrementalId; private decimal _quantity; private decimal _price; private int _id; /// /// Order ID. /// [JsonProperty(PropertyName = "id")] public int Id { get => _id; internal set { _id = value; if (_id != 0 && GroupOrderManager != null) { lock (GroupOrderManager.OrderIds) { GroupOrderManager.OrderIds.Add(_id); } } } } /// /// Order id to process before processing this order. /// [JsonProperty(PropertyName = "contingentId")] public int ContingentId { get; internal set; } /// /// Brokerage Id for this order for when the brokerage splits orders into multiple pieces /// [JsonProperty(PropertyName = "brokerId")] public List BrokerId { get; internal set; } /// /// Symbol of the Asset /// [JsonProperty(PropertyName = "symbol")] public Symbol Symbol { get; internal set; } /// /// Price of the Order. /// [JsonProperty(PropertyName = "price")] public decimal Price { get { return _price; } internal set { _price = value.Normalize(); } } /// /// Currency for the order price /// [JsonProperty(PropertyName = "priceCurrency")] public string PriceCurrency { get; internal set; } /// /// Gets the utc time the order was created. /// [JsonProperty(PropertyName = "time")] public DateTime Time { get; internal set; } /// /// Gets the utc time this order was created. Alias for /// [JsonProperty(PropertyName = "createdTime")] public DateTime CreatedTime => Time; /// /// Gets the utc time the last fill was received, or null if no fills have been received /// [JsonProperty(PropertyName = "lastFillTime", NullValueHandling = NullValueHandling.Ignore)] public DateTime? LastFillTime { get; internal set; } /// /// Gets the utc time this order was last updated, or null if the order has not been updated. /// [JsonProperty(PropertyName = "lastUpdateTime", NullValueHandling = NullValueHandling.Ignore)] public DateTime? LastUpdateTime { get; internal set; } /// /// Gets the utc time this order was canceled, or null if the order was not canceled. /// [JsonProperty(PropertyName = "canceledTime", NullValueHandling = NullValueHandling.Ignore)] public DateTime? CanceledTime { get; internal set; } /// /// Number of shares to execute. /// [JsonProperty(PropertyName = "quantity")] public virtual decimal Quantity { get { return _quantity; } internal set { _quantity = value.Normalize(); } } /// /// Order Type /// [JsonProperty(PropertyName = "type")] public abstract OrderType Type { get; } /// /// Status of the Order /// [JsonProperty(PropertyName = "status")] public OrderStatus Status { get; set; } /// /// Order Time In Force /// [JsonIgnore] public TimeInForce TimeInForce => Properties.TimeInForce; /// /// Tag the order with some custom data /// [JsonProperty(PropertyName = "tag" ,DefaultValueHandling = DefaultValueHandling.Ignore)] public string Tag { get; internal set; } /// /// Additional properties of the order /// [JsonProperty(PropertyName = "properties")] public IOrderProperties Properties { get; private set; } /// /// The symbol's security type /// [JsonProperty(PropertyName = "securityType")] public SecurityType SecurityType => Symbol.ID.SecurityType; /// /// Order Direction Property based off Quantity. /// [JsonProperty(PropertyName = "direction")] public OrderDirection Direction { get { if (Quantity > 0) { return OrderDirection.Buy; } if (Quantity < 0) { return OrderDirection.Sell; } return OrderDirection.Hold; } } /// /// Get the absolute quantity for this order /// [JsonIgnore] public decimal AbsoluteQuantity => Math.Abs(Quantity); /// /// Deprecated /// [JsonProperty(PropertyName = "value"), Obsolete("Please use Order.GetValue(security) or security.Holdings.HoldingsValue")] public decimal Value => Quantity * Price; /// /// Gets the price data at the time the order was submitted /// [JsonProperty(PropertyName = "orderSubmissionData")] public OrderSubmissionData OrderSubmissionData { get; internal set; } /// /// Returns true if the order is a marketable order. /// [JsonProperty(PropertyName = "isMarketable")] public bool IsMarketable { get { if (Type == OrderType.Limit) { // check if marketable limit order using bid/ask prices var limitOrder = (LimitOrder)this; return OrderSubmissionData != null && (Direction == OrderDirection.Buy && limitOrder.LimitPrice >= OrderSubmissionData.AskPrice || Direction == OrderDirection.Sell && limitOrder.LimitPrice <= OrderSubmissionData.BidPrice); } return Type == OrderType.Market || Type == OrderType.ComboMarket; } } /// /// Manager for the orders in the group if this is a combo order /// [JsonProperty(PropertyName = "groupOrderManager", DefaultValueHandling = DefaultValueHandling.Ignore)] public GroupOrderManager GroupOrderManager { get; set; } /// /// The adjustment mode used on the order fill price /// [JsonProperty(PropertyName = "priceAdjustmentMode")] public DataNormalizationMode PriceAdjustmentMode { get; set; } /// /// Added a default constructor for JSON Deserialization: /// protected Order() { Time = new DateTime(); PriceCurrency = string.Empty; Symbol = Symbol.Empty; Status = OrderStatus.None; Tag = string.Empty; BrokerId = new List(); Properties = new OrderProperties(); GroupOrderManager = null; } /// /// New order constructor /// /// Symbol asset we're seeking to trade /// Quantity of the asset we're seeking to trade /// Time the order was placed /// Manager for the orders in the group if this is a combo order /// User defined data tag for this order /// The order properties for this order protected Order(Symbol symbol, decimal quantity, DateTime time, GroupOrderManager groupOrderManager, string tag = "", IOrderProperties properties = null) { Time = time; PriceCurrency = string.Empty; Quantity = quantity; Symbol = symbol; Status = OrderStatus.None; Tag = tag; BrokerId = new List(); Properties = properties ?? new OrderProperties(); GroupOrderManager = groupOrderManager; } /// /// New order constructor /// /// Symbol asset we're seeking to trade /// Quantity of the asset we're seeking to trade /// Time the order was placed /// User defined data tag for this order /// The order properties for this order protected Order(Symbol symbol, decimal quantity, DateTime time, string tag = "", IOrderProperties properties = null) : this(symbol, quantity, time, null, tag, properties) { } /// /// Creates an enumerable containing each position resulting from executing this order. /// /// /// This is provided in anticipation of a new combo order type that will need to override this method, /// returning a position for each 'leg' of the order. /// /// An enumerable of positions matching the results of executing this order public virtual IEnumerable CreatePositions(SecurityManager securities) { var security = securities[Symbol]; yield return new Position(security, Quantity); } /// /// Gets the value of this order at the given market price in units of the account currency /// NOTE: Some order types derive value from other parameters, such as limit prices /// /// The security matching this order's symbol /// The value of this order given the current market price /// TODO: we should remove this. Only used in tests public decimal GetValue(Security security) { var value = GetValueImpl(security); return value*security.QuoteCurrency.ConversionRate*security.SymbolProperties.ContractMultiplier; } /// /// Gets the order value in units of the security's quote currency for a single unit. /// A single unit here is a single share of stock, or a single barrel of oil, or the /// cost of a single share in an option contract. /// /// The security matching this order's symbol protected abstract decimal GetValueImpl(Security security); /// /// Gets the default tag for this order /// /// The default tag public virtual string GetDefaultTag() { return string.Empty; } /// /// Gets a new unique incremental id for this order /// /// Returns a new id for this order internal int GetNewId() { return Interlocked.Increment(ref _incrementalId); } /// /// Modifies the state of this order to match the update request /// /// The request to update this order object public virtual void ApplyUpdateOrderRequest(UpdateOrderRequest request) { if (request.OrderId != Id) { throw new ArgumentException("Attempted to apply updates to the incorrect order!"); } if (request.Quantity.HasValue) { Quantity = request.Quantity.Value; } if (request.Tag != null) { Tag = request.Tag; } } /// /// Returns a string that represents the current object. /// /// /// A string that represents the current object. /// /// 2 public override string ToString() { return Messages.Order.ToString(this); } /// /// Creates a deep-copy clone of this order /// /// A copy of this order public abstract Order Clone(); /// /// Copies base Order properties to the specified order /// /// The target of the copy protected void CopyTo(Order order) { order.Id = Id; // The group order manager has to be set before the quantity, // since combo orders might need it to calculate the quantity in the Quantity setter. order.GroupOrderManager = GroupOrderManager; order.Time = Time; order.LastFillTime = LastFillTime; order.LastUpdateTime = LastUpdateTime; order.CanceledTime = CanceledTime; order.BrokerId = BrokerId.ToList(); order.ContingentId = ContingentId; order.Price = Price; order.PriceCurrency = PriceCurrency; order.Quantity = Quantity; order.Status = Status; order.Symbol = Symbol; order.Tag = Tag; order.Properties = Properties.Clone(); order.OrderSubmissionData = OrderSubmissionData?.Clone(); order.PriceAdjustmentMode = PriceAdjustmentMode; } /// /// Creates an to match the specified /// /// The to create an order for /// The that matches the request public static Order CreateOrder(SubmitOrderRequest request) { return CreateOrder(request.OrderId, request.OrderType, request.Symbol, request.Quantity, request.Time, request.Tag, request.OrderProperties, request.LimitPrice, request.StopPrice, request.TriggerPrice, request.TrailingAmount, request.TrailingAsPercentage, request.GroupOrderManager); } private static Order CreateOrder(int orderId, OrderType type, Symbol symbol, decimal quantity, DateTime time, string tag, IOrderProperties properties, decimal limitPrice, decimal stopPrice, decimal triggerPrice, decimal trailingAmount, bool trailingAsPercentage, GroupOrderManager groupOrderManager) { Order order; switch (type) { case OrderType.Market: order = new MarketOrder(symbol, quantity, time, tag, properties); break; case OrderType.Limit: order = new LimitOrder(symbol, quantity, limitPrice, time, tag, properties); break; case OrderType.StopMarket: order = new StopMarketOrder(symbol, quantity, stopPrice, time, tag, properties); break; case OrderType.StopLimit: order = new StopLimitOrder(symbol, quantity, stopPrice, limitPrice, time, tag, properties); break; case OrderType.TrailingStop: order = new TrailingStopOrder(symbol, quantity, stopPrice, trailingAmount, trailingAsPercentage, time, tag, properties); break; case OrderType.LimitIfTouched: order = new LimitIfTouchedOrder(symbol, quantity, triggerPrice, limitPrice, time, tag, properties); break; case OrderType.MarketOnOpen: order = new MarketOnOpenOrder(symbol, quantity, time, tag, properties); break; case OrderType.MarketOnClose: order = new MarketOnCloseOrder(symbol, quantity, time, tag, properties); break; case OrderType.OptionExercise: order = new OptionExerciseOrder(symbol, quantity, time, tag, properties); break; case OrderType.ComboLimit: order = new ComboLimitOrder(symbol, quantity, limitPrice, time, groupOrderManager, tag, properties); break; case OrderType.ComboLegLimit: order = new ComboLegLimitOrder(symbol, quantity, limitPrice, time, groupOrderManager, tag, properties); break; case OrderType.ComboMarket: order = new ComboMarketOrder(symbol, quantity, time, groupOrderManager, tag, properties); break; default: throw new ArgumentOutOfRangeException(); } order.Status = OrderStatus.New; order.Id = orderId; return order; } } }