TagValue Appendages#
OUCH 5.0 supports variable-length messages with optional fields encoded as TagValue elements. This allows messages to include additional information without changing the base message structure.
Overview#
TagValue appendages are variable-length sections appended to messages that contain optional fields. Each field is identified by a tag and can have different types (integer, string, price, etc.).
Writing TagValue Appendages#
Using AppendageWriter#
The easiest way to add optional fields to egress messages (Client → Exchange) is using the get_appendage_writer() method:
EnterOrder order;
order.set_user_ref_num(user_ref_num);
// ... set required fields ...
// Add optional fields
auto& appendage = order.get_appendage_writer();
appendage.add_min_qty(100); // Adds MinQty tag
appendage.add_max_floor(5000); // Adds MaxFloor tag
appendage.add_firm("FIRM"); // Adds Firm tag
// appendage_length_ is automatically updated
session.send_message(&order);
Available Tag Methods#
Common optional fields you can add:
auto& appendage = order.get_appendage_writer();
// Quantity fields
appendage.add_min_qty(100); // Minimum quantity
appendage.add_max_floor(5000); // Maximum floor
// Price fields
appendage.add_discretion_price(Price::from_double(150.25));
appendage.add_peg_offset(10); // Peg offset in cents
// String fields
appendage.add_firm("FIRM"); // Firm identifier
appendage.add_locate_broker("BROK"); // Locate broker
// Enum fields
appendage.add_customer_type(CustomerType::Retail);
appendage.add_price_type(PriceType::Limit);
appendage.add_post_only(PostOnly::Yes);
// Time fields
appendage.add_expire_time(3600); // Expire time in seconds
Reading TagValue Appendages#
Using Convenience Accessors#
Many messages provide convenience accessors for common optional fields:
void on_order_accepted(Session* s, std::uint64_t seq, const OrderAccepted* msg) {
// Using convenience accessor (returns std::optional)
if (auto min_qty = msg->get_min_qty()) {
std::cout << "MinQty: " << *min_qty << std::endl;
}
if (auto firm = msg->get_firm()) {
std::cout << "Firm: " << *firm << std::endl;
}
}
Using AppendageReader Directly#
For more control or to access fields without convenience accessors:
void on_order_accepted(Session* s, std::uint64_t seq, const OrderAccepted* msg) {
auto appendage = msg->get_appendage();
// Find a specific tag
if (auto element = appendage.find(TagValue::MIN_QTY)) {
UInt32 min_qty = element->as_uint32();
std::cout << "MinQty: " << min_qty << std::endl;
}
// Iterate over all tags
for (const auto& element : appendage) {
TagValue::Tag tag = element.tag();
switch (tag) {
case TagValue::MIN_QTY:
std::cout << "MinQty: " << element.as_uint32() << std::endl;
break;
case TagValue::FIRM:
std::cout << "Firm: " << element.as_string() << std::endl;
break;
// ... handle other tags
}
}
}
TagValue Element Types#
TagValue elements can contain different types:
UInt32:
element.as_uint32()Int32:
element.as_int32()String:
element.as_string()(returnsstd::string_view)Price:
element.as_price()Char:
element.as_char()
Example: Complete Order with Appendages#
EnterOrder order;
order.set_user_ref_num(user_ref_num);
order.set_side(Side::Buy);
order.set_quantity(1000);
order.set_symbol("AAPL");
order.set_price(Price::from_double(150.50));
order.set_time_in_force(TimeInForce::Day);
order.set_display(DisplayFlag::Visible);
order.set_capacity(Capacity::Agency);
order.set_intermarket_sweep_elig('N');
order.set_cross_type(CrossType::None);
order.set_cl_ord_id("ORD001");
// Add optional fields via TagValue appendage
auto& appendage = order.get_appendage_writer();
appendage.add_min_qty(100);
appendage.add_max_floor(5000);
appendage.add_firm("FIRM");
appendage.add_customer_type(CustomerType::Retail);
appendage.add_price_type(PriceType::Limit);
appendage.add_post_only(PostOnly::No);
appendage.add_expire_time(3600); // 1 hour
session.send_message(&order);
Thread Safety#
TagValueAppendageReader: Thread-safe for read operations (const methods). Multiple threads can read from the same appendage simultaneously. Iteration is not thread-safe (each thread must use its own iterator).
TagValueAppendageWriter: NOT thread-safe. All methods must be called from the same thread. Typically used from IOContext’s thread when constructing messages.
Best Practices#
Check for optional fields: Always use
std::optionalchecks when reading optional fieldsUse convenience accessors: Prefer convenience accessors when available
Iterate when needed: Use iteration when you need to process all optional fields
Type safety: Use the correct type accessor for each tag type
Thread safety: Use AppendageWriter only from IOContext’s thread (same as Session operations)