OnixS C++ Eurex T7 Market and Reference Data Interface (EMDI, RDI, EOBI) Handlers  8.1.0
API documentation
EditableOrderBook.cpp
Go to the documentation of this file.
1 /*
2 * Copyright 2002-2010 ONIX SOLUTIONS LIMITED. All rights reserved.
3 *
4 * This software owned by ONIX SOLUTIONS LIMITED [ONIXS] and is protected by copyright law
5 * and international copyright treaties.
6 *
7 * Access to and use of the software is governed by the terms of the applicable ONIXS Software
8 * Services Agreement (the Agreement) and Customer end user license agreements granting
9 * a non-assignable, non-transferable and non-exclusive license to use the software
10 * for it's own data processing purposes under the terms defined in the Agreement.
11 *
12 * Except as otherwise granted within the terms of the Agreement, copying or reproduction of any part
13 * of this source code or associated reference material to any other location for further reproduction
14 * or redistribution, and any amendments to this copyright notice, are expressly prohibited.
15 *
16 * Any reproduction or redistribution for sale or hiring of the Software not in accordance with
17 * the terms of the Agreement is a violation of copyright law.
18 */
19 #include <util/Converter.h>
20 
21 #include <OnixS/HandlerCore/ErrorBuilder.h>
23 
24 #include "EditableOrderBook.h"
25 #include "Formatting.h"
26 #include "BookSanityChecker.h"
27 
28 
29 namespace OnixS {
30 namespace Eurex {
31 namespace MarketData {
32 namespace Implementation{
33 
34 
35 using namespace HandlerCore::Common;
36 
37 EditableOrderBook::EditableOrderBook(SecurityId securityId, size_t bookDepth, LogFacility* parent, HandlerLogger* logger)
38  : OrderBook(securityId, bookDepth)
39  , Logging::LogFacility(i2str(securityId), parent, Logging::LOG_LEVEL_DEBUG)
40  , logger_(logger)
41  , bestAskAvailable_(false)
42  , bestBidAvailable_(false)
43  , entryTime_(0)
44  , lastMsgSeqNumProcessed_(0)
45 {
46  BOOST_ASSERT(bookDepth != 0);
47 
48  asks_.reserve(bookDepth + 1);
49  bids_.reserve(bookDepth + 1);
50 }
51 
53 {
54  return asks_;
55 }
56 
58 {
59  return bids_;
60 }
61 
62 bool EditableOrderBook::bestAsk(Decimal& price, Quantity& quantity) const
63 {
64  if(bestAskAvailable_)
65  {
66  price = bestAskPrice_;
67  quantity = bestAskQuantity_;
68  return true;
69  }
70  return false;
71 }
72 
73 bool EditableOrderBook::bestBid(Decimal& price, Quantity& quantity) const
74 {
75  if(bestBidAvailable_)
76  {
77  price = bestBidPrice_;
78  quantity = bestBidQuantity_;
79  return true;
80  }
81  return false;
82 }
83 
85 {
86  return entryTime_;
87 }
88 
90 {
91  return lastMsgSeqNumProcessed_;
92 }
93 
95 {
96  lastMsgSeqNumProcessed_ = lastMsgSeqNumProcessed;
97 }
98 
100 {
101  Decimal price(0.0f, 0u);
102 
103  if(!entry.mdEntryPx(price))
104  log(ONIXS_LOG_DEBUG[this] << "No price in Bid/Offer snapshot entry, 0 used" );
105 
106  Decimal quantity = 0;
107  if(!entry.mdEntrySize(quantity))
108  log(ONIXS_LOG_DEBUG[this] << "No quantity in Bid/Offer snapshot entry, 0 used" );
109 
110  UInt32 level = 0;
111  if(entry.mdPriceLevel(level))
112  {
113  UInt32 numberOfOrders = 0;
114  if(!entry.numberOfOrders(numberOfOrders))
115  log(ONIXS_LOG_DEBUG[this] << "No numberOfOrders in Bid/Offer snapshot entry, 0 used" );
116 
117  PriceLevels* levels = nullptr;
118  if(entryType == MDEntryType::Bid)
119  levels = &bids_;
120  else if(entryType == MDEntryType::Offer)
121  levels = &asks_;
122  else
123  throw ArgumentException(BOOST_CURRENT_FUNCTION, "entryType", entryType);
124 
125  if (levels->size() < depth())
126  {
127  log(ONIXS_LOG_DEBUG[this] << "Applying snapshot entry: " << (entryType == MDEntryType::Bid ? "Bid" : "Offer") << ":" << level << ":" << price << ":" << quantity << ":" << numberOfOrders );
128  levels->push_back(PriceLevel(price, quantity, numberOfOrders));
129  }
130  else
131  {
132  ErrorBuilder errorBuilder;
133  errorBuilder << "Cannot add snapshot price with level " << level << ", total level count " << levels->size();
134  errorBuilder.toError();
135  }
136 
137  }
138  else
139  {
140  log(ONIXS_LOG_DEBUG[this] << "Applying snapshot entry: " << (entryType == MDEntryType::Bid ? "Bid" : "Offer") << ":" << "-" << ":" << price << ":" << quantity );
141 
142  if(entryType == MDEntryType::Bid)
143  {
144  bestBidAvailable_ = true;
145  bestBidPrice_ = price;
146  bestBidQuantity_ = quantity;
147  }
148  else if(entryType == MDEntryType::Offer)
149  {
150  bestAskAvailable_ = true;
151  bestAskPrice_ = price;
152  bestAskQuantity_ = quantity;
153  }
154  else
155  throw ArgumentException(BOOST_CURRENT_FUNCTION, "entryType", entryType);
156  }
157 
158  entry.mdEntryTime(entryTime_);
159 }
160 
162 {
163  Decimal price;
164  if(!entry.mdEntryPx(price))
165  log(ONIXS_LOG_DEBUG[this] << "No price in Bid/Offer incremental entry, 0 used" );
166 
167  UInt32 level;
168  if(entry.mdPriceLevel(level))
169  {
170  PriceLevels* levels = nullptr;
171  if(entryType == MDEntryType::Bid)
172  levels = &bids_;
173  else if(entryType == MDEntryType::Offer)
174  levels = &asks_;
175  else
176  throw ArgumentException(BOOST_CURRENT_FUNCTION, "entryType", entryType);
177 
178  BOOST_ASSERT(level > 0);
179 
180  --level;
181 
182  switch(entry.mdUpdateAction())
183  {
184  case MDUpdateAction::New:
185  {
186  Decimal quantity = 0;
187  if(!entry.mdEntrySize(quantity))
188  log(ONIXS_LOG_DEBUG[this] << "No quantity in Bid/Offer incremental entry, 0 used" );
189 
190  UInt32 numberOfOrders = 0;
191  if(!entry.numberOfOrders(numberOfOrders))
192  log(ONIXS_LOG_DEBUG[this] << "No numberOfOrders in Bid/Offer incremental entry, 0 used" );
193 
194  log(ONIXS_LOG_DEBUG[this]
195  << "Applying incremental entry: "
196  << (entryType == MDEntryType::Bid ? "Bid" : "Offer")
197  << ":New:"
198  << level + 1
199  << ":"
200  << price
201  << ":"
202  << quantity
203  << ":"
204  << numberOfOrders);
205 
206  if(level > levels->size())
207  {
208  ErrorBuilder errorBuilder;
209  errorBuilder << "Cannot add price with level " << level << ", total level count " << levels->size();
210  errorBuilder.toError();
211  }
212  else if(levels->size() == level)
213  levels->push_back(PriceLevel(price, quantity, numberOfOrders));
214  else
215  levels->insert(levels->begin() + level, PriceLevel(price, quantity, numberOfOrders));
216 
217  while( levels->size() > depth() )
218  levels->erase(levels->begin() + depth(), levels->end());
219  }
220  break;
222  {
223  Decimal quantity = 0;
224  if(!entry.mdEntrySize(quantity))
225  log(ONIXS_LOG_DEBUG[this] << "No quantity in Bid/Offer incremental entry, 0 used" );
226 
227  UInt32 numberOfOrders = 0;
228  if(!entry.numberOfOrders(numberOfOrders))
229  log(ONIXS_LOG_DEBUG[this] << "No numberOfOrders in Bid/Offer incremental entry, 0 used" );
230 
231  log(ONIXS_LOG_DEBUG[this] << "Applying incremental entry: " << (entryType == MDEntryType::Bid ? "Bid" : "Offer") << ":Change:" << level+1 << ":" << price << ":" << quantity << ":" << numberOfOrders );
232 
233  if(level >= levels->size())
234  {
235  ErrorBuilder errorBuilder;
236  errorBuilder << "Cannot change price with level " << level << ", total level count " << levels->size();
237  errorBuilder.toError();
238  }
239  else
240  levels->at(level) = PriceLevel(price, quantity, numberOfOrders);
241  }
242  break;
244  {
245  Decimal quantity = 0;
246  if(!entry.mdEntrySize(quantity))
247  log(ONIXS_LOG_DEBUG[this] << "No quantity in Bid/Offer incremental entry, 0 used" );
248 
249  UInt32 numberOfOrders = 0;
250  if(!entry.numberOfOrders(numberOfOrders))
251  log(ONIXS_LOG_DEBUG[this] << "No numberOfOrders in Bid/Offer incremental entry, 0 used" );
252 
253  log(ONIXS_LOG_DEBUG[this] << "Applying incremental entry: " << (entryType == MDEntryType::Bid ? "Bid" : "Offer") << ":Overlay:" << level+1 << ":" << price << ":" << quantity << ":" << numberOfOrders );
254 
255  if(level >= levels->size())
256  {
257  ErrorBuilder errorBuilder;
258  errorBuilder << "Cannot overlay price with level " << level << ", total level count " << levels->size();
259  errorBuilder.toError();
260  }
261  else
262  levels->at(level) = PriceLevel(price, quantity, numberOfOrders);
263  }
264  break;
266  {
267  log(ONIXS_LOG_DEBUG[this] << "Applying incremental entry: " << (entryType == MDEntryType::Bid ? "Bid" : "Offer") << ":Delete:" << level+1 << ":" << price);
268 
269  if(level >= levels->size())
270  {
271  ErrorBuilder errorBuilder;
272  errorBuilder << "Cannot delete price with level " << level+1 << ", total level count " << levels->size();
273  errorBuilder.toError();
274  }
275  else
276  levels->erase(levels->begin() + level);
277  }
278  break;
280  {
281  log(ONIXS_LOG_DEBUG[this] << "Applying incremental entry: " << (entryType == MDEntryType::Bid ? "Bid" : "Offer") << ":DeleteFrom:" << level+1 << ":" << price);
282 
283  if(level >= levels->size())
284  {
285  ErrorBuilder errorBuilder;
286  errorBuilder << "Cannot delete price levels from " << level+1 << ", total level count " << levels->size();
287  errorBuilder.toError();
288  }
289  else
290  levels->erase(levels->begin() + level, levels->end());
291  }
292  break;
294  {
295  log(ONIXS_LOG_DEBUG[this] << "Applying incremental entry: " << (entryType == MDEntryType::Bid ? "Bid" : "Offer") << ":DeleteThru:" << level+1 << ":" << price );
296 
297  if(level >= levels->size())
298  {
299  ErrorBuilder errorBuilder;
300  errorBuilder << "Cannot delete price up to level " << level+1 << ", total level count " << levels->size();
301  errorBuilder.toError();
302  }
303  else
304  levels->erase(levels->begin(), levels->begin() + level + 1);
305  }
306  break;
307 
309  default:
310  throw ArgumentException(BOOST_CURRENT_FUNCTION, "entryType", entryType);
311 
312  }
313  }
314  else
315  {
316  switch(entry.mdUpdateAction())
317  {
318  case MDUpdateAction::New:
321  {
322  Decimal quantity = 0;
323  if(!entry.mdEntrySize(quantity))
324  log(ONIXS_LOG_DEBUG[this] << "No quantity in Bid/Offer incremental entry, 0 used" );
325 
326  log(ONIXS_LOG_DEBUG[this] << "Applying incremental entry: " << (entryType == MDEntryType::Bid ? "Bid" : "Offer") << ":" << "-" << ":" << price << ":" << quantity );
327 
328  if(entryType == MDEntryType::Bid)
329  {
330  bestBidAvailable_ = true;
331  bestBidPrice_ = price;
332  bestBidQuantity_ = quantity;
333  }
334  else if(entryType == MDEntryType::Offer)
335  {
336  bestAskAvailable_ = true;
337  bestAskPrice_ = price;
338  bestAskQuantity_ = quantity;
339  }
340  else
341  throw ArgumentException(BOOST_CURRENT_FUNCTION, "entryType", entryType);
342  }
343  break;
345  {
346  log(ONIXS_LOG_DEBUG[this] << "Applying incremental entry: " << (entryType == MDEntryType::Bid ? "Bid" : "Offer") << ":Delete:" << "-" << ":" << price );
347 
348  if(entryType == MDEntryType::Bid)
349  bestBidAvailable_ = false;
350  else if(entryType == MDEntryType::Offer)
351  bestAskAvailable_ = false;
352  else
353  throw ArgumentException(BOOST_CURRENT_FUNCTION, "entryType", entryType);
354  }
355  break;
358  {
359  //log(ONIXS_LOG_WARN[this] << "Unexpected update action");
360  ErrorBuilder errorBuilder;
361  errorBuilder << "Unexpected update action:" << entry.mdUpdateAction();
362  errorBuilder.toError();
363  }
364  break;
365 
367  default:
368  throw ArgumentException(BOOST_CURRENT_FUNCTION, "entryType", entryType);
369  }
370  }
371 
372  entry.mdEntryTime(entryTime_);
373 }
374 
376 {
377  log(ONIXS_LOG_DEBUG[this] << "Applying empty book entry");
378 }
379 
381 {
382  bestAskAvailable_ = false;
383  bestBidAvailable_ = false;
384 
385  bids_.clear();
386  asks_.clear();
387 
388  entryTime_ = 0;
389  lastMsgSeqNumProcessed_ = 0;
390 
391  log(ONIXS_LOG_DEBUG[this] << "Cleared" );
392 }
393 
395 {
397  checker.checkSanity();
398 }
399 
400 }}}}
UInt32 lastMsgSeqNumProcessed() const
Sequence number of the last processed message.
bool mdEntryPx(Decimal &price) const
Price.
UInt64 mdEntryTime() const
For bids and offers the official time of book entry, for trades official time of execution (all in na...
bool bestBid(Decimal &price, Quantity &quantity) const
Returns best implied bid.
Encapsulates price level concept.
Definition: OrderBook.h:33
const PriceLevels & asks() const
Returns a set of descending bid prices for the given security.
bool mdPriceLevel(UInt32 &level) const
const PriceLevels & bids() const
Returns a set of ascending ask prices for the given security.
bool mdEntryPx(Decimal &price) const
Price.
bool mdEntryTime(UInt64 &time) const
Time of entry (nanoseconds) for last trade entry only (TradeCondition="U").
bool mdEntrySize(Decimal &size) const
Quantity.
unsigned int UInt32
Definition: Numeric.h:41
Definition: Defines.h:30
Decimal type for better precision.
Definition: Numeric.h:63
PriceLevelCollections::Array PriceLevels
Sequence of price levels.
Definition: OrderBook.h:135
Int64 SecurityId
Alias for Security Id type.
Definition: Defines.h:51
bool mdEntrySize(Decimal &size) const
Quantity.
bool numberOfOrders(UInt32 &number) const
Number Of Orders.
void apply(const MDSnapshotEntry &entry, MDEntryType::Enum entryType)
bool mdEntryTime(UInt64 &time) const
For bids and offers the official time of book entry, for trades official time of execution (all in na...
MDUpdateAction::Enum mdUpdateAction() const
Update action.
bool bestAsk(Decimal &price, Quantity &quantity) const
Returns best implied ask.
size_t depth() const
Returns the maximum book depth.
Definition: OrderBook.h:239
Used to identify absence of value.
Definition: Defines.h:630
UInt64 Quantity
Alias for Quantity type.
Definition: Defines.h:54
bool numberOfOrders(UInt32 &number) const
Number Of Orders.
Market data snapshot entry.
Definition: DepthSnapshot.h:51
EditableOrderBook(SecurityId securityId, size_t bookDepth, LogFacility *parent, HandlerCore::Common::HandlerLogger *logger)