in java/runner/src/intTest/java/com/epam/deltix/data/connectors/validator/L2DataValidator.java [451:595]
public void sendPackage(PackageHeaderInfo headerInfo) {
try {
if ((!headerInfo.hasSymbol()) || (!CharSequenceUtils.equals(headerInfo.getSymbol(), symbol))) {
sendMessageToLogger(headerInfo, TypeConstants.EXCHANGE_NULL, "Incorrect symbol", Severity.ERROR);
}
if (!headerInfo.hasEntries()) {
sendMessageToLogger(headerInfo, TypeConstants.EXCHANGE_NULL, "Missed entries list", Severity.ERROR);
}
if (!headerInfo.hasPackageType()) {
sendMessageToLogger(headerInfo, TypeConstants.EXCHANGE_NULL, "Missed package type", Severity.ERROR);
}
if (headerInfo.getEntries().size() == 0) {
sendMessageToLogger(headerInfo, TypeConstants.EXCHANGE_NULL, "Empty entries list", Severity.WARNING);
return;
}
checkTradePrice(headerInfo);
checkAllEntriesFields(headerInfo);
exchangeIds.clear();
previousBestAsks.clear();
previousBestBids.clear();
if (headerInfo.getPackageType() != PackageType.INCREMENTAL_UPDATE) {
processSnapshot(headerInfo);
long exchangeId = headerInfo.getEntries().get(0).getExchangeId();
exchangeIds.add(exchangeId);
OrderBookQuote askQuote = book.getMarketSide(QuoteSide.ASK).getQuote(0);
OrderBookQuote bidQuote = book.getMarketSide(QuoteSide.BID).getQuote(0);
if (askQuote != null) previousBestAsks.add(askQuote.getPrice()); else previousBestAsks.add(Decimal64Utils.NEGATIVE_INFINITY);
if (bidQuote != null) previousBestBids.add(bidQuote.getPrice()); else previousBestBids.add(Decimal64Utils.POSITIVE_INFINITY);
book.update(headerInfo);
} else {
for (int i = 0; i < headerInfo.getEntries().size(); ++i) {
incrementHeader.getEntries().add(headerInfo.getEntries().get(i));
if (!exchangeIds.contains(incrementHeader.getEntries().get(0).getExchangeId())) {
long exchangeId = incrementHeader.getEntries().get(0).getExchangeId();
exchangeIds.add(exchangeId);
OrderBookQuote askQuote = book.getMarketSide(QuoteSide.ASK).getQuote(0);
OrderBookQuote bidQuote = book.getMarketSide(QuoteSide.BID).getQuote(0);
if (askQuote != null) previousBestAsks.add(askQuote.getPrice()); else previousBestAsks.add(Decimal64Utils.NEGATIVE_INFINITY);
if (bidQuote != null) previousBestBids.add(bidQuote.getPrice()); else previousBestBids.add(Decimal64Utils.POSITIVE_INFINITY);
}
if (incrementHeader.getEntries().get(0) instanceof L2EntryUpdate) {
L2EntryUpdate entryUpdate = (L2EntryUpdate) incrementHeader.getEntries().get(0);
validateIncrementEntry(headerInfo, entryUpdate);
} else if (incrementHeader.getEntries().get(0) instanceof L2EntryNew) {
L2EntryNew entryNew = (L2EntryNew) incrementHeader.getEntries().get(0);
validateIncrementEntry(headerInfo, entryNew);
}
book.update(incrementHeader);
incrementHeader.getEntries().clear();
}
}
if (checkExchangeMisPrice) {
long minAsk = Decimal64Utils.POSITIVE_INFINITY;
long maxAsk = Decimal64Utils.NEGATIVE_INFINITY;
long minBid = Decimal64Utils.POSITIVE_INFINITY;
long maxBid = Decimal64Utils.NEGATIVE_INFINITY;
MarketSide<OrderBookQuote> askQuotes = book.getMarketSide(QuoteSide.ASK);
MarketSide<OrderBookQuote> bidQuotes = book.getMarketSide(QuoteSide.BID);
if (askQuotes.depth() > 0) {
minAsk = Decimal64Utils.min(askQuotes.getQuote(0).getPrice(), minAsk);
maxAsk = Decimal64Utils.max(askQuotes.getQuote(0).getPrice(), maxAsk);
}
if (bidQuotes.depth() > 0) {
minBid = Decimal64Utils.min(bidQuotes.getQuote(0).getPrice(), minBid);
maxBid = Decimal64Utils.max(bidQuotes.getQuote(0).getPrice(), maxBid);
}
if (!Decimal64Utils.isZero(minAsk) && Decimal64Utils.isGreater(Decimal64Utils.divide(Decimal64Utils.subtract(maxAsk, minAsk), Decimal64Utils.abs(minAsk)), exchangeMisPrice)) {
sendMessageToLogger(headerInfo, TypeConstants.EXCHANGE_NULL, internalLogBuilder.clear().append("Ask Mispricing between exchanges"), Severity.WARNING);
}
if (!Decimal64Utils.isZero(minBid) && Decimal64Utils.isGreater(Decimal64Utils.divide(Decimal64Utils.subtract(maxBid, minBid), Decimal64Utils.abs(minBid)), exchangeMisPrice)) {
sendMessageToLogger(headerInfo, TypeConstants.EXCHANGE_NULL, internalLogBuilder.clear().append("Bid Mispricing between exchanges"), Severity.WARNING);
}
}
if (checkLevelMisPrice) {
MarketSide<OrderBookQuote> askQuotes = book.getMarketSide(QuoteSide.ASK);
MarketSide<OrderBookQuote> bidQuotes = book.getMarketSide(QuoteSide.BID);
for (int level = 1; level < askQuotes.depth() ;level++) {
long previousPrice = askQuotes.getQuote(level - 1).getPrice();
long currentPrice = askQuotes.getQuote(level).getPrice();
if (Decimal64Utils.isZero(currentPrice)) continue;
long r = Decimal64Utils.divide(previousPrice, currentPrice);
if (Decimal64Utils.isLess(r, Decimal64Utils.subtract(Decimal64Utils.ONE, levelMisPrice))) {
sendMessageToLogger(headerInfo, askQuotes.getQuote(level).getExchangeId(), internalLogBuilder.clear().append("Ask Mispricing in the exchange ").append(AlphanumericUtils.toString(askQuotes.getQuote(level).getExchangeId())).append(" at level ").append(level), Severity.WARNING);
}
}
for (int level = 1; level < bidQuotes.depth() ;level++) {
long previousPrice = bidQuotes.getQuote(level - 1).getPrice();
long currentPrice = bidQuotes.getQuote(level).getPrice();
long r = Decimal64Utils.divide(currentPrice, previousPrice);
if (Decimal64Utils.isZero(previousPrice)) continue;
if (Decimal64Utils.isLess(r, Decimal64Utils.subtract(Decimal64Utils.ONE, levelMisPrice))) {
sendMessageToLogger(headerInfo, bidQuotes.getQuote(level).getExchangeId(), internalLogBuilder.clear().append("Bid Mispricing in the exchange ").append(AlphanumericUtils.toString(bidQuotes.getQuote(level).getExchangeId())).append(" at level ").append(level), Severity.WARNING);
}
}
}
for (int i = 0; i < exchangeIds.size(); ++i) {
MarketSide<OrderBookQuote> askQuotes = book.getMarketSide(QuoteSide.ASK);
MarketSide<OrderBookQuote> bidQuotes = book.getMarketSide(QuoteSide.BID);
OrderBookQuote bestAskQuote = askQuotes != null ? askQuotes.getQuote(0) : null;
OrderBookQuote bestBidQuote = bidQuotes != null ? bidQuotes.getQuote(0) : null;
if (bestAskQuote != null && bestBidQuote != null) {
if (Decimal64Utils.isLess(bestAskQuote.getPrice(), bestBidQuote.getPrice()) && checkBidMoreThanAsk) {
sendMessageToLogger(headerInfo, bestAskQuote.getExchangeId(), "Bid > Ask", Severity.WARNING);
}
if (checkPackageMisPrice) {
if (!Decimal64Utils.isInfinity(previousBestAsks.get(i)) && !Decimal64Utils.isZero(previousBestAsks.get(i))) {
if (Decimal64Utils.isGreater(Decimal64Utils.abs(Decimal64Utils.divide(Decimal64Utils.subtract(previousBestAsks.get(i), bestAskQuote.getPrice()), previousBestAsks.get(i))), packageMisPrice)) {
sendMessageToLogger(headerInfo, bestAskQuote.getExchangeId(), "Too big ask price change after this package", Severity.WARNING);
}
}
if (!Decimal64Utils.isInfinity(previousBestBids.get(i)) && !Decimal64Utils.isZero(previousBestBids.get(i))) {
if (Decimal64Utils.isGreater(Decimal64Utils.abs(Decimal64Utils.divide(Decimal64Utils.subtract(previousBestBids.get(i), bestBidQuote.getPrice()), previousBestBids.get(i))), packageMisPrice)) {
sendMessageToLogger(headerInfo, bestAskQuote.getExchangeId(), "Too big bid price change after this package", Severity.WARNING);
}
}
}
}
if (bestAskQuote == null && checkEmptySide) {
sendMessageToLogger(headerInfo, exchangeIds.get(i), "Ask side is empty", Severity.WARNING);
}
if (bestBidQuote == null && checkEmptySide) {
sendMessageToLogger(headerInfo, exchangeIds.get(i), "Bid side is empty", Severity.WARNING);
}
if (askQuotes.depth() < minValidNumberOfLevels) {
sendMessageToLogger(headerInfo, exchangeIds.get(i), "Ask side has less than minimal valid number of levels", Severity.WARNING);
}
if (bidQuotes.depth() < minValidNumberOfLevels) {
sendMessageToLogger(headerInfo, exchangeIds.get(i), "Bid side has less than minimal valid number of levels", Severity.WARNING);
}
}
} catch (Throwable e) {
logger.onLogEvent(this, Severity.ERROR, e, "Error in L2DataValidator.");
}
}