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() (returns std::string_view)

  • Price: element.as_price()

  • Char: element.as_char()

Supported Tags#

Common TagValue tags include:

Tag

Type

Description

MIN_QTY

UInt32

Minimum quantity

MAX_FLOOR

UInt32

Maximum floor

FIRM

String

Firm identifier

CUSTOMER_TYPE

Char

Customer type (R/N)

PRICE_TYPE

Char

Price type (L/P/M/R/Q/m)

PEG_OFFSET

Int32

Peg offset

DISCRETION_PRICE

Price

Discretionary price

POST_ONLY

Char

Post only flag (P/N)

RANDOM_RESERVES

UInt32

Random reserve shares

EXPIRE_TIME

UInt32

Expiration time in seconds

GROUP_ID

UInt16

Group ID

USER_REF_IDX

UInt8

User reference index

See Appendix A for the complete list of supported tags.

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#

  1. Check for optional fields: Always use std::optional checks when reading optional fields

  2. Use convenience accessors: Prefer convenience accessors when available

  3. Iterate when needed: Use iteration when you need to process all optional fields

  4. Type safety: Use the correct type accessor for each tag type

  5. Thread safety: Use AppendageWriter only from IOContext’s thread (same as Session operations)