Create a solution for maintaining information about orders that are being placed on an exchange by an order router. This should derive from
IOrderManager (provided in challenge.h).
The in-house trading system tracks orders using a unique internal identifier (provided in challenge.h). The exchange tracks orders using a unique
text identifier.
Each order has a price and a quantity. Both are uint32_t. Prices and quantities must be positive numbers.
When an order request operation is received from a trader, the order manager must be able to store and retrieve information using the
OrderIdentifier.
When a response is received from the exchange the order manager must be able to retrieve the information using the text identifier.
The trader can perform the following requests. Each of these includes the OrderIdentifier but not the exchange's identifier:
Enter order. Includes OrderIdentifier, price and quantity but not exchange's identifier (this is provided when the exchange confirms the
order).
Cancel order. Only the OrderIdentifier is provided. The order is considered as still in the market until the exchange confirms the
cancellation.
The exchange can provide the following updates:
New order confirmed. Contains both the OrderIdentifier and the exchange's text identifier.
Order traded. Includes quantity traded and exchange's identifier.
Order cancelled. This can be due to the trader cancelling it, or the exchange cancelling the order. Contains only exchange's identifier.
If all the order quantity is traded then the order should be considered to be removed from the market and stops being active. An order can be
traded multiple times until all its quantity is used up.
The trader must not reuse a previously used order identifier, and they are only allowed cancel an order which is active in the market and which
they haven't already sent a cancel request for.
An order becomes active when the exchange confirms it, and stops being active when the exchange provides a cancel update on the order or a
trade update which uses all of the remaining quantity of the order.
Requests and updates have restrictions in the sequence in which they can occur for a specific order. Here A->B means that B can only occur after
A, if it occurs. There are no restrictions on sequencing between different orders.
Enter order -> New order confirmed
New order confirmed -> Order traded
New order confirmed -> Order cancelled
Order traded -> Order cancelled
You can assume the exchange never sends incorrect information, for instance a trade greater than the order quantity or a cancellation for an
order which doesn't exist.
Your order manager solution should handle requests from the trader and responses from the exchange in the form of methods being called in the
class you derive from IOrderManager. You are free to create other classes as part of the solution.
Please include explanatory comments describing why you chose to design each part of the solution in the way you did, plus a description of your
testing methods. If you discover ambiguities in the challenge, describe what assumptions you have made to allow you to continue.
Put all your files in an archive along with instructions how to build and run it.
#pragma once
#include <stdint.h>
#include <string>
#include <map>
// company's order identifier
struct OrderIdentifier {
OrderIdentifier() = default;
OrderIdentifier(uint16_t m, uint16_t d, uint16_t t, uint32_t s)
: _market(m)
, _desk(d)
, _trader(t)
, _sequence(s)
{}
uint16_t _market{0};
uint16_t _desk{0};
uint16_t _trader{0};
uint32_t _sequence{0}; // increments with each order from a particular trader
bool operator < (OrderIdentifier const& id) const
{
// construct key, assume each para takes up 8 bits
uint32_t key1 = (_market << 24) | (_desk << 16) | (_trader << 8) | _sequence;
uint32_t key2 = (id._market << 24) | (id._desk << 16) | (id._trader << 8) | id._sequence;
return (key1 < key2) ? true : false;
}
};
// company's order
struct Order {
enum orderStatus{
ORDER_STATUS_ENTER = 0,
ORDER_STATUS_CONFIRMED = 1,
ORDER_STATUS_TRADED = 2,
ORDER_STATUS_CANCELLED = 3,
ORDER_STATUS_MAX
};
Order() = default;
Order(uint32_t p, uint32_t q) : _price(p), _quantity(q){}
uint32_t _price{0}; // order price
uint32_t _quantity{0}; // order quantity
bool _isActive{false}; // order active or not
bool _isCancelled{false}; // order cancelled by trader or not
orderStatus _status{ORDER_STATUS_ENTER}; // order status
};
//
// The order router will call these methods as it receives order operations from the
// trader and exchange.
// Note:
// 1. Thread safety needs to be considered for your implementation of this class: e.g.
// - OnTraderXXX may be called from different threads
// - You can assume OnExchangeXXX will only be called from a single thread.
// - But OnTraderXXX and OnExchangeXXX maybe called from different threads.
// 2. Performance, in terms of latency, of all the interface functions are important.
//
class IOrderManager
{
public:
// trader operations - these return false if there is a problem
virtual bool OnTraderEnter(const OrderIdentifier& aInternal, uint32_t aPrice, uint32_t aQuantity) = 0;
virtual bool OnTraderCancel(const OrderIdentifier& aInternal) = 0;
// exchange operations - these return false if there is a problem
virtual bool OnExchangeNew(const OrderIdentifier& aInternal, const std::string& aExternal) = 0;
virtual bool OnExchangeTrade(const std::string& aExternal, uint32_t aQuantity) = 0;
virtual bool OnExchangeCancel(const std::string& aExternal) = 0;
virtual bool IsOrderActive(const OrderIdentifier& aInternal) const = 0;
virtual bool IsOrderActive(const std::string& aExternal) const = 0;
// returns the quantity of the order that is active in the market, or zero if the order isn't
// recognised or is not active
virtual uint32_t GetActiveOrderQuantity(const OrderIdentifier& aInternal) const = 0;
};
// order manager solution derived from IOrderManager
class OrderManager : public IOrderManager
{
public:
OrderManager() = default;
virtual ~OrderManager(){};
// get mOrdersHistory key from order identifier
inline uint32_t getKey(const OrderIdentifier& aInternal) const
{
return (aInternal._market << 24) | (aInternal._desk << 16)
| (aInternal._trader << 8) | aInternal._sequence;
}
// trader operations - these return false if there is a problem
virtual bool OnTraderEnter(const OrderIdentifier& aInternal, uint32_t aPrice, uint32_t aQuantity);
virtual bool OnTraderCancel(const OrderIdentifier& aInternal);
// exchange operations - these return false if there is a problem
virtual bool OnExchangeNew(const OrderIdentifier& aInternal, const std::string& aExternal);
virtual bool OnExchangeTrade(const std::string& aExternal, uint32_t aQuantity);
virtual bool OnExchangeCancel(const std::string& aExternal);
virtual bool IsOrderActive(const OrderIdentifier& aInternal) const;
virtual bool IsOrderActive(const std::string& aExternal) const;
// returns the quantity of the order that is active in the market, or zero if the order isn't
// recognised or is not active
virtual uint32_t GetActiveOrderQuantity(const OrderIdentifier& aInternal) const;
public:
/**************************************************************************
* 1.store all history order in mOrdersHistory, to stop trader from reusing a
* previous order identifier
* 2.when reponse from exchange, to get order identifier from order identifier
* so that only one copy of order exists
* 3.use unordered_map so an order can be found with low latency
***************************************************************************/
std::unordered_map<std::string, OrderIdentifier> mOrdersInMarket; // all order in market
std::unordered_map<uint32_t, Order> mOrdersHistory; // all order in history
};
#include "challenge.h"
#include "assert.h"
#include <mutex>
std::mutex mutexForOrder; // order mutex
/*
********************************************************************
* Desc: insert an order in mOrdersInMarket if not exist
* status: ORDER_STATUS_ENTER
*********************************************************************
*/
bool OrderManager::OnTraderEnter(const OrderIdentifier& aInternal, uint32_t aPrice, uint32_t aQuantity)
{
std::lock_guard<std::mutex> lck(mutexForOrder);
uint32_t key = getKey(aInternal);
// trader must not reuse a previously used order identifier
std::map<uint32_t, Order>::iterator it = mOrdersHistory.find(key);
if (it != mOrdersHistory.end())
{
return false;
}
Order order(aPrice, aQuantity);
mOrdersHistory.insert(std::pair<uint32_t, Order>(key, order));
return true;
}
/*
********************************************************************
* Desc: cancel an order active in the market or not cancelled before
* status: ORDER_STATUS_CONFIRMED -> ORDER_STATUS_CANCELLED
* ORDER_STATUS_TRADED -> ORDER_STATUS_CANCELLED
*********************************************************************
*/
bool OrderManager::OnTraderCancel(const OrderIdentifier& aInternal)
{
std::lock_guard<std::mutex> lck(mutexForOrder);
uint32_t key = getKey(aInternal);
// cancel an order not exist
std::map<uint32_t, Order>::iterator it = mOrdersHistory.find(key);
if (it == mOrdersHistory.end())
{
return false;
}
// order cancelld before
if (it->second._isCancelled == true)
{
return false;
}
// order not active
if (it->second._isActive == false)
{
return false;
}
// order status invalid
if (it->second._status != Order::ORDER_STATUS_CONFIRMED && it->second._status != Order::ORDER_STATUS_TRADED)
{
return false;
}
// update order status
it->second._isCancelled = true;
it->second._status = Order::ORDER_STATUS_CANCELLED;
return true;
}
/*
********************************************************************
* Desc: insert an order in mText2Internal, and update order status
* status: ORDER_STATUS_ENTER -> ORDER_STATUS_CONFIRMED
*********************************************************************
*/
bool OrderManager::OnExchangeNew(const OrderIdentifier& aInternal, const std::string& aExternal)
{
std::lock_guard<std::mutex> lck(mutexForOrder);
uint32_t key = getKey(aInternal);
std::map<uint32_t, Order>::iterator it = mOrdersHistory.find(key);
if (it == mOrdersHistory.end())
{
return false;
}
// ignore a history order
if (Order::ORDER_STATUS_MAX == it->second._status)
{
return false;
}
it->second._status = Order::ORDER_STATUS_CONFIRMED;
it->second._isActive = true;
// update order status
it->second._status = Order::ORDER_STATUS_CONFIRMED;
it->second._isActive = true;
// update mOrdersInMarket, not record key to keep OrderIdentifier information
mOrdersInMarket.insert(std::pair<std::string, OrderIdentifier>(aExternal, aInternal));
return true;
}
/*
********************************************************************
* Desc: Trade by aQuantity, and update order status
* if quantity used up, erase order
* status: ORDER_STATUS_CONFIRMED -> ORDER_STATUS_TRADED
*********************************************************************
*/
bool OrderManager::OnExchangeTrade(const std::string& aExternal, uint32_t aQuantity)
{
std::lock_guard<std::mutex> lck(mutexForOrder);
// find OrderIdentifier in mOrdersInMarket by exchange's identifier
std::map<std::string, OrderIdentifier>::iterator iter = mOrdersInMarket.find(aExternal);
if (iter == mOrdersInMarket.end())
{
return false;
}
// find order in mOrdersHistory by key
uint32_t key = getKey(iter->second);
std::map<uint32_t, Order>::iterator it = mOrdersHistory.find(key);
// order not exist
if (it == mOrdersHistory.end())
{
return false;
}
// status invalid
if (it->second._status != Order::ORDER_STATUS_CONFIRMED && it->second._status != Order::ORDER_STATUS_TRADED)
{
return false;
}
// aQuantity invalid
if (aQuantity > it->second._quantity)
{
return false;
}
// update order status
it->second._quantity -= aQuantity;
if (0 != it->second._quantity)
{
it->second._status = Order::ORDER_STATUS_TRADED;
}
else
{
// remove from market
mOrdersInMarket.erase(aExternal);
it->second._status = Order::ORDER_STATUS_MAX;
it->second._isActive = false;
it->second._isCancelled = true;
}
return true;
}
/*
********************************************************************
* Desc: Cancelled by Exchange
* status: ORDER_STATUS_CANCELLED -> ORDER_STATUS_ENTER
*********************************************************************
*/
bool OrderManager::OnExchangeCancel(const std::string& aExternal)
{
std::lock_guard<std::mutex> lck(mutexForOrder);
// find OrderIdentifier in mOrdersInMarket by exchange's identifier
std::map<std::string, OrderIdentifier>::iterator iter = mOrdersInMarket.find(aExternal);
if (iter == mOrdersInMarket.end())
{
return false;
}
// find order in mOrdersHistory by key
uint32_t key = getKey(iter->second);
std::map<uint32_t, Order>::iterator it = mOrdersHistory.find(key);
// order not exist
if (it == mOrdersHistory.end())
{
return false;
}
// status invalid
if (it->second._status != Order::ORDER_STATUS_CANCELLED)
{
return false;
}
// update order status
mOrdersInMarket.erase(aExternal);
it->second._status = Order::ORDER_STATUS_MAX;
it->second._isActive = false;
it->second._isCancelled = true;
return true;
}
bool OrderManager::IsOrderActive(const OrderIdentifier& aInternal) const
{
std::lock_guard<std::mutex> lck(mutexForOrder);
uint32_t key = getKey(aInternal);
std::map<uint32_t, Order>::const_iterator it = mOrdersHistory.find(key);
if (it == mOrdersHistory.end())
{
return false;
}
return it->second._isActive;
}
bool OrderManager::IsOrderActive(const std::string& aExternal) const
{
std::lock_guard<std::mutex> lck(mutexForOrder);
// find OrderIdentifier in mOrdersInMarket by exchange's identifier
std::map<std::string, OrderIdentifier>::const_iterator iter = mOrdersInMarket.find(aExternal);
if (iter == mOrdersInMarket.end())
{
return false;
}
// find order in mOrdersHistory by key
uint32_t key = getKey(iter->second);
std::map<uint32_t, Order>::const_iterator it = mOrdersHistory.find(key);
// order not exist
if (it == mOrdersHistory.end())
{
return false;
}
return it->second._isActive;
}
/*
********************************************************************
* Desc: get order quantity, if not exist, return 0
*********************************************************************
*/
uint32_t OrderManager::GetActiveOrderQuantity(const OrderIdentifier& aInternal) const
{
std::lock_guard<std::mutex> lck(mutexForOrder);
uint32_t key = getKey(aInternal);
std::map<uint32_t, Order>::const_iterator it = mOrdersHistory.find(key);
if (it == mOrdersHistory.end())
{
return 0;
}
if (false == it->second._isActive)
{
return 0;
}
return it->second._quantity;
}
// test normal flow
void testCase01()
{
OrderIdentifier id1(0, 0, 0, 0);
OrderIdentifier id2(0, 0, 0, 1);
OrderIdentifier id3(0, 0, 0, 2);
OrderIdentifier id4(0, 0, 0, 0);
OrderIdentifier id5(0, 0, 0, 5);
OrderManager mgr;
// on trader enter
assert(true == mgr.OnTraderEnter(id1, 2, 10)); // valid input
assert(true == mgr.OnTraderEnter(id2, 3, 20)); // valid input
assert(true == mgr.OnTraderEnter(id3, 4, 5)); // valid input
assert(false == mgr.OnTraderEnter(id4, 2, 11)); // invalid input
assert(3 == mgr.mOrdersHistory.size()); // check order count
uint32_t key1 = mgr.getKey(id1);
std::map<uint32_t, Order>::iterator it = mgr.mOrdersHistory.find(key1);
assert(Order::ORDER_STATUS_ENTER == it->second._status); // check order status
// on exchane new
assert(false == mgr.OnExchangeNew(id5, "id5")); // invalid input
assert(true == mgr.OnExchangeNew(id1, "id1")); // valid input
assert(1 == mgr.mOrdersInMarket.size()); // check count
it = mgr.mOrdersHistory.find(key1);
assert(Order::ORDER_STATUS_CONFIRMED == it->second._status); // check order status
assert(true == it->second._isActive); // check active
// on exchange trade
assert(false == mgr.OnExchangeTrade("id5", 1)); // order not exist
assert(false == mgr.OnExchangeTrade("id2", 1)); // order status err
assert(false == mgr.OnExchangeTrade("id1", 100)); // order quantity invalid
assert(true == mgr.OnExchangeTrade("id1", 1)); // valid input
it = mgr.mOrdersHistory.find(key1);
assert(Order::ORDER_STATUS_TRADED == it->second._status); // check order status
assert(9 == it->second._quantity); // chekc order quantity
// is order active
assert(true == mgr.IsOrderActive("id1")); // valid
assert(false == mgr.IsOrderActive("id2")); // invalid
assert(false == mgr.IsOrderActive("id5")); // order not exist
assert(true == mgr.IsOrderActive(id1)); // valid
assert(false == mgr.IsOrderActive(id2)); // invalid
assert(false == mgr.IsOrderActive(id5)); // invalid
// get order quantity
assert(9 == mgr.GetActiveOrderQuantity(id1)); // order active
assert(0 == mgr.GetActiveOrderQuantity(id2)); // order inactive
assert(0 == mgr.GetActiveOrderQuantity(id2)); // order not exits
// on exchange trade
assert(true == mgr.OnExchangeTrade("id1", 9)); // valid input
assert(3 == mgr.mOrdersHistory.size()); // check order count
assert(true == mgr.mOrdersInMarket.empty()); // check count
// on exchange new
assert(false == mgr.OnExchangeNew(id1, "id1")); // a history order
assert(true == mgr.OnExchangeNew(id2, "id2")); // valid input
// on trader cancel
assert(false == mgr.OnTraderCancel(id3)); // cancel an invalid order
assert(true == mgr.OnTraderCancel(id2)); // valid input
uint32_t key2 = mgr.getKey(id2);
it = mgr.mOrdersHistory.find(key2);
assert(Order::ORDER_STATUS_CANCELLED == it->second._status); // check order status
assert(true == it->second._isActive); // check active
assert(true == it->second._isCancelled); // check cancelled or not
assert(false == mgr.OnTraderCancel(id2)); // order cancelled before
// on exchane cancel
assert(false == mgr.OnExchangeCancel("id3")); // invalid input
assert(true == mgr.OnExchangeCancel("id2")); // valid input
assert(false == it->second._isActive); // check active
assert(true == it->second._isCancelled); // check cancelled or not
assert(Order::ORDER_STATUS_MAX == it->second._status); // check order status
}
int main()
{
testCase01();
return 0;
}
OS: Linux
G++ Version: 4.8.2
Build CMD: g++ -g -std=c++11 challenge.cpp -o challenge
Run : ./challenge