/* * 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 NAMESPACES **********************************************************/ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using QuantConnect.Interfaces; using QuantConnect.Logging; using QuantConnect.Orders; namespace QuantConnect.Lean.Engine.TransactionHandlers { /// /// Backtesting transaction handler class for modelling the order fills and portfolio impact when in a backtest. /// public class BacktestingTransactionHandler : ITransactionHandler { /******************************************************** * CLASS VARIABLES *********************************************************/ private int _orderId = 1; private bool _exitTriggered = false; private bool _ready = false; private bool _isActive = false; private IAlgorithm _algorithm; /******************************************************** * CLASS PROPERTIES *********************************************************/ /// /// The orders queue holds orders which are sent to exchange, partially filled, completely filled or cancelled. /// Once the transaction thread has worked on them they get put here while witing for fill updates. /// public ConcurrentDictionary Orders { get { return _algorithm.Transactions.Orders; } set { _algorithm.Transactions.Orders = value; } } /// /// OrderEvents is an orderid indexed collection of events attached to each order. Because an order might be filled in /// multiple legs it is important to keep a record of each event. /// public ConcurrentDictionary> OrderEvents { get { return _algorithm.Transactions.OrderEvents; } set { _algorithm.Transactions.OrderEvents = value; } } /// /// OrderQueue holds the newly updated orders from the user algorithm waiting to be processed. Once /// orders are processed they are moved into the Orders queue awaiting the brokerage response. /// public ConcurrentQueue OrderQueue { get { return _algorithm.Transactions.OrderQueue; } set { _algorithm.Transactions.OrderQueue = value; } } /// /// Boolean flag signalling the handler is ready and all orders have been processed. /// public bool Ready { get { return _ready; } } /// /// Boolean flag indicating the thread is busy. /// False indicates it is completely finished processing and ready to be terminated. /// public bool IsActive { get { return _isActive; } } /******************************************************** * CONSTRUCTOR *********************************************************/ /// /// Constructor for the backtesting transaction handler. /// /// Algorithm instance public BacktestingTransactionHandler(IAlgorithm algorithm) { _algorithm = algorithm; _isActive = true; _ready = false; } /******************************************************** * CLASS METHODS *********************************************************/ /// /// Primary thread entry point to launch the transaction thread. /// public void Run() { //Run until the end of the algorithm. while (!_exitTriggered) { //1. Add order commands from queue to primary order list. if (OrderQueue.Count == 0) { //We've processed all the orders in queue.Allow interruption of thread if nothing to do (99.99% of time). _ready = true; //Set finished processing flag: _algorithm.ProcessingOrder = false; //NOP. Thread.Sleep(1); } else { //We're working... _ready = false; //Scan jobs in the new orders queue: Order order; if (OrderQueue.TryDequeue(out order)) { switch (order.Status) { case OrderStatus.New: //If we don't have this key, add it to the dictionary if (!Orders.ContainsKey(order.Id)) { //Tell algorithm to wait: _algorithm.ProcessingOrder = true; //Add the order to the collection Orders.TryAdd(order.Id, order); } break; case OrderStatus.Canceled: if (Orders.ContainsKey(order.Id) && Orders[order.Id].Status == OrderStatus.Submitted) { //Just set the master dictionary to a cancelled order, only IF we've only been submitted and no further processing. Orders[order.Id] = order; } break; case OrderStatus.Update: if (Orders.ContainsKey(order.Id) && Orders[order.Id].Status == OrderStatus.Submitted) { //Just set the master dictionary to a updated order, only IF we've only been submitted and no further processing. Orders[order.Id] = order; } break; } } } //2. NOW ALL ORDERS IN ORDER DICTIONARY::> // Scan through Orders: Process fills. Trigger Events. // Refresh the order model: look at the orders for ones - process every time. var keys = (from order in Orders where order.Value.Status != OrderStatus.Filled && order.Value.Status != OrderStatus.Canceled && order.Value.Status != OrderStatus.Invalid select order.Key).ToList(); //Now we have the list of keys; re-apply the order models to each order. foreach (var id in keys) { //We're working... var fill = new OrderEvent(); _ready = false; var order = Orders[id]; var sufficientBuyingPower = _algorithm.Transactions.GetSufficientCapitalForOrder(_algorithm.Portfolio, order); //Before we check this queued order make sure we have buying power: if (sufficientBuyingPower) { //Based on the order type: refresh its model to get fill price and quantity fill = _algorithm.Securities[order.Symbol].Model.Fill(_algorithm.Securities[order.Symbol], order); //Apply the filled order to our portfolio: if (fill.Status == OrderStatus.Filled || fill.Status == OrderStatus.PartiallyFilled) { //If the fill models come back suggesting filled, process the affects on portfolio _algorithm.Portfolio.ProcessFill(fill); } } else { //Flag order as invalid and push off queue: order.Status = OrderStatus.Invalid; _algorithm.Error("Order Error: id: " + id + ": Insufficient buying power to complete order."); } //We have an event! :) Order filled, send it in to be handled by algorithm portfolio. if (fill.Status != OrderStatus.None) //order.Status != OrderStatus.Submitted { //Create new order event: Engine.ResultHandler.OrderEvent(fill); try { //Trigger our order event handler _algorithm.OnOrderEvent(fill); } catch (Exception err) { _algorithm.Error("Order Event Handler Error: " + err.Message); } } } } // End While. Log.Trace("BacktestingTransactionHandler.Run(): Ending Thread..."); _isActive = false; } /// /// Submit a new order to be processed. /// /// New order object /// New unique quantconnect order id public int NewOrder(Order order) { //If this is a new order (with no id) set it: if (order.Id == 0) order.Id = _orderId++; //Submit to queue order.Status = OrderStatus.New; OrderQueue.Enqueue(order); _ready = false; return order.Id; } /// /// Update and resubmit the order to the OrderQueue for processing. /// /// Order we'd like updated /// True if successful, false if already cancelled or filled. public bool UpdateOrder(Order order) { //Failed. if (Orders[order.Id].Status == OrderStatus.Filled || Orders[order.Id].Status == OrderStatus.Canceled) { return false; } //Flag the order as new, send it to the queue: order.Status = OrderStatus.Update; OrderQueue.Enqueue(order); _ready = false; return true; } /// /// Cancel the order specified /// /// Order we'd like to cancel. /// True if successful, false if its already been cancelled or filled. public bool CancelOrder(Order order) { //Failed. if (Orders[order.Id].Status == OrderStatus.Filled || Orders[order.Id].Status == OrderStatus.Canceled) { return false; } //Flag the order as new, send it to the queue: order.Status = OrderStatus.Canceled; OrderQueue.Enqueue(order); _ready = false; return true; } /// /// Set a local reference to the algorithm instance. /// /// IAlgorithm object public void SetAlgorithm(IAlgorithm algorithm) { //Access to the algorithm _algorithm = algorithm; } /// /// Signal a end of thread request to stop montioring the transactions. /// public void Exit() { _exitTriggered = true; } } // End Algorithm Class: } // End Namespace