Files
quantconnect--lean/Engine/TransactionHandlers/BacktestingTransactionHandler.cs
T
2015-01-12 12:03:33 -03:00

341 lines
13 KiB
C#

/*
* 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
{
/// <summary>
/// Backtesting transaction handler class for modelling the order fills and portfolio impact when in a backtest.
/// </summary>
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
*********************************************************/
/// <summary>
/// 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.
/// </summary>
public ConcurrentDictionary<int, Order> Orders
{
get
{
return _algorithm.Transactions.Orders;
}
set
{
_algorithm.Transactions.Orders = value;
}
}
/// <summary>
/// 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.
/// </summary>
public ConcurrentDictionary<int, List<OrderEvent>> OrderEvents
{
get
{
return _algorithm.Transactions.OrderEvents;
}
set
{
_algorithm.Transactions.OrderEvents = value;
}
}
/// <summary>
/// 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.
/// </summary>
public ConcurrentQueue<Order> OrderQueue
{
get
{
return _algorithm.Transactions.OrderQueue;
}
set
{
_algorithm.Transactions.OrderQueue = value;
}
}
/// <summary>
/// Boolean flag signalling the handler is ready and all orders have been processed.
/// </summary>
public bool Ready
{
get
{
return _ready;
}
}
/// <summary>
/// Boolean flag indicating the thread is busy.
/// False indicates it is completely finished processing and ready to be terminated.
/// </summary>
public bool IsActive
{
get
{
return _isActive;
}
}
/********************************************************
* CONSTRUCTOR
*********************************************************/
/// <summary>
/// Constructor for the backtesting transaction handler.
/// </summary>
/// <param name="algorithm">Algorithm instance</param>
public BacktestingTransactionHandler(IAlgorithm algorithm)
{
_algorithm = algorithm;
_isActive = true;
_ready = false;
}
/********************************************************
* CLASS METHODS
*********************************************************/
/// <summary>
/// Primary thread entry point to launch the transaction thread.
/// </summary>
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<int>();
//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;
}
/// <summary>
/// Submit a new order to be processed.
/// </summary>
/// <param name="order">New order object</param>
/// <returns>New unique quantconnect order id</returns>
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;
}
/// <summary>
/// Update and resubmit the order to the OrderQueue for processing.
/// </summary>
/// <param name="order">Order we'd like updated</param>
/// <returns>True if successful, false if already cancelled or filled.</returns>
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;
}
/// <summary>
/// Cancel the order specified
/// </summary>
/// <param name="order">Order we'd like to cancel.</param>
/// <returns>True if successful, false if its already been cancelled or filled.</returns>
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;
}
/// <summary>
/// Set a local reference to the algorithm instance.
/// </summary>
/// <param name="algorithm">IAlgorithm object</param>
public void SetAlgorithm(IAlgorithm algorithm)
{
//Access to the algorithm
_algorithm = algorithm;
}
/// <summary>
/// Signal a end of thread request to stop montioring the transactions.
/// </summary>
public void Exit()
{
_exitTriggered = true;
}
} // End Algorithm Class:
} // End Namespace