-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
146 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using System; | ||
using OrderBook.Core.Model.Enums; | ||
|
||
namespace OrderBook.Core.Model | ||
{ | ||
public class ProcessedOrder | ||
{ | ||
public string Symbol { get; set; } | ||
public OrderSide OrderSide { get; set; } | ||
public int Quantity { get; set; } | ||
public int Price { get; set; } | ||
public DateTime TimestampReceived { get; set; } | ||
public DateTime TimestampExecuted { get; set; } | ||
public Guid Reference { get; set; } | ||
|
||
public static ProcessedOrder CreateFromOrder(Order order) | ||
{ | ||
return new ProcessedOrder | ||
{ | ||
Symbol = order.Symbol, | ||
OrderSide = order.OrderSide, | ||
Price = order.Price, | ||
Quantity = order.Quantity, | ||
Reference = order.Reference, | ||
TimestampReceived = order.Timestamp, | ||
TimestampExecuted = DateTime.UtcNow | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,106 @@ | ||
using OrderBook.Core.Model; | ||
using OrderBook.Core.Model.Enums; | ||
using System.Messaging; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System; | ||
|
||
namespace OrderBook.Server | ||
{ | ||
class Program | ||
{ | ||
private const string MESSAGE_QUEUE_RX_NAME = @".\Private$\OrderBookServer_OrdersQueue"; | ||
private const string MESSAGE_QUEUE_TX_NAME = @".\Private$\OrderBookServer_ProcessedOrdersQueue"; | ||
private static MessageQueue _rxMessageQueue; | ||
private static MessageQueue _txMessageQueue; | ||
|
||
private static readonly IReadOnlyList<string> AvailableSymbols = new List<string> { "TEST", "TEST1", "TEST2" }; | ||
private static Dictionary<string, Core.Model.OrderBook> _orderBooks; | ||
|
||
static void Main(string[] args) | ||
{ | ||
var orderBookForSomeStock = new Core.Model.OrderBook(); | ||
orderBookForSomeStock.Orders.Add(new Order(OrderSide.Sell, 100, 32)); | ||
|
||
// wait for orders | ||
// try matching | ||
// put in order book if not matched | ||
// delete from orders if matched and broadcast the trade to everyone | ||
// at the end of trading day, close orders depending on "good til x" | ||
InitializeOrderBooks(); | ||
InitializeMessageQueues(); | ||
|
||
while (true) | ||
{ | ||
// TODO: | ||
// if (current time is in market closed time) | ||
// Cleanup "good til x" orders: refer to the Duration section in http://apps.tmx.com/en/trading/order_types/ | ||
// block on WaitForMarketOpen() | ||
|
||
var message = _rxMessageQueue.Receive(); | ||
ProcessOrder((Order)message.Body); | ||
} | ||
} | ||
|
||
private static void InitializeOrderBooks() | ||
{ | ||
_orderBooks = new Dictionary<string, Core.Model.OrderBook>(); | ||
|
||
// TODO: load orderbooks from disk | ||
|
||
foreach (var symbol in AvailableSymbols) | ||
{ | ||
_orderBooks.Add(symbol, new Core.Model.OrderBook()); | ||
} | ||
} | ||
|
||
private static void InitializeMessageQueues() | ||
{ | ||
CreateOrOpenQueue(ref _rxMessageQueue, MESSAGE_QUEUE_RX_NAME); | ||
CreateOrOpenQueue(ref _txMessageQueue, MESSAGE_QUEUE_TX_NAME); | ||
} | ||
|
||
private static void CreateOrOpenQueue(ref MessageQueue messageQueue, string queueName) | ||
{ | ||
if (MessageQueue.Exists(queueName)) | ||
{ | ||
messageQueue = new MessageQueue(queueName); | ||
} | ||
else | ||
{ | ||
messageQueue = MessageQueue.Create(queueName); | ||
} | ||
|
||
messageQueue.Formatter = new BinaryMessageFormatter(); | ||
} | ||
|
||
private static void ProcessOrder(Order order) | ||
{ | ||
// Update timestamp to server time | ||
order.Timestamp = DateTime.UtcNow; | ||
|
||
// Try to math the order | ||
var orderBook = _orderBooks[order.Symbol]; | ||
var matches = orderBook.Orders | ||
.Where(bookOrder => bookOrder.OrderSide != order.OrderSide | ||
&& (order.OrderSide == OrderSide.Buy ? bookOrder.Price <= order.Price : bookOrder.Price >= order.Price) | ||
&& bookOrder.Quantity == order.Quantity) // TODO: temporary (remove when handle partial fills) | ||
.OrderBy(bookOrder => bookOrder.Price) | ||
.ThenBy(bookOrder => bookOrder.Timestamp) | ||
.ToList(); | ||
|
||
// TODO: handle partial fills (execute some trades and put the rest of the order in the queue) | ||
|
||
if (matches.Any()) | ||
{ | ||
// Delete from orders if matched and broadcast the trade to everyone | ||
var match = matches.First(); | ||
orderBook.Orders.Remove(match); | ||
BroadcastProcessedOrder(ProcessedOrder.CreateFromOrder(match)); | ||
BroadcastProcessedOrder(ProcessedOrder.CreateFromOrder(order)); | ||
} | ||
else | ||
{ | ||
// Put in order book if not matched | ||
orderBook.Orders.Add(order); | ||
} | ||
} | ||
|
||
private static void BroadcastProcessedOrder(ProcessedOrder order) | ||
{ | ||
_txMessageQueue.Send(order); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,14 @@ | ||
# OrderBook | ||
|
||
A C# price-time order book matching engine. | ||
A C# price-time order book matching engine. | ||
|
||
## Prerequisites | ||
|
||
OrderBook.Server and OrderBook.ServerEndpoint projects make use of *Microsoft Message Que (MSMQ) Server*. To enable this Windows feature, run `OptionalFeatures` at a command prompt and look for MSQM. | ||
|
||
## Architecture | ||
|
||
OrderBook.Server contains the OrderBook (the list of orders). Clients connect to OrderBook.ServerEndpoint, which transmits orders to the OrderBook.Server and broadcast notifications to clients. Clients never connect directly to OrderBook.Server for security and load concerns. | ||
|
||
The OrderBook.Server queue expects to recieve messages of type `Order` and sends messages of type `ProcessedOrder`. | ||
|