An advanced interactive console application to send orders and receive responses.
Source code
CUI.h
#include "Book.h"
#include <iostream>
#include <string>
#include <vector>
namespace CUI {
struct Screen
{
struct MessagePrinter
{
explicit MessagePrinter(const std::string& prefix) : prefix_(prefix) {}
template <typename MessageType>
void operator()(const MessageType& msg) const
{
info(prefix_, msg.toString());
}
std::string prefix_;
};
typedef Threading::Mutex Mutex;
typedef Threading::Guard<Threading::Mutex> Guard;
static Mutex& mutex()
{
static Mutex mutex;
return mutex;
}
static std::string getInput()
{
std::string userInput;
std::getline(std::cin, userInput);
return userInput;
}
template <typename T>
static T getInput(const T & defaultValue = T())
{
std::string userInput;
std::getline(std::cin, userInput);
if (userInput.empty())
return defaultValue;
else
{
Messaging::Int64 converted = Messaging::Int64();
std::istringstream ss(userInput);
ss >> converted;
return static_cast<T>(converted);
}
}
template <typename T>
static void getInput(T & value, const T & defaultValue = T())
{
std::string userInput;
std::getline(std::cin, userInput);
if (userInput.empty())
value = defaultValue;
else
{
Messaging::Int64 converted = Messaging::Int64();
std::istringstream ss(userInput);
ss >> converted;
value = static_cast<T>(converted);
}
}
{
std::string userInput;
std::getline(std::cin, userInput);
Timestamp timestamp;
if(
fromStr(timestamp, userInput, TimestampFormat::YYYYMMDD))
else
value = 0;
}
template <typename T>
static void getInputChar(T & value, const T & defaultValue = T())
{
std::string userInput;
std::getline(std::cin, userInput);
if (userInput.empty())
value = defaultValue;
else
{
std::istringstream ss(userInput);
ss >> converted;
value = static_cast<T>(converted);
}
}
static void info(const std::string& message)
{
info("INFO: ", message);
}
static void info(const std::string& prefix, const std::string& message)
{
Guard g(mutex());
std::cout << std::endl << prefix << message << std::endl;
}
static void out(const std::string& message)
{
Guard g(mutex());
std::cout << message << std::endl;
}
static void out(const std::string& prefix, const SbeMessage& message)
{
info("Unknown message type.");
}
template <typename ItemType>
static void outItems(const ItemType & items)
{
Guard g(mutex());
for (size_t index = 0; index < items.size(); ++index)
std::cout << (index + 1) << ") " << items[index].text << std::endl;
}
static bool shouldContinue()
{
{
Guard g(mutex());
std::cout << std::endl << "Press <Enter> to view next page or Enter 'quit' to abort current action: ";
}
const std::string userInput = getInput();
{
Guard g(mutex());
std::cout << std::endl;
}
return userInput.empty();
}
};
enum CommandExecutionStatus
{
TerminateExecution,
ContinueExecution,
RepeatExecution
};
template <typename Owner>
class Menu
{
public:
typedef void (Owner::*Command)(CommandExecutionStatus*);
explicit Menu(Owner* owner)
: owner_(owner)
{
}
~Menu()
{
}
Menu& add(const std::string& text, const std::string& commandText, Command command)
{
items_.push_back(Item(text, commandText, command));
return *this;
}
void processRequests() const
{
CommandExecutionStatus status = ContinueExecution;
while (TerminateExecution != status)
{
outputItems();
processRequest(&status);
}
}
private:
struct Item
{
Item(const std::string& itemText, const std::string& itemCommandText, Command itemCommand)
: text(itemText)
, commandText(itemCommandText)
, command(itemCommand)
{
}
Item(const Item& other)
: text(other.text)
, commandText(other.commandText)
, command(other.command)
{
}
std::string text;
std::string commandText;
Command command;
};
Owner* owner_;
std::vector<Item> items_;
void outputItems() const
{
Screen::info("Select operation you want to perform: ");
Screen::outItems(items_);
}
void processRequest(CommandExecutionStatus* status) const
{
Screen::out("Enter your choice : ");
std::string userInput = Screen::getInput();
const size_t selectedItemNumber = static_cast<size_t>(atoi(userInput.c_str()) - 1);
if (items_.size() <= selectedItemNumber)
{
Screen::info("Invalid command requested. Repeat your choice.");
}
else
{
try
{
const Item& selectedItem = items_[selectedItemNumber];
Screen::info(selectedItem.commandText);
(owner_->*(selectedItem.command))(status);
const unsigned int CommandExecutionTimeout = 500;
Threading::ThisThread::sleep(CommandExecutionTimeout);
}
catch (const std::exception& ex)
{
Screen::info("ERROR: ", ex.what());
}
}
}
};
template <typename Stringable>
class ListViewer
{
public:
typedef typename Samples::Book<Stringable>::EntryList StringableList;
static void outputItems(const StringableList& items, size_t itemsPerPage)
{
if (0 == items.size())
{
Screen::info("No items available.");
}
else
{
size_t itemNumber = 0;
size_t itemsToOutput = itemsPerPage;
for (StringableListConstEntry itemPtr = items.begin(); itemPtr != items.end(); ++itemPtr)
{
if (0 == --itemsToOutput)
{
itemsToOutput = itemsPerPage;
if (!Screen::shouldContinue())
return;
}
Screen::out(
toStr(++itemNumber) +
") " + (*itemPtr)->toString() +
".");
}
}
}
private:
typedef typename StringableList::const_iterator StringableListConstEntry;
};
}
Settings.h
#include <string>
#include <map>
namespace Samples
{
class Settings
{
public:
explicit Settings(const std::string& filePath);
std::string get(const std::string& key) const;
std::string get(const std::string& key, const std::string& defaultValue) const;
std::string getIfAny(const std::string& key) const;
int getAsInt(const std::string& key) const;
int getAsInt(const std::string& key, int defaultValue) const;
bool getAsBool(const std::string& key) const;
bool getAsBool(const std::string& key, bool defaultValue) const;
bool isSet(const std::string& key) const;
private:
typedef std::map<std::string, std::string> Key2ValueMap;
typedef Key2ValueMap::const_iterator Key2ValueMapEntry;
Key2ValueMap map_;
};
}
Settings.cpp
#include "Settings.h"
namespace
{
void trimSpaces(std::string* str)
{
const char whiteSpace[] = " \t\r";
size_t found = str->find_last_not_of(whiteSpace);
if (std::string::npos == found)
str->resize(0);
else
str->resize(found + 1);
found = str->find_first_not_of(whiteSpace);
if (std::string::npos != found && 0 != found)
str->erase(0, found);
}
int lexicalCompare(const std::string& left, const std::string& right)
{
#if defined(_WIN32)
return _stricmp(left.c_str(), right.c_str());
#else
return strcasecmp(left.c_str(), right.c_str());
#endif
}
}
namespace Samples
{
Settings::Settings(const std::string& filePath)
{
std::ifstream in(filePath.c_str());
if (!in)
{
std::string errReason("Cannot open the settings file '");
errReason += filePath;
errReason += "'";
throw std::domain_error(errReason);
}
std::string line;
while (getline(in, line))
{
const std::string::size_type comment = line.find('#');
if (std::string::npos != comment)
line.resize(comment);
std::string::size_type separator = line.find('=');
if (std::string::npos == separator)
continue;
std::string key = line.substr(0, separator);
trimSpaces(&key);
std::string value = line.substr(separator + 1);
trimSpaces(&value);
map_.insert(make_pair(key, value));
}
}
std::string Settings::get(const std::string& key) const
{
const Key2ValueMapEntry it = map_.find(key);
if (map_.end() == it)
{
std::string errReason("There's no setting '");
errReason += key;
errReason += "' available";
throw std::domain_error(errReason);
}
return it->second;
}
std::string Settings::get(const std::string& key, const std::string& defaultValue) const
{
const Key2ValueMapEntry it = map_.find(key);
return (map_.end() == it) ? defaultValue : it->second;
}
std::string Settings::getIfAny(const std::string& key) const
{
return get(key, std::string());
}
int Settings::getAsInt(const std::string& key) const
{
const std::string value = get(key);
return atoi(value.c_str());
}
int Settings::getAsInt(const std::string& key, int defaultValue) const
{
const std::string value = getIfAny(key);
return value.empty() ? defaultValue : atoi(value.c_str());
}
bool Settings::getAsBool(const std::string& key) const
{
const std::string value = get(key);
return 0 == lexicalCompare("true", value);
}
bool Settings::getAsBool(const std::string& key, bool defaultValue) const
{
const std::string value = getIfAny(key);
return !value.empty() ? 0 == lexicalCompare("true", value) : defaultValue;
}
bool Settings::isSet(const std::string& key) const
{
return map_.count(key) > 0;
}
}
Book.h
#include <string>
#include <vector>
#include <map>
namespace Samples
{
template <typename EntryType>
class Book
{
public:
typedef std::map<ClOrdID, EntryType> BookEntries;
typedef typename BookEntries::iterator EntryIter;
Book& store(const EntryType& entry);
Book& store(
const EntryType& entry,
ClOrdID id);
typedef std::vector<EntryType*> EntryList;
void getEntries(EntryList & list);
private:
typedef Threading::Mutex Mutex;
typedef Threading::Guard<Threading::Mutex> Guard;
Mutex mutex_;
BookEntries entries_;
};
template <typename EntryType>
Book<EntryType>& Book<EntryType>::store(const EntryType& newEntry)
{
return store(newEntry, newEntry.id());
}
template <typename EntryType>
Book<EntryType>& Book<EntryType>::store(
const EntryType& newEntry,
ClOrdID id)
{
const Guard guard(mutex_);
const std::pair<EntryIter, bool> result = entries_.insert(std::make_pair(id, newEntry));
if (!result.second)
{
std::stringstream ss;
ss << "Book already has an entry registered under ID = '" << id << "'.";
throw std::domain_error(ss.str());
}
return *this;
}
template <typename EntryType>
EntryType* Book<EntryType>::find(
ClOrdID id)
{
const Guard guard(mutex_);
const EntryIter entry = entries_.find(id);
}
template <typename EntryType>
void Book<EntryType>::getEntries(Book<EntryType>::EntryList & list)
{
const Guard guard(mutex_);
for (EntryIter iter = entries_.begin(); iter != entries_.end(); ++iter)
list.push_back(&iter->second);
}
}
Order.h
#include <string>
namespace Samples
{
struct Order
{
Order();
std::string toString() const;
Order& price(UInt64 value);
Order& side(Side::Enum value);
Order& ordType(OrdType::Enum value);
Order& stopPx(Int64 value);
Order& timeInForce(TimeInForce::Enum value);
Order& orderStatus(OrdStatus::Enum value);
Order& lastPx(Int64 value);
Order& transactTime(UInt64 value);
Int64 price_;
Side::Enum side_;
OrdType::Enum ordType_;
Int64 stopPx_;
TimeInForce::Enum timeInForce_;
OrdStatus::Enum orderStatus_;
Int64 lastPx_;
UInt64 transactTime_;
};
}
Order.cpp
#include "Order.h"
#include "../Common/Helpers.h"
namespace Samples
{
Order::Order()
: cIOrdId_(IdGenerator::newId())
, orderId_()
, price_(NullPriceOptional::NullMantissa().value())
, orderQty_()
, securityId_()
, side_(Side::Buy)
, ordType_(OrdType::Limit)
, stopPx_(NullPriceOptional::NullMantissa().value())
, timeInForce_(TimeInForce::Day)
, orderStatus_(OrdStatus::New)
, lastPx_()
, transactTime_()
{
}
template<typename T>
std::string toStrOptional(T value, T emptyValue)
{
return value == emptyValue ? std::string() :
toStr(value);
}
std::string Order::toString() const
{
std::ostringstream builder;
builder
<< "ClOrdID = " << cIOrdId_
<<
", OrderID = " <<
toStr(orderId_)
<<
", SecurityId = " <<
toStr(securityId_)
<<
", OrderStatus = " <<
toStr(orderStatus_)
<<
", Type = " <<
toStr(ordType_)
<<
", Price = " <<
toStr(price_)
<<
", Quantity = " <<
toStr(orderQty_)
<<
", Side = " <<
toStr(side_)
<<
", LastFillPrice = " <<
toStr(lastPx_)
<<
", TimeInForce = " <<
toStr(timeInForce_)
<< ", TransactTime = " << Timestamp(transactTime_).toString();
return builder.str();
}
{
return cIOrdId_;
}
Order& Order::cIOrdId(
ClOrdID value)
{
cIOrdId_ = value;
return *this;
}
Order& Order::orderId(
OrderID value)
{
orderId_ = value;
return *this;
}
Order& Order::price(UInt64 value)
{
price_ = value;
return *this;
}
{
orderQty_ = value;
return *this;
}
{
minQty_ = value;
return *this;
}
{
leavesQty_ = value;
return *this;
}
{
cumQty_ = value;
return *this;
}
{
securityId_ = value;
return *this;
}
{
side_ = value;
return *this;
}
{
ordType_ = value;
return *this;
}
Order& Order::stopPx(Int64 value)
{
stopPx_ = value;
return *this;
}
{
timeInForce_ = value;
return *this;
}
{
expireDate_ = value;
return *this;
}
{
orderStatus_ = value;
return *this;
}
Order& Order::lastPx(Int64 value)
{
lastPx_ = value;
return *this;
}
Order& Order::transactTime(UInt64 value)
{
transactTime_ = value;
return *this;
}
}
MessageFactory.h
#include "../Common/Helpers.h"
#include "Order.h"
namespace Samples {
class MessageFactory
{
public:
NewOrderSingle & getNewOrder(const Order& order) const;
OrderCancelReplaceRequest & getModifyRequest(const Order& order) const;
OrderCancelRequest & getCancelRequest(const Order& order) const;
NewOrderCross & getNewOrderCross(const Order& buySideOrder, const Order& sellSideOrder) const;
OrderMassActionRequest & getOrderMassActionRequest() const;
SecurityDefinitionRequest & getSecurityDefinitionRequest() const;
private:
NewOrderSingle initNewOrder() const;
OrderCancelReplaceRequest initOrderCancelReplaceRequest() const;
OrderCancelRequest initOrderCancelRequest() const;
NewOrderCross initNewOrderCross() const;
OrderMassActionRequest initOrderMassActionRequest() const;
SecurityDefinitionRequest initSecurityDefinitionRequest() const;
InvestorID iid_;
};
}
MessageFactory.cpp
#include "../Settings/Defaults.h"
#include "MessageFactory.h"
namespace Samples {
const char* Location = "DMA";
const char* EnteringTrader = "TADA";
const char* DeskId = "1";
template<typename MessageType>
void setMarketSegmentId(MessageType & msg,
MarketSegmentID marketSegmentId)
{
msg->businessHeader().setMarketSegmentId(marketSegmentId);
}
{
iid_.setPrefix(300);
iid_.setDocument(123456);
marketSegmentId_ = marketSegmentId;
}
NewOrderSingle MessageFactory::initNewOrder() const
{
NewOrderSingle msg;
setMarketSegmentId(msg, marketSegmentId_);
msg->setOrdTagId(OrderTag)
.setMmProtectionReset(Boolean::FalseValue)
.setSenderLocation(Location)
.setEnteringTrader(EnteringTrader)
.setSelfTradePreventionInstruction(SelfTradePreventionInstruction::None)
.setMemo("ONIXS 123456789")
.setDeskId(DeskId)
.setAccount(Account)
.setInvestorId(iid_);
return msg;
}
NewOrderSingle & MessageFactory::getNewOrder(const Order& order) const
{
static NewOrderSingle msg = initNewOrder();
.setOrderQty(order.orderQty_)
.setSecurityId(order.securityId_)
.setClOrdId(order.cIOrdId_)
.setMinQty(order.minQty_)
.setOrdType(order.ordType_)
.setTimeInForce(order.timeInForce_)
.setSide(order.side_);
if (order.timeInForce_ == TimeInForce::GoodTillDate)
return msg;
}
OrderCancelReplaceRequest MessageFactory::initOrderCancelReplaceRequest() const
{
OrderCancelReplaceRequest msg;
setMarketSegmentId(msg, marketSegmentId_);
msg->setOrdTagId(OrderTag)
.setMmProtectionReset(Boolean::FalseValue)
.setSenderLocation(Location)
.setEnteringTrader(EnteringTrader)
.setSelfTradePreventionInstruction(SelfTradePreventionInstruction::None)
.setMemo("ONIXS 123456789")
.setInvestorId(iid_)
.setAccount(Account)
.setDeskId(DeskId);
return msg;
}
OrderCancelReplaceRequest & MessageFactory::getModifyRequest(const Order& order) const
{
static OrderCancelReplaceRequest msg = initOrderCancelReplaceRequest();
.setOrderQty(order.orderQty_)
.setSecurityId(order.securityId_)
.setClOrdId(order.cIOrdId_)
.setOrderId(order.orderId_)
.setMinQty(order.minQty_)
.setOrdType(order.ordType_)
.setTimeInForce(order.timeInForce_)
.setSide(order.side_);
if (order.timeInForce_ == TimeInForce::GoodTillDate)
return msg;
}
OrderCancelRequest MessageFactory::initOrderCancelRequest() const
{
OrderCancelRequest msg;
setMarketSegmentId(msg, marketSegmentId_);
msg->
setSenderLocation(Location)
.setEnteringTrader(EnteringTrader)
.setMemo("ONIXS 123456789")
.setDeskId(DeskId);
return msg;
}
OrderCancelRequest & MessageFactory::getCancelRequest(const Order& order) const
{
static OrderCancelRequest msg = initOrderCancelRequest();
msg->setClOrdId(order.cIOrdId_)
.setOrderId(order.orderId_)
.setSecurityId(order.securityId_)
.setSide(order.side_)
;
return msg;
}
NewOrderCross MessageFactory::initNewOrderCross() const
{
NewOrderCross msg;
setMarketSegmentId(msg, marketSegmentId_);
msg->
setSenderLocation(Location)
.setEnteringTrader(EnteringTrader)
.setMemo("ONIXS 123456789")
.setCrossPrioritization(CrossPrioritization::None)
.setDeskId(DeskId)
;
return msg;
}
NewOrderCross & MessageFactory::getNewOrderCross(const Order& buySideOrder, const Order& sellSideOrder) const
{
static NewOrderCross msg = initNewOrderCross();
.setSecurityId(buySideOrder.securityId_)
.setOrderQty(buySideOrder.orderQty_)
.setCrossId(IdGenerator::newId())
;
NewOrderCross106::Sides sides = msg->sides(2);
NewOrderCross106::SidesEntry buyEntry = sides[0];
NewOrderCross106::SidesEntry sellEntry = sides[1];
buyEntry.setClOrdId(buySideOrder.cIOrdId_)
.setSide(buySideOrder.side_)
.setAccount(Account);
sellEntry.setClOrdId(sellSideOrder.cIOrdId_)
.setSide(sellSideOrder.side_)
.setAccount(Account);
return msg;
}
OrderMassActionRequest MessageFactory::initOrderMassActionRequest() const
{
OrderMassActionRequest msg;
setMarketSegmentId(msg, marketSegmentId_);
msg->setOrdTagId(OrderTag)
.setMassActionScope(MassActionScope::AllOrdersForATradingSession)
.setMassActionType(MassActionType::CancelOrders)
.setInvestorId(iid_);
return msg;
}
OrderMassActionRequest & MessageFactory::getOrderMassActionRequest() const
{
static OrderMassActionRequest msg = initOrderMassActionRequest();
msg->setClOrdId(IdGenerator::newId());
return msg;
}
SecurityDefinitionRequest MessageFactory::initSecurityDefinitionRequest() const
{
SecurityDefinitionRequest msg;
setMarketSegmentId(msg, marketSegmentId_);
msg->
setSenderLocation(Location)
.setEnteringTrader(EnteringTrader);
return msg;
}
SecurityDefinitionRequest & MessageFactory::getSecurityDefinitionRequest() const
{
static SecurityDefinitionRequest msg = initSecurityDefinitionRequest();
msg->setSecurityReqId(IdGenerator::newId());
SecurityDefinitionRequest300::Legs legs = msg->legs(1);
legs[0]
.setLegSymbol("GOAU3")
return msg;
}
}
TradingClient.h
#include "MessageFactory.h"
#include "Settings.h"
#include "CUI.h"
#include "Order.h"
#include "Book.h"
namespace Samples
{
{
public:
explicit TradingClient(const Settings& settings);
void run();
private:
const Settings& settings_;
PtrTraits<Session>::UniquePtr session_;
bool connected_;
Book<Order> orderBook_;
typedef Book<Order>::EntryList OrderList;
MessageFactory messageFactory_;
typedef CUI::Menu<TradingClient> Menu;
Menu menu_;
void constructMenu();
void createSessions();
void processSendSequence(CUI::CommandExecutionStatus*);
void processExit(CUI::CommandExecutionStatus*);
void processOrderCancelRequest(CUI::CommandExecutionStatus*);
void processOrderMassActionRequest(CUI::CommandExecutionStatus*);
void processOrderModifyRequest(CUI::CommandExecutionStatus*);
void processSendNewOrder(CUI::CommandExecutionStatus*);
void processViewOrders(CUI::CommandExecutionStatus*);
void processViewSessions(CUI::CommandExecutionStatus*);
void processSecurityDefinitionRequest(CUI::CommandExecutionStatus*);
void processCrossOrder(CUI::CommandExecutionStatus*);
void resetSequenceNumbers(CUI::CommandExecutionStatus*);
void processEstablishConnection(CUI::CommandExecutionStatus*);
void processDisconnect(CUI::CommandExecutionStatus*);
bool checkSessionConnected();
template <typename SbeMessageType, size_t MaxMessageSize, typename MessageInitializer>
void send(Messaging::MessageHolder<SbeMessageType, MaxMessageSize, MessageInitializer> &msg);
Order* findByOrderId(
ClOrdID clOrdId);
Order newOrder();
void onMessageSending(char* bytes, size_t size, Session*) ONIXS_B3_BOE_OVERRIDE;
void onNegotiateResponse(const NegotiateResponse2 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onNegotiateReject(const NegotiateReject3 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onEstablishAck(const EstablishAck5 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onEstablishReject(const EstablishReject6 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onSequence(const Sequence9 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onTerminate(const Terminate7 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onRetransmitReject(const Messaging::RetransmitReject14 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onRetransmission(const Messaging::Retransmission13 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onNotApplied(const Messaging::NotApplied8 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onBusinessMessageReject(const BusinessMessageReject206 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onExecutionReportNew(const ExecutionReportNew200 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onExecutionReportModify(const ExecutionReportModify201 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onExecutionReportCancel(const ExecutionReportCancel202 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onExecutionReportTrade(const ExecutionReportTrade203 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onExecutionReportReject(const ExecutionReportReject204 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onSecurityDefinitionResponse(const SecurityDefinitionResponse301 & msg, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onOrderMassActionReport(const OrderMassActionReport702& msg, Session* sn) ONIXS_B3_BOE_OVERRIDE;
void onStateChange(SessionStateId::Enum newState, SessionStateId::Enum prevState, Session * sn) ONIXS_B3_BOE_OVERRIDE;
void onError(SessionErrorReason::Enum reason, const
std::
string & description, Session * sn, Messaging::SbeMessage) ONIXS_B3_BOE_OVERRIDE;
void onWarning(SessionWarningReason::Enum reason, const
std::
string & description, Session * sn, Messaging::SbeMessage) ONIXS_B3_BOE_OVERRIDE;
};
}
TradingClient.cpp
#include "../Settings/Defaults.h"
#include "TradingClient.h"
namespace Samples
{
Session* createSession(
const SessionSettings& settings, SessionListener* listener,
UInt64 sessionVerId = Session::UndefinedSessionVerID, const std::string& customKey = "")
{
return new Session(settings, listener, SessionStorageType::FileBased,
ONIXS_B3_BOE_NULLPTR, sessionVerId, customKey);
}
template <typename MsgType>
bool isRetransmitted(const MsgType& msg)
{
return msg.businessHeader().possResend() == PossResend::TrueValue;
}
template <typename MsgType>
void setCommonFields(Order * order, const MsgType& msg)
{
order->
orderQty(msg.orderQty())
.securityId(msg.securityId())
.transactTime(msg.transactTime().time());
}
template <typename MsgType>
void setExpireDate(Order * order, const MsgType& msg)
{
Timestamp expireDate;
if(msg.expireDate(expireDate))
}
template <typename MsgType>
void setOrdStatus(OrdStatus::Enum & ordStatus, const MsgType& msg)
{
ordStatus = msg.ordStatus();
}
using namespace CUI;
typedef ListViewer<Order> OrdersViewer;
template <typename SbeMessageType, size_t MaxMessageSize, typename MessageInitializer>
void TradingClient::send(Messaging::MessageHolder<SbeMessageType, MaxMessageSize, MessageInitializer> &msg)
{
if (!checkSessionConnected())
return;
session_->send(msg);
}
Order* TradingClient::findByOrderId(
ClOrdID clOrdId)
{
Order* const msg = orderBook_.find(clOrdId);
if (!msg)
{
std::stringstream ss;
ss
<< "Cannot process received Execution Report. No order was sent with ClOrdID (tag = 11) equal to '"
<< clOrdId
<< " in the current session'";
throw std::domain_error(ss.str());
}
return msg;
}
TradingClient::TradingClient(const Settings& settings)
: settings_(settings)
, connected_(false)
, messageFactory_(atoi(settings.get("MarketSegment").c_str()))
, menu_(this)
{
createSessions();
constructMenu();
}
void TradingClient::run()
{
menu_.processRequests();
}
void TradingClient::constructMenu()
{
menu_
.add("Connect Sessions.", "Sending Negotiation/Establishment.. ", &TradingClient::processEstablishConnection)
.add("Review the status of sessions. ", "Viewing the status of sessions.. ", &TradingClient::processViewSessions)
.add("Send Sequence. ", "Sending Sequence.. ", &TradingClient::processSendSequence)
.add("Review sent orders and their status. ", "Viewing sent orders and their status.. ", &TradingClient::processViewOrders)
.add("Create and send a New Order. ", "Creating new order.. ", &TradingClient::processSendNewOrder)
.add("Submit Order Modify Request. ", "Order Modify Request.. ", &TradingClient::processOrderModifyRequest)
.add("Submit Order Cancel Request. ", "Order Cancel Request.. ", &TradingClient::processOrderCancelRequest)
.add("Submit Order Mass Action Request. ", "Order Mass Action Request.. ", &TradingClient::processOrderMassActionRequest)
.add("Submit SecurityDefinitionRequest. ", "Creating SecurityDefinition.. ", &TradingClient::processSecurityDefinitionRequest)
.add("Submit New Order Cross. ", "Creating Cross Order.. ", &TradingClient::processCrossOrder)
.add("Disconnect all sessions", "Closing connections...", &TradingClient::processDisconnect)
.add("Reset Sequence Numbers", "Reset Sequence Numbers", &TradingClient::resetSequenceNumbers)
.add("Close the connection and exit. ", "Exiting application.. ", &TradingClient::processExit);
}
bool TradingClient::checkSessionConnected()
{
if (!connected_)
Screen::info("Session is not connected. Please establish the connection first.");
return connected_;
}
void TradingClient::createSessions()
{
Screen::info("Creating the primary BOE Session..");
SessionSettings settings;
settings
.licenseStore("../../license")
.sessionId(settings_.getAsInt("SessionId"))
.accessKey(settings_.get("AccessKey"))
.enteringFirm(settings_.getAsInt("FirmId"))
.tradingSystemName(settings_.get("TradingSystemName"))
.tradingSystemVersion(settings_.get("TradingSystemVersion"))
.keepAliveInterval(settings_.getAsInt("KeepAliveInterval"));
session_.reset(createSession(settings, this));
const int incomingSequenceNumber = settings_.getAsInt("InSeqNum");
if (incomingSequenceNumber > 0)
session_->inSeqNum(incomingSequenceNumber);
const int outgoingSequenceNumber = settings_.getAsInt("OutSeqNum");
if (outgoingSequenceNumber > 0)
session_->outSeqNum(outgoingSequenceNumber);
}
void TradingClient::processEstablishConnection(CommandExecutionStatus* status)
{
if (!connected_)
{
try
{
session_->connect(settings_.get("Host"), settings_.getAsInt("Port"));
}
catch (const std::exception& ex)
{
Screen::info("Cannot connect to the host: ", ex.what());
}
Screen::info("Done");
}
else
Screen::info("Connection is already established.");
*status = ContinueExecution;
}
void TradingClient::processDisconnect(CommandExecutionStatus*)
{
{
Screen::info("Disconnecting from primary counterparty.. ");
session_->disconnect();
}
Screen::info("Done");
}
void TradingClient::processSendSequence(CommandExecutionStatus*)
{
if (!checkSessionConnected())
return;
session_->sendSequenceMessage();
Screen::info("Done");
}
void TradingClient::processViewOrders(CommandExecutionStatus*)
{
OrderList orderList;
orderBook_.getEntries(orderList);
OrdersViewer::outputItems(orderList, 10);
}
void TradingClient::processViewSessions(CommandExecutionStatus*)
{
Screen::info("Primary BOE session: ",
".");
}
void TradingClient::processOrderMassActionRequest(CommandExecutionStatus*)
{
if (!checkSessionConnected())
return;
Screen::info("Sending Order Mass Action Request to counterparty.. ");
OrderMassActionRequest & msg = messageFactory_.getOrderMassActionRequest();
send(msg);
Screen::info("Done");
}
void TradingClient::processSecurityDefinitionRequest(CommandExecutionStatus*)
{
if (!checkSessionConnected())
return;
Screen::info("Sending Security Definition Request to counterparty.. ");
SecurityDefinitionRequest & msg = messageFactory_.getSecurityDefinitionRequest();
send(msg);
Screen::info("Done");
}
void TradingClient::processSendNewOrder(CommandExecutionStatus*)
{
if (!checkSessionConnected())
return;
Screen::info("Sending new order to counterparty.. ");
Order order = newOrder();
NewOrderSingle & msg = messageFactory_.getNewOrder(order);
send(msg);
orderBook_.store(order);
Screen::info("Done");
}
void TradingClient::processOrderCancelRequest(CommandExecutionStatus*)
{
if (!checkSessionConnected())
return;
OrderList ordersInBook;
orderBook_.getEntries(ordersInBook);
if (ordersInBook.empty())
{
Screen::info("No orders found");
return;
}
OrdersViewer orderViewer;
orderViewer.outputItems(ordersInBook, 10);
Screen::out("Enter the index of the order to be canceled ");
const int orderIndex = atoi(Screen::getInput().c_str());
const Order * order = ordersInBook.at(orderIndex - 1);
OrderCancelRequest & msg = messageFactory_.getCancelRequest(*order);
send(msg);
Screen::info("Done");
}
void TradingClient::processOrderModifyRequest(CommandExecutionStatus*)
{
if (!checkSessionConnected())
return;
OrderList ordersInBook;
orderBook_.getEntries(ordersInBook);
if (ordersInBook.empty())
{
Screen::info("No orders found");
return;
}
OrdersViewer orderViewer;
orderViewer.outputItems(ordersInBook, 10);
Screen::out("Enter the index of the order to be modified ");
const int orderIndex = atoi(Screen::getInput().c_str());
Order* const order = ordersInBook.at(orderIndex - 1);
Screen::info("Enter new OrderQty (or Enter to skip): ");
Screen::getInput(order->orderQty_, order->orderQty_);
Screen::info("Enter new Price mantissa (or Enter to skip): ");
Screen::getInput(order->price_, order->price_);
OrderCancelReplaceRequest & msg = messageFactory_.getModifyRequest(*order);
send(msg);
Screen::info("Done");
}
void TradingClient::processCrossOrder(CommandExecutionStatus*)
{
if (!checkSessionConnected())
return;
Order buySideOrder;
Screen::info(
"Enter Price mantissa (default = " +
toStr(DefaultPriceMantissa) +
"): ");
Screen::getInput(buySideOrder.price_, DefaultPriceMantissa);
Screen::info(
"Enter SecurityId (default=" +
toStr(DefaultSecurityId) +
"): ");
Screen::getInput(buySideOrder.securityId_, DefaultSecurityId);
Screen::info("Enter Quantity (default = 100): ");
Screen::getInput<QuantityOptional>(buySideOrder.orderQty_, 100ul);
Order sellSideOrder;
sellSideOrder
.price(buySideOrder.price_)
.securityId(buySideOrder.securityId_)
.orderQty(buySideOrder.orderQty_)
NewOrderCross & msg = messageFactory_.getNewOrderCross(buySideOrder, sellSideOrder);
send(msg);
orderBook_
.store(buySideOrder)
.store(sellSideOrder);
Screen::info("Done");
}
void TradingClient::processExit(CommandExecutionStatus* status)
{
*status = TerminateExecution;
}
void TradingClient::resetSequenceNumbers(CommandExecutionStatus*)
{
{
if(session_->negotiated())
session_->reset();
}
else
Screen::info("Operation can be performed only when the sessions are disconnected");
Screen::info("Done");
}
Order TradingClient::newOrder()
{
Order order;
Screen::info(
"Enter SecurityId (default=" +
toStr(DefaultSecurityId) +
"): ");
Screen::getInput(order.securityId_, DefaultSecurityId);
Screen::info(
"Enter OrderType(40) (1 - Market order, 2 - Limit order (default), 3 - Stop order, 4 - Stop - Limit order, K - Market - Limit order): ");
{
Screen::info(
"Enter Price mantissa (default = " +
toStr(DefaultPriceMantissa) +
"): ");
Screen::getInput(order.price_, DefaultPriceMantissa);
}
{
Screen::info("Enter StopPx: ");
Screen::getInput(order.stopPx_);
}
Screen::info("Enter OrderQuantity (default = 100): ");
Screen::getInput<QuantityOptional>(order.orderQty_, 100ul);
Screen::info("Enter MinQty or skip");
Screen::info(
"Enter TimeInForce (0 - Day (default), 1 - Good Till Cancel, 3 - Immediate Or Cancel, 6 - Good Till Date): ");
{
Screen::info("Enter Expire date YYYYMMDD: ");
Screen::getInputLocalMktDate(order.expireDate_);
}
Screen::info("Enter Side (1 - Buy (default), 2 - Sell): ");
Screen::getInputChar(order.side_,
Side::Buy);
return order;
}
void TradingClient::onMessageSending(char* bytes, size_t size, Session*)
{
Screen::out("Outbound message: ", SbeMessage(bytes, static_cast<Messaging::MessageSize>(size)));
}
void TradingClient::onBusinessMessageReject(const BusinessMessageReject206 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onNegotiateResponse(const NegotiateResponse2 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onNegotiateReject(const NegotiateReject3 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onEstablishAck(const EstablishAck5 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onEstablishReject(const EstablishReject6 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onSequence(const Sequence9 &, Session *)
{
}
void TradingClient::onTerminate(const Terminate7 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onRetransmitReject(const Messaging::RetransmitReject14 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onRetransmission(const Messaging::Retransmission13 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onNotApplied(const Messaging::NotApplied8 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onExecutionReportNew(const ExecutionReportNew200 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
if(isRetransmitted(msg))
return;
Order * const order = findByOrderId(msg.clOrdId());
assert(order);
setCommonFields(order, msg);
setExpireDate(order, msg);
setOrdStatus(order->orderStatus_, msg);
order->orderId_ = msg.clOrdId();
order->timeInForce_ = msg.timeInForce();
order->orderId_ = msg.orderId();
Screen::info("Order changed: " + order->toString());
}
void TradingClient::onExecutionReportModify(const ExecutionReportModify201 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
if(isRetransmitted(msg))
return;
Order * const order = findByOrderId(msg.clOrdId());
assert(order);
setCommonFields(order, msg);
setExpireDate(order, msg);
order->orderId_ = msg.clOrdId();
order->timeInForce_ = msg.timeInForce();
order->orderId_ = msg.orderId();
setOrdStatus(order->orderStatus_, msg);
order->leavesQty(msg.leavesQty())
.cumQty(msg.cumQty());
Screen::info("Order changed: " + order->toString());
}
void TradingClient::onExecutionReportCancel(const ExecutionReportCancel202 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
if(isRetransmitted(msg))
return;
Order * const order = findByOrderId(msg.clOrdId());
assert(order);
setCommonFields(order, msg);
setExpireDate(order, msg);
order->orderId_ = msg.clOrdId();
order->timeInForce_ = msg.timeInForce();
order->orderId_ = msg.orderId();
setOrdStatus(order->orderStatus_, msg);
order->leavesQty(msg.cumQty());
Screen::info("Order changed: " + order->toString());
}
void TradingClient::onExecutionReportReject(const ExecutionReportReject204 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
if(isRetransmitted(msg))
return;
Order * const order = findByOrderId(msg.clOrdId());
assert(order);
setCommonFields(order, msg);
setExpireDate(order, msg);
order->timeInForce_ = msg.timeInForce();
setOrdStatus(order->orderStatus_, msg);
if(msg.orderId(ordId))
order->orderId_ = ordId;
Screen::info("Order changed: " + order->toString());
}
void TradingClient::onExecutionReportTrade(const ExecutionReportTrade203 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
if(isRetransmitted(msg))
return;
if(!msg.clOrdId(clOrdId))
return;
Order * const order = findByOrderId(clOrdId);
assert(order);
setCommonFields(order, msg);
order->orderId_ = msg.orderId();
order->orderStatus(msg.ordStatus())
.orderId(clOrdId)
.leavesQty(msg.leavesQty())
.cumQty(msg.cumQty())
.lastPx(msg.lastPx().mantissa());
Screen::info("Order changed: " + order->toString());
}
void TradingClient::onSecurityDefinitionResponse(const SecurityDefinitionResponse301 & msg, Session *)
{
Screen::info("Message received from counterparty: ", msg.toString());
}
void TradingClient::onOrderMassActionReport(const OrderMassActionReport702& msg, Session*)
{
Screen::info("Received Order Mass Action Report: ", msg.toString());
Screen::info(
"MassActionResponse=",
toStr(msg.massActionResponse()));
if (msg.securityId(securityId))
Screen::info(
"MarketSegmentID=",
toStr(securityId));
}
{
}
void TradingClient::onError(
SessionErrorReason::Enum,
const std::string & description, Session *, Messaging::SbeMessage)
{
Screen::info("Session-level error: ", description);
}
{
Screen::info("Session-level warning: ", description);
}
}
Main.cpp
#include "Settings.h"
#include "TradingClient.h"
using namespace Samples;
namespace
{
void ensureSettingsAreUpToDate(const Settings& appSettings)
{
const std::string upToDateParameter("settingsAreUpToDate");
if (!appSettings.getAsBool(upToDateParameter))
{
std::string errReason("Please update the configuration file (settings.ini) with values received from B3 Support Team and set the '");
errReason += upToDateParameter;
errReason += "' configuration parameter to 'true'";
throw std::domain_error(errReason);
}
}
}
int main(int argc, char * argv[])
{
try
{
std::cout << "B3 BOE Trading Client sample." << std::endl
<< "Usage: " << argv[0] << " {ConfigurationFileName}" << std::endl;
std::string configurationFileName = (argc > 1) ? argv[1] : "settings.ini";
Settings settings(configurationFileName);
ensureSettingsAreUpToDate(settings);
TradingClient client(settings);
client.run();
}
catch (const std::exception& ex)
{
std::cerr << std::endl << "Error: " << ex.what() << "." << std::endl;
return 1;
}
return 0;
}