OnixS C++ FIX Engine  4.10.1
API Documentation
SBE B3 Sample

This sample provides a set of customized SBE codecs that can be used to handle BVMF entrypoint messages.


Source code:


SbeB3Codecs.h:

namespace OnixS {
namespace B3 {
using namespace OnixS::FIX;
using namespace OnixS::FIX::SBE;
#pragma pack(push, 1)
/// The composite represents message header from the B3 template.
/// All the structure fields are directly mapped to the SBE composite members.
struct MessageHeaderComposite
{
UInt16 blockLength;
UInt16 templateId;
UInt16 schemaId;
UInt16 version;
};
/// The composite represents framing header from the B3 template.
/// All the structure fields are directly mapped to the SBE composite members.
///
/// This header must be used manually.
struct FramingHeaderComposite
{
UInt16 messageLength;
UInt16 encodingType;
/// Setup framing header fields using length of the SBE message.
void setup(UInt16 messageOnlySize) {
messageLength = messageOnlySize + sizeof(FramingHeaderComposite);
encodingType = 0xEB50U; // Little-endian mark.
}
};
#pragma pack(pop)
/// Formats input data to hexadecimal format and adds trailing zero.
size_t toHexString(char* buffer, const size_t bufferSize, const void* data, const size_t dataSize);
/// Returns integer value corresponding to the given hexadecimal digit.
int hexToInt(char x);
/// Decodes hexadecimal string to the binary data.
size_t fromHexString(void* data, const size_t dataSize, const char* hex, const size_t hexLength);
/// Template for the trvial composite type decoder that takes binary data from wire level
/// and places hexadecimal string to appropriate field of the FIX message.
template<typename T>
class HexStringCompositeDecoder : public ISbeFieldDecoder
{
public:
typedef T DataType;
size_t decode(SbeFieldDecoderOutput output, unsigned /* version */, const void* data, size_t available) ONIXS_FIXENGINE_OVERRIDE
{
const size_t DataSize = sizeof(DataType);
if (available < DataSize)
throw std::domain_error("Not enough data to decode custom composite");
const DataType* typedData = static_cast<const DataType*>(data);
char content[DataSize*2 + 1];
size_t length = toHexString(content, sizeof(content), typedData, DataSize);
output.set(content, (SbeWireSize)length);
return DataSize;
}
};
/// Template for the trvial composite type encoder that accepts hexadecimal string
/// from the field of the FIX message, converts it to the appropriate binary data,
/// and puts the data to the wire level.
template<typename T>
class HexStringCompositeEncoder : public ISbeFieldEncoder
{
public:
typedef T DataType;
SbeWireSize queryFixedSize(unsigned /* version */) ONIXS_FIXENGINE_OVERRIDE
{
return (SbeWireSize)sizeof(DataType);
}
void encode(SbeBinaryOutputStream out, unsigned /* version */, const SbeConstFieldAccess& field) ONIXS_FIXENGINE_OVERRIDE
{
std::string s;
if (!field.toString(s))
throw std::domain_error("Unable to convert field value to string");
DataType data;
fromHexString(&data, sizeof(DataType), s.c_str(), s.length());
out.put(&data, sizeof(data));
}
};
/// The custom codecs library that must be used to handle B3 entrypoint templates.
class CodersLibrary : public ISbeCustomCoderLibrary
{
public:
void queryCodingControl(CoderOwnerId /* owner */, SbeFieldMetaData /* field */,
SbeFieldCodingSettings manageCoding) ONIXS_FIXENGINE_OVERRIDE {
manageCoding.useFormattedDateTime(false); // Suppress time formatting.
manageCoding.useValueNamesExternally(false); // Use enum raw values.
}
ISbeFieldDecoder* queryDecoder(CoderOwnerId owner, SbeFieldMetaData field) ONIXS_FIXENGINE_OVERRIDE;
ISbeFieldEncoder* queryEncoder(CoderOwnerId owner, SbeFieldMetaData field) ONIXS_FIXENGINE_OVERRIDE;
public:
// Customized coders for CustodianInfo
//<composite name="CustodianInfo" description="Custodian information is required for going private offer.">
// <type name="custodian" primitiveType="uint32" presence="optional" nullValue="0" description="Identifies the custodian."/>
// <type name="custodyAccount" primitiveType="uint32" presence="optional" nullValue="0" description="Identifies the custody account."/>
// <type name="custodyAllocationType" primitiveType="uint32" presence="optional" nullValue="0" description="Custody allocation type."/>
//</composite>
#pragma pack(push, 1)
struct CustodianInfoLayout
{
UInt32 custodian;
UInt32 custodyAccount;
UInt32 custodyAllocationType;
void fromString(const StringRef& ref) {
OnixS::B3::fromHexString(this, sizeof(*this), ref.data(), ref.length());
}
};
#pragma pack(pop)
private:
HexStringCompositeDecoder<CustodianInfoLayout> custodianInfoDecoder_;
HexStringCompositeEncoder<CustodianInfoLayout> custodianInfoEncoder_;
public:
// Customized coders for InboundBusinessHeader
//<composite name="InboundBusinessHeader" description="Header used for inbound business messages.">
// <ref name="sessionID" type="SessionID"/>
// <ref name="msgSeqNum" type="SeqNum"/>
// <ref name="sendingTime" type="SendingTime"/>
// <ref name="marketSegmentID" type="MarketSegmentID"/>
// <type name="padding" primitiveType="char" semanticType="String" characterEncoding="ASCII" length="1" description="Padding for alignment purpose."/>
//</composite>
#pragma pack(push, 1)
struct InboundBusinessHeader
{
UInt32 sessionID;
UInt32 msgSeqNum;
UInt32 sendingTime;
char marketSegmentID;
char padding;
void fromString(const StringRef& ref) {
OnixS::B3::fromHexString(this, sizeof(*this), ref.data(), ref.length());
}
};
#pragma pack(pop)
private:
HexStringCompositeDecoder<InboundBusinessHeader> inboundBusinessHeaderDecoder_;
HexStringCompositeEncoder<InboundBusinessHeader> inboundBusinessHeaderEncoder_;
public:
// Customized coders for OutboundBusinessHeader
//<composite name="OutboundBusinessHeader" description="Header used for outbound business messages.">
// <ref name="sessionID" type="SessionID"/>
// <ref name="msgSeqNum" type="SeqNum"/>
// <ref name="sendingTime" type="SendingTime"/>
// <ref name="possResend" type="PossResend"/>
// <type name="padding" primitiveType="char" semanticType="String" characterEncoding="ASCII" length="1" description="Padding for alignment purpose."/>
//</composite>
#pragma pack(push, 1)
struct OutboundBusinessHeader
{
UInt32 sessionID;
UInt32 msgSeqNum;
UInt64 sendingTime;
char possResend;
char padding;
void fromString(const StringRef& ref) {
OnixS::B3::fromHexString(this, sizeof(*this), ref.data(), ref.length());
}
};
#pragma pack(pop)
private:
HexStringCompositeDecoder<OutboundBusinessHeader> outboundBusinessHeaderDecoder_;
HexStringCompositeEncoder<OutboundBusinessHeader> outboundBusinessHeaderEncoder_;
public:
// Customized coders for BidirectionalBusinessHeader
//<composite name="BidirectionalBusinessHeader" description="Header used for business messages that can go inbound or outbound.">
// <ref name="sessionID" type="SessionID"/>
// <ref name="msgSeqNum" type="SeqNum"/>
// <ref name="sendingTime" type="SendingTime"/>
// <ref name="possResend" type="PossResend"/>
// <ref name="marketSegmentID" type="MarketSegmentIDOptional"/>
// <type name="padding" primitiveType="char" semanticType="String" characterEncoding="ASCII" length="2" description="Padding for alignment purpose."/>
//</composite>
#pragma pack(push, 1)
struct BidirectionalBusinessHeader
{
UInt32 sessionID;
UInt32 msgSeqNum;
UInt32 sendingTime;
char possResend;
char marketSegmentID;
char padding;
void fromString(const StringRef& ref) {
OnixS::B3::fromHexString(this, sizeof(*this), ref.data(), ref.length());
}
};
#pragma pack(pop)
private:
HexStringCompositeDecoder<BidirectionalBusinessHeader> bidirectionalBusinessHeaderDecoder_;
HexStringCompositeEncoder<BidirectionalBusinessHeader> bidirectionalBusinessHeaderEncoder_;
private:
// Special decoders for framing header.
// They are not used actually but required to successfully parse the template with the dummy message
// that contains field of the FramingHeader type.
HexStringCompositeDecoder<FramingHeaderComposite> framingHeaderBusinessHeaderDecoder_;
HexStringCompositeEncoder<FramingHeaderComposite> framingHeaderBusinessHeaderEncoder_;
};
}
}

SbeB3Codecs.cpp:

#include "SbeB3Codecs.h"
namespace OnixS {
namespace B3 {
size_t toHexString(char* buffer, const size_t bufferSize, const void* data, const size_t dataSize) {
if (bufferSize < (dataSize * 2 + 1)) {
throw std::domain_error("Not enough buffer size for hexadecimal encoding.");
}
const char* digits = "0123456789ABCDEF";
size_t counter = dataSize;
for (const unsigned char* p = static_cast<const unsigned char*>(data); counter; --counter) {
char x = digits[(*p) >> 4];
*buffer++ = x;
x = digits[(*p) & 0x0F];
*buffer++ = x;
++p;
}
*buffer = 0;
return dataSize * 2;
}
int hexToInt(char x) {
if (x >= '0' && x <= '9') {
return x - '0';
}
if (x >= 'a' && x <= 'f') {
return 0xA + (x - 'a');
}
if (x >= 'A' && x <= 'F') {
return 0xA + (x - 'A');
}
throw std::domain_error("Not a hexadecimal digit");
}
size_t fromHexString(void* data, const size_t dataSize, const char* hex, const size_t hexLength) {
if (dataSize < hexLength / 2) {
throw std::domain_error("Not enough buffer size for hexadecimal decoding.");
}
if (hexLength % 2) {
throw std::domain_error("The hexadecimal string must be of an even length.");
}
char* dest = static_cast<char*>(data);
for (size_t hLength = hexLength; hLength; hLength -= 2) {
char c = *hex++;
int v = hexToInt(c) << 4;
c = *hex++;
v += hexToInt(c);
*dest++ = v;
}
return hexLength / 2;
}
ISbeFieldDecoder* CodersLibrary::queryDecoder(CoderOwnerId /* owner */, SbeFieldMetaData field) {
if (!field.typeMetaData().name().compare("CustodianInfo"))
return &custodianInfoDecoder_;
if (!field.typeMetaData().name().compare("InboundBusinessHeader"))
return &inboundBusinessHeaderDecoder_;
if (!field.typeMetaData().name().compare("OutboundBusinessHeader"))
return &outboundBusinessHeaderDecoder_;
if (!field.typeMetaData().name().compare("BidirectionalBusinessHeader"))
return &bidirectionalBusinessHeaderDecoder_;
if (!field.typeMetaData().name().compare("FramingHeader"))
return &framingHeaderBusinessHeaderDecoder_;
return ONIXS_FIXENGINE_NULLPTR;
}
ISbeFieldEncoder* CodersLibrary::queryEncoder(CoderOwnerId /* owner */, SbeFieldMetaData field) {
if (!field.typeMetaData().name().compare("CustodianInfo"))
return &custodianInfoEncoder_;
if (!field.typeMetaData().name().compare("InboundBusinessHeader"))
return &inboundBusinessHeaderEncoder_;
if (!field.typeMetaData().name().compare("OutboundBusinessHeader"))
return &outboundBusinessHeaderEncoder_;
if (!field.typeMetaData().name().compare("BidirectionalBusinessHeader"))
return &bidirectionalBusinessHeaderEncoder_;
if (!field.typeMetaData().name().compare("FramingHeader"))
return &framingHeaderBusinessHeaderEncoder_;
return ONIXS_FIXENGINE_NULLPTR;
}
}
}

SbeB3.cpp:

#include "SbeB3Codecs.h"
#include "../../Common/Helpers.h"
#include "../../Common/Settings.h"
using namespace Settings;
using namespace OnixS::FIX;
using namespace OnixS::FIX::SBE;
namespace {
void encodeAndDecode();
};
int main(int, const char * [])
{
try {
EngineSettings engineSettings;
engineSettings.listenPort(-1) // ListenPort is set to '-1' to disable the telecommunication level.
.licenseStore(LicenseStore);
Engine::init(engineSettings);
encodeAndDecode();
std::cout << "Press Enter to exit." << std::endl;
waitUntilEnterKey();
}
catch(const Exception & ex) {
processSampleException(ex.what());
return 3;
}
}
namespace {
Message createPositionMaintenaceReport(const Dictionary &dictionary) {
char customCompositeHex[256];
Message msg("38", dictionary);
// Create business header and encode it into hexadecimal string.
OnixS::B3::CodersLibrary::OutboundBusinessHeader businessHeader;
businessHeader.sessionID = 10;
businessHeader.msgSeqNum = 1;
businessHeader.sendingTime = (UInt64)Timestamp::utc().toUnixNanosecondTimestamp();
businessHeader.possResend = 'Y';
businessHeader.padding = 0;
OnixS::B3::toHexString(customCompositeHex, sizeof(customCompositeHex), &businessHeader, sizeof(businessHeader));
// ...the hexadecimal string is used as the value of FIX field:
msg.set(35524, customCompositeHex);
// Fill elements that will be reflected as fixed-size part of the SBE message.
msg.set(710, 33); // posReqID
msg.set(48, 10250); // securityID
msg.set(22, '8'); //securityIDSource, SecurityIDSource.EXCHANGE_SYMBOL
msg.set(207, "BVMF"); // securityExchange, constant
msg.set(721, 12345); // posMaintRptID
msg.set(709, 1); // posTransType
msg.set(712, 1); // posMaintAction
msg.set(722, 9); // posMaintStatus
msg.set(1003, 37259); // tradeID
msg.set(713, 43591); // origPosReqRefID
msg.set(581, 39); // accountType
msg.set(715, Timestamp::utc().toUnixNanosecondTimestamp() / (86400 * 1000000000LL)); // clearingBusinessDate
msg.set(834, OnixS::FIX::Decimal(1, -4)); // thresholdAmount
msg.set(60, Timestamp::utc().toUnixNanosecondTimestamp()); // transactTime
msg.set(1, 999); // account
msg.set(35503, 'T'); // senderLocation
msg.set(723, 0); // posMaintResult
msg.set(719, 1); // contraryInstructionIndicator
// Create repeating group.
OnixS::FIX::Group group = msg.setGroup(702, 2); // noPositions
group[0].set(703, 'E'); // posType
group[0].set(704, 100); // longQty
group[0].set(705, 0); // shortQty
group[1].set(703, 'E'); // posType
group[1].set(704, 0); // longQty
group[1].set(705, 110); // shortQty
// Fill var-length data.
msg.set(35510, "Test sample"); // deskID
msg.set(5149, "Synthetic message"); // memo
msg.set(58, "Don't use it in real trading!"); // text
return msg;
}
OnixS::B3::FramingHeaderComposite* getFramingHeader(void* data) {
return static_cast<OnixS::B3::FramingHeaderComposite*>(data);
}
void encodeAndDecode() {
OnixS::B3::CodersLibrary coderLib;
const std::string sbeTemplates = readTextFile("b3-entrypoint-messages-6.4.2.xml");
Dictionary dict = Decoder::generateFixDictionary(sbeTemplates);
Message msg = createPositionMaintenaceReport(dict);
std::cout << "Message to encode: " << msg.toString() << std::endl;
///// Encodes the message.
Encoder encoder(sbeTemplates, &coderLib);
// The leading part of the buffer will be used for framing header.
const size_t FramingHeaderSize = sizeof(OnixS::B3::FramingHeaderComposite);
unsigned char encodedData[1000];
size_t rootBlockLength = 0;
// Encode the message and reserve area for the framing header.
size_t messageDataSize = encoder.encodeWithHeader(msg, 503, 0, &encodedData[FramingHeaderSize], sizeof(encodedData)- FramingHeaderSize, &rootBlockLength);
// Allocate framing header.
OnixS::B3::FramingHeaderComposite *framingHeader = getFramingHeader(encodedData);
framingHeader->setup((UInt16)messageDataSize);
//// Decodes the message.
Decoder decoder(sbeTemplates, dict, &coderLib);
int templateId = 0;
int version = 0;
size_t decodedBytes = 0;
// Decode message with skipping of the framing header content.
Message decodedMsg = decoder.decode(&encodedData[FramingHeaderSize], messageDataSize, &decodedBytes, &templateId, &version);
std::cout << "Decoded message: " << decodedMsg.toString() << std::endl;
}
};