Expressive Bidding

Developer Reference


Production Status: Expressive Bidding is currently enabled for "Pairs" only (i.e. Expressive Orders consisting of two symbols with constraints on relative price and quantity), and does not currently include the `mkt()` function. Examples are included beyond what is currently live to facilitate discussion of expected use cases and testing. For details on currently available functionality, please see our Form ATS-N.

This guide provides detailed information on developing Bidder Logic. For a higher level treatment of Bidder Logic and Expressive Bidding in general, see the Expressive Bidding Guide.

Programming Guide

OneChronos has developed a package of ReasonML/OCaml functions and types to make bidder logic as easy as possible to write. This section is a programmer's reference for these OneChronos-specific aspects of bidder logic. These functions and types are each described in detail in the bidding language reference below.

The following sections focus on a few of the most important functions and types for creating bidder logic and how to use them.

List Operations

Bidder logic can work with lists of participating quantity variables (qty()) of variable length (length unknown at compile time). The simplest way to work with such lists is with OCaml/ReasonML List module operations and/or the helper functions described below.

Arithmetic operations over lists:

  • sum_l(x): returns the sum given a list of values (sum over x_i in list x);
  • max_l(x): returns the maximum value given a list of values (largest x_i in list x); and
  • min_l(x): returns the minimum value given a list of values (smallest x_i in list x).

Arithmetic operations with higher order functions via Map:

  • sum_map(f, x): given a function and a list, applies the function to each element and returns the sum (sum over f(x_i) in list x);
  • max_map(f, x): given a function and a list, applies the function to each element and returns the maximum (largest of f(x_i) in list x); and
  • min_map(f, x): given a function and a list, applies the function to each element and returns the minimum (smallest f(x_i) in list x).

Logical operations for generating constraints over lists:

  • all_l(p, x): returns true if all values in a list meet predicate (p), otherwise false;
  • exactly_one (p, x): returns true if exactly one value in a list meet predicate (p), otherwise false; and
  • all_eq(f, x): returns true if each f(x_i) in x is equal to each other f(x_j) in x for all (x_i, x_j pairs).

Non-participating variables and their computations can also benefit from list operations. Non-participating variables are those that are resolved and defined in advance of the auction. The OneChronos bidding language also works with a subset of the standard ReasonML/OCaml list operations, which can be used for such non-participating variables. They are defined in the List module and can be called by referencing the module and the method together(e.g., List.hd) or by opening the module by running open List; before the bidder logic code block, allowing direct access to the methods within (e.g., hd).

To see all available methods, run #show List in a try-onechronos Jupyter notebook. For general information, refer to the library documentations accordingly (ReasonML library / OCaml library). Note that #show also works for any module made available, e.g., #show Expressive_order.t.

Bidder Logic Return

There are a few functions that can be used to construct the Bidder_action return type for bidder logic code:

  • place_orders(): return a list of orders
  • place_notional(): return an expression representing the amount to pay/receive
  • place_basket(): return a net price for a total basket

These can be used independently to construct simple (unconstrained) Bidder_actions or be nested within subject_to() to easily subject the action to constraints. Each of these is described in the following sections.

  • subject_to(): attach constraints to an action above
  • assuming(): alias for subject_to()

The place_orders Action

The place_orders() function takes a list of orders as an argument. The action it returns is an expression of intent to participate in an auction according to the parameters of those orders (in particular, limit price and limit quantity). This semantically means "I wish to participate in each of these symbols at the price terms specified in the corresponding target orders, or better."

Input parameter type:

  • orders - a list of Expressive Bid types (list(Expressive_order.t)).

Examples:

reason
/* Simply "pass through" all target orders to the auction */
place_orders(arg.orders);

/* Place two specific orders: order_1 and order_2 */
place_orders([order_1, order_2])

/* Pass through orders subject to `constraints` expression */
subject_to(constraints, place_orders(arg.orders))

The place_notional Action

The place_notional() function provides a more explicit representation of the terms at which the bidder will participate, in the sense that it is stated as an equation summing to a total notional amount. Specifically, this equation is a linear combination (sum of products) where each product is a price times a quantity.

Input parameter type:

  • expr - a linear combination of price and qty() products. For example:
reason
place_notional(140 * qty(order_a) + 220 * qty(order_b))

A price expressed here cannot result in a fill at a price outside—or more aggressive than—the limit defined in the corresponding order.

Note that place_orders() is a specialization of place_notional(). When using place_orders(), price is inferred from each order. As such,

reason
place_notional(o_1.order.price * qty(order_a))

...is equivalent to:

reason
place_orders([order_a])

The place_notional() function accepts a linear expression: a sum of qty() terms with price coefficients. Instead of explicitly defining this expression, it can be generated over arbitrary lists of prices and quantities using list operations.

reason
/* pairs of prices and orders */
let price_order_pairs = [(price_a, order_a), (price_b, order_b)];

/* notional expression: price_a * qty(a) + price_b * qty(b) */
let total_notional = sum_map((px, ord) => px * qty(ord), price_order_pairs);
[place_notional(total_notional)];

The place_basket Action

The place_basket return action is useful for portfolio trades where there is flexibility on the prices of individual securities, as long as the basket as a whole is executed within specific net price parameters. This flexibility permits execution against a larger number of possible contra positions versus a basket where per-security prices are enforced such that they sum to the same net price. This flexibility may increase the probability of a fill.

Input parameters:

  • coeffs - a linear sum of integer times qty() terms: int * qty(Expressive_order.t). The first int element represents a quantity weighting for the security given by the second element (qty(Expressive_order)). The quantity weighting integer specifically describes the number of shares required for the given security in a single unit of the basket. For example, a basket of 2 shares of order_a and 3 shares of order_b would be: 2 * qty(order_a) + 3 * qty(order_b).
  • offset - price with_side representing the maximum net price per basket (difference between notional sold and notional purchased). Allowable variant constructors are (Buy, <int>) expressing willingness to pay up to the given amount per unit, or (Sell, <int>) expressing willingness to provide the basket for at least the given amount per unit. Because the unit price applies to an exact basket composition, constraints should not be included that permit flexibility on basket composition.
  • max_units - an integer representing a limit on the maximum number of units to be filled.

The following example represents an intent to pay (i.e., net buy) up to 100 dollars per unit of a basket whose components consist of buying 2 shares of "A" and selling 3 shares of "B" (represented by order_a and order_b respectively):

reason
/* Buy constructor indicates net buying */
place_basket(2 * order_a + 3 * order_b, (Buy, 100), 10);

An example of a possible fill for three units of the basket would be:

  • Security "A": BUY 6 "A" at 200 dollars per share; and
  • Security "B": SELL 9 "B" at 100 dollars per share.

The 2:3 ratio expressed in the unit argument is enforced as six shares of "A" and nine shares of "B," for a total of three (basket) units. The total amount bought is 1,200 dollars of "A," and the total amount sold is 900 dollars of "B," netting to 300 dollars paid (equal to the expressed limit of 100 dollars per unit). Note that if either leg ("A" or "B") is price improved, the amount paid per unit would be lower, and the execution may still take place. Any increase in the price paid for "A" would need to be offset by an increase in the price received for "B" for an execution to occur. For example, paying 200 dollars per share for "A" would not be permissible unless the price received for "B" was at least 100 dollars.

While prices for individual securities are not specified, fills in individual securities will still automatically be constrained to the prices defined (if any) in corresponding target orders and will adhere to NBBO execution constraints.

Note: sum_map can also be used to generate the unit argument for basket actions:

reason
let my_orders = [order_a, order_b, order_c]   // would be derived from `arg.orders`

place_basket(
  sum_map(o => o.data.weighting * qty(o), my_orders),
  Sell(3810),
  100
)

Bidder Logic Arguments

There are two arguments to a bidder logic function whose types are bidder_arg and ~mkt.

The bidder_arg argument is a record with two components (bidder_data and orders) originating from the inputs provided via FIX messages. By convention, this argument is usually just referred to as arg in the bidder logic function definition. The ~mkt argument is an accessor function for looking up market data measured by the venue.

In this section, we will discuss how these parameters are defined and used in bidder logic code. For a complete mapping between the input parameters and FIX tags, please refer to FIX documentation.

Target Orders

Target orders are limit orders upon which the bidder logic acts. This is a required input for all invocations of bidder logic. In the OneChronos Bidding Language, the Expressive_order type is used extensively. When orders are submitted via FIX messages, they are automatically transformed (deserialized) from JSON and represented in the following type structure for use in bidder logic:

reason
module Expressive_order = {
  type order = {
    id: order_id,     /* unique identifier for the order */
    symbol: symbol,   /* symbol as string */
    side: order_side, /* Buy or Sell */
    qty: quantity,    /* limit order quantity as int */
    price: price      /* limit price as int */
  }
  type data = 'a; /* bidder data merged onto the order */
}

Note that a target order provided over FIX may be linked to one or more bidders via the bidder logic reference ID, and a bidder may also have one or more orders to transact on. Because of this many-to-many relation, the input parameter orders is a list of Expressive_orders. Therefore, to deconstruct the list and work with orders individually, list operations are key.

Single order example using hd function (ReasonML library / OCaml library):

reason
let single_order: bidder(_) = (arg, ~mkt) => {
  let o = List.hd(arg.orders); /* expect only 1 order so retrieve the first order */
  let symbol = o.order.symbol; /* evaluates to "A" */
  /* [...] */
}

The function hd is included in the language's built-in List module. Other useful list operations are discussed in the list operations section.

Bidder Data

In addition to strongly typed target orders, you can optionally design custom data types specific to a submission of bidder logic. This provides flexibility when dealing with data that changes from auction to auction but isn't easily communicated through standard limit order tags. This custom data is exposed through the bidder_arg.bidder_data parameter and helps expressing/configuring unique bidding constraints like quantity ratios across symbols, factors, basket-level notional value limits, dollar neutrality bounds, etc.

To submit bidder_data at runtime, it must be in JSON format (e.g., name-value pairs), provided as a FIX tag, and included as part of a target order linked to the bidder it complements. Once the data is serialized into a JSON string and successfully transported over FIX, it is then automatically deserialized and structurally validated against the type defined with the bidder logic code. This is described further in the Expressive Bidding section of the FIX documentation.

While each user data is unique, accessing the data fields inside bidder logic is no different from retrieving key-value pairs from any other JSON object. For example, for a bidder that expects a list of symbols and a notional cap as an input, the user data might be defined and initialized in the following JSON format:

reason
/* type defined and submitted with bidder logic */
type custom_data = {
  symbols: list(string),
  notional_max: int
}

/* Provided as JSON over FIX at runtime */
let my_custom_data = {
  symbols=["A", "B", "C"],
  notional_max=500000
}

Remember that my_custom_data would be passed in later to the bidder logic function via the bidder_data input parameter. Since we know the type definition, accessing each field is as simple as arg.bidder_data.symbols and arg.bidder_data.notional_max. To see a simple example bidder with arg.bidder_data passed in, please refer to quickstart examples. Examples provided in the Runnable Templates section also make use of bidder data.

Market Data Accessors

OneChronos measures market data from the national exchanges via SIP continuously and constructs a snapshot of the national best bid and offer (NBBO) at the start of each auction. For more information on how we do this, see the market data section of our ATS-N filing.

The market data snapshot is made available by the mkt function, which is provided as an optional bidder logic argument as: let my_bidder: bidder(_) = (arg, ~mkt) => {...}. The ~ in ~mkt indicates that the argument is optional. When calling the function, the ~ is not included in the function name.

The mkt Function

val mkt: symbol -> mkt_price_point list

Arguments:

  • symbol: string value representing the symbol whose data is being requested.

Return:

  • list(mkt_price_point): a list of market price records. Each record contains data for an individual exchange identified by the src field, with one of the records containing composite (with field src: Composite). The full record type is defined as:
reason
type mkt_price_point = {
  src: mkt_src,          /* market center ID (variant) */
  bid: price,            /* best bid price */
  offer: price,          /* best offer price */
  bid_qty: quantity,     /* quantity at best bid price */
  offer_qty: quantity,   /* quantity at best offer price */
}

Market Data Helper functions

The bidding language includes several helper functions for accessing key data points from the record returned by mkt. For example, the mid(), nbb(), and nbo() functions return the single price representing the marketwide composite midpoint, NBB, or NBO respectively. These functions can be used as follows:

reason
/* Access midpoint price for symbol `my_symbol` */
let my_mkt_data = mkt(my_symbol);
let midpoint = mid(my_mkt_data);

/* Equivalent to above using pipe operator `|>` */
let midpoint = mkt(my_symbol) |> mid;

The full list of helper functions available is provided in the API reference below.

Examples:

reason
/* NBB price for symbol AAPL */
let nbb_aapl = mkt("AAPL") |> nbb;

/* Fetch composite midpoint for several symbols as a list of prices */
let mids = List.map(o=> mkt(o.order.symbol) |> mid, arg.orders);

/* Alternative: as a symbol-keyed association list */
let symbol_midpoint_assoc =
  List.map(o => (o.order.symbol, mkt(o.order.symbol) |> mid), arg.orders);

Working with Baskets

One key benefit of Expressive Bidding is the ability to express preferences over multiple symbols in a bid. So far, we have seen examples involving one or two symbols. Consider a scenario where we want to trade equal quantities of each symbol in a basket. Expanding on the previous implementation of equal_qtys:

reason
  let equal_qtys = qty(order_a) == qty(order_b) && qty(order_b) == qty(order_c);
  [subject_to(equal_qtys,
    place_orders([order_a, order_b, order_c])]
}

We can quickly see that the above method would be tedious and inflexible for a larger basket (e.g., a broad-based index). When we have many symbols, one way to check for equal quantities is to iteratively check that the quantity of one symbol is equal to the quantity of the rest of the symbols in the basket. Instead of writing each symbol by hand, here we apply some of the list operations discussed above — specifically, let's use the all_eq operation over the list of orders provided via the orders parameter to express our quantity constraints:

reason
let basket_bidder: bidder(_) = (arg, ~mkt) => {
  let equal_qtys = all_eq(order => qty(order), arg.orders);
  [subject_to(equal_qtys,
    place_orders(arg.orders))
  ];
}

We can modify this slightly to enforce equality in some ratios across symbols instead of exact equality. Suppose we now want to enforce a fill quantity across all symbols that ensures each symbol's notional is a ratio of some max notional. We can modify the above bidder to pass in additional information about these ratios and a max notional value through the bidder_data parameter (for details, refer to the bidder data section). For example:

reason

/* `ratio` and `basket_data` data types are defined with bidder logic submission */
type ratio = {
  sym: string,
  side: side, /* from module `Order` */
  ratio: real,
  price: int
}

type basket_data = {
  max_notional: int,
  ratios: list(ratio)
}

/* Example of decoded JSON provided at runtime via FIX and accessed via `bidder_data` parameter */
let my_basket_data = {
  ratios: [
    {sym="A", side=Buy, ratio=0.2, price=50},
    {sym="B", side=Buy, ratio=0.14, price=65},
    {sym="C", side=Buy, ratio=0.07, price=42},
  ],
  max_notional: 1000000,
}

/* bidder logic */
let basket_bidder: bidder(_) = (arg, ~mkt) => {
  let correct_ratios = all_eq((o => o.order.price * o.data.ratio * qty(o)), arg.orders);
  let total_notional = sum_map(o => o.order.price * qty(o), arg.orders);
  let below_max_notional = total_notional <= arg.bidder_data.max_notional;
  [subject_to(correct_ratios && below_max_notional,
    place_orders(arg.orders))
  ];
};

This bidder can be reused for any basket of symbols of arbitrary length. By using standard list operators as well as special extensions like all_eq, handling baskets with many symbols (e.g., broad-based ETFs) becomes more manageable and simpler to reason about. For further applications, please refer to the portfolio examples as well as the next section, where we demonstrate how to implement dollar neutrality when trading multiple symbols.

Dollar Neutrality Constraints

Dollar neutrality is a widely applied concept whenever a trade involves both buying and selling of two or more securities. For example, long/short pairs trades and ETF creation/redemption often require enforcing a degree of dollar neutrality. OneChronos' bidding language makes it easy to express this constraint across any number of securities.

First, we calculate the net notional across all participating orders.

reason
/* for two limit orders */
let notional_A = order_a.order.price * qty(order_a);
let notional_B = order_b.order.price * qty(order_b);
let net_notional = notional_A - notional_B;

reason
/* for baskets of symbols */
let long_securities = List.filter(order => order.side == Buy, orders);
let short_securities = List.filter(order => order.side == Sell, orders);
let notional_long = sum_map(order => qty(order) * order.price, long_securities);
let notional_short = sum_map(order => qty(order) * order.price, short_securities);
let net_notional = notional_long - notional_short;

Then we can use net_notional in a constraint to enforce that it is zero or approximately zero. Since getting the notional amount sold and notional amount purchased to be precisely the same is difficult, we may wish to define a small epsilon value (where "small" depends on the problem at hand). The following constraint captures the willingness to accept a net_notional difference of up to 100 dollars.

reason
/* for two limit orders */
subject_to(net_notional <= 100,
  place_notional(notional_A + notional_B))

/* for basket of symbols */
subject_to(net_notional <= 100,
  place_notional(notional_long + notional_short))

Exclusive-OR Constraints

Suppose we have a list of securities but want to trade one and only one. This concept is similar to the commonly known XOR in boolean logic. The critical difference in Expressive Bidding is that we often need mutual exclusion across multiple inputs, while a standard XOR is a binary operation. Multiple XOR operations may be chained together to evaluate more than two inputs, however doing so does not result in "one and only one" logic, but instead is true for an odd number of true inputs. Since this isn't particularly useful, OneChronos instead provides an exactly_one helper for cases where one and only one of something is desired. The exactly_one function has a truth table similar to a one-hot encoding as opposed to chains of XOR. It comes in handy when, for example, you want to be filled on exactly one security based on market conditions at the time of auction. It can also be useful for trading strategies that involve a choice among multiple options, such as price indifference curves and sector portfolios.

There are a few ways of expressing exclusive-or as a constraint. Given orders order_a and order_b for securities "A" and "B," we can write the following:

reason
let (qty_a, qty_b) = (qty(order_a), qty(order_b))
subject_to((qty_a > 0 && qty_b == 0) || (qty_b > 0 && qty_a == 0),
  /* [...] */
)

Or alternatively:

reason
let (qty_a, qty_b) = (qty(order_a), qty(order_b))
subject_to((qty_a + qty_b == qty_a) || (qty_a + qty_b == qty_b)
  /* [...] */
)

However, in both of the methods above, we can already see that these constraints can become lengthy when we have more than two securities. Instead, we want to go through the list of symbols, check the quantity of each symbol, and only transact if one and only one symbol's quantity is greater than 0. To make this easy, OneChronos has a list operation for that — exactly_one:

reason
exactly_one(order => qty(order) > 0, orders)
  /* [...] */
)

Rather than writing out each stock's fill quantity and comparing it to the next, we can simplify the constraint to a much more compact code:

Working with Per-Security Data

There are many cases where we want to enforce constraints that incorporate information about individual securities. That information might include per-security risk or investment factors, index weightings for individual components, correlation/covariance scores, fair value data, etc.

To make per-security data easier to work with, OneChronos provides a "pre-processor" per_sym_data_preprocessor that can automatically merge per-security data from bidder data onto the orders argument in bidder logic. Consider the following bidder data example where we wish to attach a weighting to each order in the orders argument.

reason
type component = {symbol: string, side: side, weighting: int}
type ratios_data = {basket: list(component)}

let ratios_data_sample = {
  basket: [
    {symbol: "ASDF", side: Sell, weighting: 6},
    {symbol: "QWER", side: Sell, weighting: 3},
    {symbol: "ZXCV", side: Sell, weighting: 1}
  ]
};

/* Maps `component` data onto `orders` arg; see Expressive Bidding Guide for more info */
let component_key = (c: component) => (c.side, c.symbol);

The component_key is what the preprocessor uses as a key to merge each weighting element onto the appropriate Expressive_order. This is similar to a database join using a composite key. The key is a function that takes an element of the record type we wish to map onto each order as an argument. This argument must be a record type, and it must contain symbol and side elements. The key function returns the (side, symbol) tuple that identifies the record fields matching Order.side and Order.symbol. The key is provided as the optional per_sym_data_key when uploading bidder logic via upload_bidder.

In bidder logic, any element in the record type provided as an argument to the key can be accessed via the Expressive_order.data field. For the example above, we can access the weighting fields for each order as follows:

reason
let weightings = List.map(o => o.data.weighting, arg.orders);

Additional Considerations

Things to consider when creating bidder logic for use in production.

Constraints and Target Order Limits

Limit quantities and limit prices on target orders always take precedence over constraints in bidder logic. A limit quantity constraint of 1000 shares on a target order can be considered a bidder logic constraint of the form qty(order_1) <= 1000. This precedence also applies to minimum fill constraints on target orders. For example, setting a minimum fill constraint as qty(order_1) >= 10000 but submitting the underlying target order with a maximum fill quantity of 1000 will result in an unsatisfiable set of constraints, and no fills.

Any fill received will be subject to the constraints expressed in the bidder logic function's return. If those conditions can't be met, then no executions will take place for that Expressive Bid.

Nonlinear Constraints

Variables bound using the qty() function represent terms in the linear equations (constraints and objective function) for the optimization used in OneChronos auctions. Any expressions involving qty() must be linear equations. For example, an equation of the form qty(order_a) + qty(order_b) < 10000 is allowable but qty(order_a) * qty(order_b) < 10000 is not. Coefficients on participating qty() variables must be numbers.

Bidder Actions with Overlapping Constraints

Bidder actions are mutually exclusive; if a list of more than one Bidder_action is returned from a bidder logic function, only one of those actions will receive an execution. The constraints specified in these actions should not "overlap," i.e., they should not be constructed such that the same execution (quantities and prices in one or more symbols) meets constraints for multiple actions. While "overlapping" constraints are possible, they are not recommended as no guarantees are made about which action will take precedence in that case.

Runtime Complexity Limits

Some bidders may not be eligible for an auction depending on the complexity of their bidder logic and constraints. This is because each auction has a finite amount of computational resources. Therefore, each bidder is allocated an equal amount of computational resources and evaluated before any auction to see if it exceeds its resource allocation — OneChronos refers to this as "fuel." If a bidder runs out of fuel, then an error message is sent via FIX to alert the user that their bids will not participate in the following auction.

Odd Lots and Round Lots

All executions on OneChronos are in round lots. However, bidder logic can include constraints with quantities that are not expressed in round lots. Those quantity constraints will act as upper or lower bounds but carry an implicit constraint that fills are also in round lots.

Suppose, for example, that a security has a round lot of 100 shares. An order for that security (nflx) may be subject to a constraint as in the following bidder action:

reason
subject_to(qty(nflx) <= 250,
  place_orders([nflx])]
)

This order could only be filled for 0, 100, or 200 shares. It could not be filled for a quantity between 201 and 250 because, despite being permitted by the bidder logic constraint, it would not be permitted by the implicit round lot constraint.

Expressive Bidding Language Reference

Table of Contents

This section contains technical guides to core modules, types, and functions specific to the OneChronos bidding language. Definitions and examples are provided in ReasonML but are 1:1 equivalent and convertible to OCaml. Scroll through each card to learn more.

Bid

Collection of functions to build any expressive bidding logic. Must be opened prior to returning any bidder_action.

Bid_linexp

Defines how arithmetic expressions used to build bidder logic are constructed and compiled.

Bidder_action

Defines the resulting actions of a bidder logic that contains a list of constraints and an action.

Expressive_order

Defines an Expressive_order type, which is a mapping of an Order.t (target order) to its corresponding user-defined custom per-symbol data.

Narya_bidder

Main module that defines the fundamental constructs of the OneChronos bidding language.

Order

Defines an Order type representing target order properties, provided as FIX tags on underlying target orders.

Narya_lib.Sim

Functions to create expressive bidders and run auction simulations.

Bid

Collection of functions to build any expressive bidding logic. Must be opened prior to returning any bidder_action.

val const

Passes an integer coefficient to a Const constructor that can be evaluated inside a Bid_linexp expression. Required for using integer types in constraints and expressions involving qty variables.

reason
let const: coeff => Bid_linexp.t;

val all_eq

Evaluates to true if all expressions in the list evaluate to equivalent values

reason
let all_eq: list(Bid_linexp.t) => Bid_constraint.t;

Returns: a Bid_constraint.t

ParameterTypeDescriptionRequired?
leBid_linexp.t lista list of linear expressionsyes

Example:

reason
open Bid;
 let weighted_qtys = List.map(o => o.data.weighting * qty_order(o.order), arg.orders)
 all_eq(weighted_qtys);

 /* Returns true if the weighted fill qty for symbols in the target orders are the same */

val all_distinct

Similar toall_eq but evaluates to true if all expressions in the list evaluate to distinct values

reason
let all_distinct: list(Bid_linexp.t) => Bid_constraint.t;

val exactly_one

Evaluates to true if one and only one value in the list of logical constraints is true

reason
let exactly_one: list(Bid_constraint.t) => Bid_constraint.t;

Returns: a Bid_constraint.t

ParameterTypeDescriptionRequired?
csBid_constraint.t lista list of constraintsyes

Example:

reason
open Bid;
 let fill_qtys = List.map(o => qty_order(o.order) > 0, arg.orders);
 exactly_one(fill_qtys);

 /* Returns true if only one of the fill qtys is greater than zero */

val all

Similar to exactly_one but evaluates to true if all values in the list of constraints evaluate to true

reason
let all: list(Bid_constraint.t) => Bid_constraint.t;

val some

Similar to exactly_one but evaluates to true only some values in the list of constraints evaluate to trueNB: some is the negation of all and can also be described as "more than one but not all"

reason
let some: list(Bid_constraint.t) => Bid_constraint.t;

val sum_l

Computes the arithmetic sum of all expressions in the list

reason
let sum_l: list(Bid_linexp.t) => Bid_linexp.t;

Returns: a Bid_linexp.t

ParameterTypeDescriptionRequired?
leBid_linexp.t lista list of linear expressionsyes

Example:

reason
open Bid;
 let notionals = List.map(o => o.order.price * qty_order(o.order), arg.orders);
 sum_l(notionals);

 /* Returns the total notional value of all orders */

val max_l

Similar to sum_l but returns the expression with the max value

reason
let max_l: list(Bid_linexp.t) => Bid_linexp.t;

val min_l

Similar to sum_l but returns the expression with the min value

reason
let min_l: list(Bid_linexp.t) => Bid_linexp.t;

val sum_map

Applies a function to all elements in a given list and sums up the resulting values of the function. Computes the sum as a linear expressionNB: sum_map is similar to sum_l but simplifying by folding List.map inside sum_l

reason
let sum_map: ('a => Bid_linexp.t, list('a)) => Bid_linexp.t;

Returns: a Bid_linexp.t

ParameterTypeDescriptionRequired?
f('a -> Bid_linexp.t)a function that evaluates any type 'ainto a linear expressionyes
l'a lista polymorphic list of any type 'ayes

Example:

reason
let total_notional = sum_map (fun o => o.order.price * qty_order(o.order), arg.orders);

 /* Returns the total notional value of all orders */

val max_map

Similar to sum_map but returns the expression with the max value

reason
let max_map: ('a => Bid_linexp.t, list('a)) => Bid_linexp.t;

val min_map

Similar to sum_map but returns the expression with the max value

reason
let min_map: ('a => Bid_linexp.t, list('a)) => Bid_linexp.t;

val qty_order

Converts the given Order.t input to a Bid_linexp.Var(Qty) variable. Represents the future fill quantity for the given order.

reason
let qty_order: Order.t => Bid_linexp.t;

Returns: a Bid_linexp.t

ParameterTypeDescriptionRequired?
oOrder.ta target orderyes

Example:

reason
let o1 = {Order.id: "o1", symbol: "ASDF", side: Buy, qty: 6000, price: 123}; Bid.qty_order(o1);

 /* Return value */
 - : Bid_linexp.t = (Qty {id=c1; sym=(Buy "ASDF");})

val qty

Similar to qty_order but creates a fill qty variable from an Expressive order.

reason
let qty: Expressive_order.t('a) => Bid_linexp.t;

Returns: a Bid_linexp.t

ParameterTypeDescriptionRequired?
o'a Expressive_order.tan expressive order, typcially from arg.ordersyes

val place_notional

Returns a bidder action that represents the notional amount for the bidder to pay or receive.

reason
let place_notional: Bid_linexp.t => action;

Returns: a bidder action

ParameterTypeDescriptionRequired?
leBid_linexp.tnotional expression as a sum of price * quantity termsyes

Example:

reason
let o1 = {Order.id: "o1", symbol: "ASDF", side: Buy, qty: 6000, price: 123};
 let o2 = {Order.id: "o2", symbol: "QWER", side: Buy, qty: 3000, price: 456};
 Bid.place_notional(140 * qty_order(o1) + 220 * qty_order(o2));

 /* Return values */
 - : action =
 	 {action=(Place_notional
 		 ((140 * (Qty {id=o1; sym=(Buy "ASDF");}))
 		 + (220 * (Qty {id=o2; sym=(Buy "QWER");}))));
 	 constraints=[]} /* Default empty constraint list - to set constraints, use `assuming`, `subject_to`, or `st` */

val place_orders

Similar to place_notional but the notional value is computed based on the orders provided and the limit prices specified within the orders.

reason
let place_orders: list(Expressive_order.t('a)) => action;

Returns: a bidder action

ParameterTypeDescriptionRequired?
os'a Expressive_order.t listexpressive orders from which to compute the desired notional valuesyes

Example:

reason
let expressive_o1 = {Expressive_order.order:o1, data:[]} /* empty data */
 let expressive_o2 = {Expressive_order.order:o2, data:[]} /* empty data */
 Bid.place_orders([expressive_o1, expressive_o2]);

 /* Return values */
 - : action =
 {action=(Place_orders
 	 (Sum
 	 [(Qty {id=o1; sym=(Buy "ASDF");});(Qty {id=o2; sym=(Buy "QWER");})]));
 constraints=[]} /* Default empty constraint list - to set constraints, use `assuming`, `subject_to`, or `st` */

val place_basket

Returns a bidder action specifically for a basket bidder, which requires quantity weightings for each basket component, a net unit price for the basket, and the max units of the basket to be traded.

reason
let place_basket:
  (~coeffs: Bid_linexp.t, ~offset: with_side(price), ~max_units: quantity) =>
  action;

Returns: a bidder action

ParameterTypeDescriptionRequired?
coeffsBid_linexp.tsum of weighted quantities of the symbols in the basketyes
offsetprice with_sideprice for the entire basket rather than for each componentyes
max_unitsquantitymax number of units of the basket to tradeyes

Example:

reason
type my_data = {weighting:int}
 let expressive_o1 = {Expressive_order.order:o1, data:{weighting: 2}}
 let expressive_o2 = {Expressive_order.order:o2, data:{weighting: 1}}
 let offset = (Buy, 250);
 let max_units = 1000;
 Bid.place_basket(sum_map(o => o.data.weighting * qty(o), [expressive_o1, expressive_o2]), offset, max_unit)

 /* Return values */
 - : action =
 {action=(Place_basket
 	 :coeffs (Sum
 		 [(2 * (Qty {id=o1; sym=(Buy "ASDF");}));
 		 (1 * (Qty {id=o2; sym=(Buy "QWER");}))])
 	:offset (Buy 2309) :max-units 1000);
 constraints=[]} /* Default empty constraint list - to set constraints, use `assuming`, `subject_to`, or `st` */

val simple_avg

Computes the average of two integer values (e.g. (x+y)/2)

reason
let simple_avg:
  (int, int) => int;

val abs_val

Computes the absolute value of a given input

reason
let abs_val: int => int;

val symbols_of

Retrieves the symbols of a given target order list

reason
let symbols_of:
  list(Order.t) => list(symbol);

Bid_linexp

Defines how arithmetic expressions used to build bidder logic are constructed and compiled.

type t

reason
type t =
  | Var(Bid_var.t)
  | Const(coeff)
  | Plus(t, t)
  | Mult(coeff, t)
  | Div(t, coeff)
  | List_op(list_op, list(t))
  | Abs(t);

Bidder_action

Defines the resulting actions of a bidder logic that contains a list of constraints and an action.

type action

reason
type action =
  | Standard_order({
      oid: string,
      sym: with_side(symbol),
      px: price,
    })
  | Place_orders(Bid_linexp.t)
  | Place_notional(Bid_linexp.t)
  | Place_basket({
      coeffs: Bid_linexp.t,
      offset: with_side(weight),
      max_units: int,
    });

type t

reason
type t = {
  constraints: list(Bid_constraint.t),
  action,
};

Expressive_order

Defines an Expressive_order type, which is a mapping of an Order.t (target order) to its corresponding user-defined custom per-symbol data.

type t

reason
type t('a) = {
  order: Order.t,
  data: 'a,
};

Narya_bidder

Main module that defines the fundamental constructs of the OneChronos bidding language.

type symbol

reason
type symbol = string;

type price

Prices are integer values for the purpose of simulation. In production, prices are expressed as decimal dollar amounts.

reason
type price = int;

type coeff

A coefficient used in constraints, linear expressions, or quantity ratios

reason
type coeff = int;

type quantity

Represents static quantities (e.g. limit quantities from Order.t). Not to be confused with qty which creates a symbol fill quantity variable.

reason
type quantity = int;

type weight

Integer weighting used to express quantity ratios in a net price basket / portfolio bid.

reason
type weight = int;

type order_id

reason
type order_id = string;

type side

reason
type side =
  | Buy
  | Sell;

type with_side

A constructor that defines a tuple where the fist element is a side and the second is any type (most commonly used with a symbol or a basket unit price).

reason
type with_side('a) = (side, 'a);

val with_side_get

Retrieves the element (type 'a) to which side is assigned to.

reason
let with_side_get: with_side('a) => 'a;

ParameterTypeDescriptionRequired?
t'a with_sidea tuple with side as the first element and any type 'a as the second elementyes

Example:

reason
let my_basket_price = (Buy, 250);
 let px = with_side_get(my_basket_price);

 /* Return value */
 val px : int = 250

type mkt_src

A variant used to retrieve the desired value from market data for a given symbol.

reason
type mkt_src =
  | Cboe
  | CboeBYX
  | CboeBZX
  | CboeEDGA
  | CboeEDGX
  | Nasdaq
  | NasdaqBX
  | NasdaqPHLX
  | NasdaqISE
  | NYSE
  | NYSEArca
  | NYSEAmerican
  | NYSENational
  | NYSEChicago
  | FINRA
  | IEX
  | LTSE
  | MIAX
  | MEMX
  | Composite;

type mkt_price_point

A single data point representing the NBBO for a given symbol and given source (exchange).

reason
type mkt_price_point = {
  src: mkt_src,
  bid: price,
  offer: price,
  bid_qty: quantity,
  offer_qty: quantity,
};

module Order

Defines an Order type representing target order properties, provided as FIX tags on underlying target orders.

module Bid_linexp

Defines how arithmetic expressions used to build bidder logic are constructed and compiled.

module Bidder_action

Defines the resulting actions of a bidder logic that contains a list of constraints and an action.

type action

reason
type action = Bidder_action.t;

type actions

A list of bidder actions

reason
type actions = list(action);

module Expressive_order

Defines an Expressive_order type, which is a mapping of an Order.t (target order) to its corresponding user-defined custom per-symbol data.

module Bid

Collection of functions to build any expressive bidding logic. Must be opened prior to returning any bidder_action.

val assuming

An alias for assuming

reason
let assuming: (Bid_constraint.t, action) => action;

val subject_to

Appends a given constraint term to the constraint list of a bidder action

reason
let subject_to: (Bid_constraint.t, action) => action;

Returns: an updated action with the constraint provided

ParameterTypeDescriptionRequired?
constraintBid_constraint.ta logical constraint that must be true for a bidder action to take placeyes
actionBidder_action.tthe action that is contingent on the constraintyes

Example:

reason
let expressive_o1 = {Expressive_order.order:o1, data:[]} /* empty data */
 let expressive_o2 = {Expressive_order.order:o2, data:[]} /* empty data */
 Bid.subject_to(
 	 qty(expressive_o1) > 100,
 	 place_orders([expressive_o1, expressive_o2])
 );

 /* Return values */
 - : action =
 {action=(Place_orders
 	 (Sum
 	 [(Qty {id=o1; sym=(Buy "ASDF");});(Qty {id=o2; sym=(Buy "QWER");})]));
 constraints=[(Qty {id=o1; sym=(Buy "ASDF");}) > 100]}

val st

An alias for subject_to

reason
let st: (Bid_constraint.t, action) => action;

type bidder_arg

Arguments to a bidder, which are a list of Expressive_orders and any custom bidder data, where 'a is a user-defined existential type for the per-symbol data attached to Expressive orders, and 'b is a user-defined existential type for bidder data.

reason
type bidder_arg('a, 'b) = {
  orders: list(Expressive_order.t('a)),
  bidder_data: 'b,
};

type short_name

Name given to bidder during upload_bidder.

reason
type short_name = string;

type hash

SHA1 encrypted value of the associated bidder logic code.

reason
type hash = string;

type kind

Bidder type that is part of the bidder name object. Note that K_standard is only used for simulation.

reason
type kind =
  | K_standard
  | K_user_defined;

type name

Full name assigned to a bidder upon upload with short_name, hash, and kind`.

reason
type name = {
  short: short_name,
  hash,
  kind,
};

type mkt_symbol_snapshot

A list of prices from different market centers for a given symbol and point in time

reason
type mkt_symbol_snapshot = {
  mkt_sym: symbol,
  mkt_prices: list(mkt_price_point),
};

type mkt_snapshot

A full list of available market data at a given point in time.

reason
type mkt_snapshot = {
  mkt_timestamp: timestamp,
  sym_snapshots: list(mkt_symbol_snapshot),
};

val composite

Return only the Composite price point, given a full list of mkt_price_points from different sources.

reason
let composite: list(mkt_price_point) => mkt_price_point;

val nbb

Return the single NBB price, given a full list of mkt_price_points (must contain Composite source record).

reason
let nbb: list(mkt_price_point) => price;

val nbo

Return the single NBO price, given a full list of mkt_price_points (must contain Composite source record).

reason
let nbo: list(mkt_price_point) => price;

val mid

Return the single Midpoint (simple, unweighted average) price, given a full list of mkt_price_points (must contain Composite source record).

reason
let mid: list(mkt_price_point) => price;

Order

Defines an Order type representing target order properties, provided as FIX tags on underlying target orders.

type t

reason
type t = {
  id: order_id,
  symbol,
  side,
  qty: quantity,
  price,
};

Narya_lib.Sim

Functions to create expressive bidders and run auction simulations.

val upload_bidder

Uploads bidder to server by bidder name. Once your custom bidder has been defined, use upload_bidder to upload the bidder to the server.

reason
let upload_bidder:
  (
    ~name: string=?,
    ~bidder_data_is_valid: string=?,
    ~bidder_data_preprocessor: string=?,
    ~per_sym_data_key: string=?,
    ~per_sym_data_preprocessor: string=?,
    string
  ) =>
  name;

Returns: a bidder name that consists of short_name, hash, and kind

ParameterTypeDescriptionRequired?
f_namestringname of bidder functionyes
namestringcustom name for bidderno
bidder_data_is_validstringname of bidder data validator functionno
bidder_data_preprocessorstringname of bidder data JSON deserializerno
per_sym_data_keystringname of per-symbol data lookup functionno
per_sym_data_preprocessorstringname of per-symbol data JSON deserializerno

Example:

reason
let portfolio_bidder_id = upload_bidder(
 	 ~bidder_data_preprocessor="of_json_ratios_data",
 	 ~per_sym_data_preprocessor="of_json_component",
 	 ~per_sym_data_key="component_key",
 	 "portfolio_bidder"
 );

 /* Return value */
 val portfolio_bidder_id : name =
 	 {short = "portfolio_bidder";
 	 hash = "85d1958f1f3e3ee390b285172980078279cf0370";
 	 kind = K_user_defined}


val list_bidders

Lists all active bidders.

reason
let list_bidders:
  unit => list(string);

Returns: a list of concatenated names of the format short_name/hash.

val delete_bidders

Deletes bidder(s) based on list of bidder shortnames provided. If no list provided, then deletes all bidders in the state.

reason
let delete_bidders:
  (
    ~bidders: list(string)=?,
    unit
  ) =>
  unit;

ParameterTypeDescriptionRequired?
biddersshort_name listlist of bidder short names to be removedno

Example:

reason
delete_bidders(
 	 ~bidders=["portfolio_bidder"]
 ) ();


val update_mkt_data

Replaces existing market data with a new list of market symbol snapshots; prior market symbol snapshots are not preserved. Automatically updates timestamp associated with snapshots.

reason
let update_mkt_data:
  list(B.mkt_symbol_snapshot) => unit;

Returns: updated mkt_price_point list

ParameterTypeDescriptionRequired?
mkt_datamkt_symbol_snapshot listlist of market data points per symbolyes

Example:

reason
let mkt_data: list(mkt_symbol_snapshot) = [
 	 {mkt_sym: "ASDF", mkt_prices: [{bid: 120, offer: 124, src: Composite, bid_qty: 1000, offer_qty: 1000}]},
 	 {mkt_sym: "QWER", mkt_prices: [{bid: 453, offer: 457, src: Composite, bid_qty: 1000, offer_qty: 1000}]},
 	 {mkt_sym: "ZXCV", mkt_prices: [{bid: 786, offer: 790, src: Composite, bid_qty: 1000, offer_qty: 1000}]},
 ];
 update_mkt_data(mkt_data);



val get_mkt_data

Retrieves the current set of market data.

reason
let get_mkt_data: unit => B.mkt_snapshot;

Returns: the current mkt_snapshot.

val set_orders

Assigns and maps a list of target orders to the bidder specified. Replaces any existing target orders associated with the same bidder.

reason
let set_orders:
  (name, list(B.Order.t)) => unit;

ParameterTypeDescriptionRequired?
bidder_namenamename of the bidder to upload orders toyes
ordersOrder.t listlist target ordersyes

Example:

reason
let portfolio_sample_orders: list(Order.t) = [
 	 {id: "o1", symbol: "ASDF", side: Sell, qty: 10000, price: 121},
 	 {id: "o2", symbol: "QWER", side: Sell, qty: 10000, price: 454},
 	 {id: "o3", symbol: "ZXCV", side: Sell, qty: 10000, price: 787},
 ];

 set_orders(portfolio_bidder_id, portfolio_sample_orders);

 /* Return value */
 val portfolio_sample_orders : Order.t list =
 	 {Order.id = "o1"; symbol = "ASDF"; side = Sell; qty = 10000; price = 121};
 	 {Order.id = "o2"; symbol = "QWER"; side = Sell; qty = 10000; price = 454};
 {	 Order.id = "o3"; symbol = "ZXCV"; side = Sell; qty = 10000; price = 787}]
 - : unit = ()


val get_orders

Retreives list of target orders associated with given bidder name.

reason
let get_orders: name => list(B.Order.t);

Returns: current target orders (Order.t list) associated with given bidder

ParameterTypeDescriptionRequired?
bidder_namenamename of the bidder to get target orders fromyes

Example:

reason
get_orders(portfolio_bidder_id);

 /* Return value */
 - : Order.t list =
 [{Order.id = "o1"; symbol = "ASDF"; side = Sell; qty = 10000; price = 121};
 {Order.id = "o2"; symbol = "QWER"; side = Sell; qty = 10000; price = 454};
 {Order.id = "o3"; symbol = "ZXCV"; side = Sell; qty = 10000; price = 787}]


val set_bidder_data

Assigns custom bidder data (JSON) to a bidder specified by its name. By default, no/empty per-symbol data is assigned (see set_per_sym_data for symbol specific data).

reason
let set_bidder_data: (~name: name, B.Json.t) => unit;

ParameterTypeDescriptionRequired?
bidder_namenamename of the bidder to assign data toyes
bidder_dataJson.tcustom bidder datayes

Example:

reason
set_bidder_data(portfolio_bidder_id, to_json_ratios_data(portfolio_sample_data));


val set_empty_data

reason
let set_empty_data: name => unit;

ParameterTypeDescriptionRequired?
bidder_namenamename of the bidder to assign default data toyes

Example:

reason
set_empty_data(portfolio_bidder_id);


val set_data

Assigns bidder data to the specified bidder based on the inputs provided. If only bidder data is provided, then the behavior is identical to set_bidder_data. If both bidder data and per-symbol data are provided, both are updated assuming both preprocessors and the per-symbol key were provided at the point of bidder upload. Per-symbol data cannot be provided alone, it must be provided with bidder data. If neither are provided, the behavior follows set_empty_data.

reason
let set_data:
  (
    ~per_sym: list('a)=?,
    ~to_json_per_sym: 'a => B.Json.t=?,
    ~bidder_data: 'b=?,
    ~to_json_bidder_data: 'b => B.Json.t=?,
    name
  ) =>
  unit;

ParameterTypeDescriptionRequired?
bidder_namenamename of the bidder to assign data toyes
per_sym'a listlist of custom per-symbol datano
to_json_per_sym<fun> = ('a -> Json.t)serializer for custom per-symbol datano
bidder_data'bcustom bidder datano
to_json_bidder_data<fun> = ('b -> Json.t)serializer for custom bidder datano

Example:

reason
set_data(
 	 portfolio_bidder_id,
 	 ~bidder_data=portfolio_sample_data,
 	 ~to_json_bidder_data=to_json_ratios_data,
 	 ~per_sym=portfolio_sample_data.basket,
 	 ~to_json_per_sym=to_json_component,
 );


val upload_standard_order

Uploads a standard limit order as its own bidder with the given name.

reason
let upload_standard_order: (string, B.Order.t) => name;

Returns: a bidder name that consists of short_name, hash, and kind.

ParameterTypeDescriptionRequired?
bidder_namenamename to assign to the standard limit orderyes
orderOrder.tstandard limit orderyes

Example:

reason
let contra_1: Order.t = {Order.id: "c1", symbol: "ASDF", side: Buy, qty: 6000, price: 123};
 let contra_2: Order.t = {Order.id: "c2", symbol: "QWER", side: Buy, qty: 3000, price: 456};
 let contra_3: Order.t = {Order.id: "c3", symbol: "ZXCV", side: Buy, qty: 10000, price: 789};

 upload_standard_order("contra_1", contra_1);
 upload_standard_order("contra_2", contra_2);
 upload_standard_order("contra_3", contra_3);

 /* Return values */
 val contra_1 : Order.t =
 	 {Order.id = "c1"; symbol = "ASDF"; side = Buy; qty = 6000; price = 123}
 val contra_2 : Order.t =
 	 {Order.id = "c2"; symbol = "QWER"; side = Buy; qty = 3000; price = 456}
 val contra_3 : Order.t =
 	 {Order.id = "c3"; symbol = "ZXCV"; side = Buy; qty = 10000;price = 789}

 - : name =
 {short = "contra_1";
 	 hash = "920cff9f-263c-4077-8863-c0cef4d51ef2";
 	 kind = K_standard}
 - : name =
 {short = "contra_2";
 	 hash = "1f44476b-e87e-4d6c-85e4-34a769b6dc16";
 	 kind = K_standard}
 - : name =
 {short = "contra_3";
 	 hash = "3db2722f-59e4-45cf-b130-9e74333930af";
 	 kind = K_standard}


val gen_mkt_data_from_orders

Generates a list of market data vectors based on a list of orders provided. The bid/offer prices are set to +/- 2 points from the limit price. Data contains prioces from three exchange sources and Composite.

reason
let gen_mkt_data_from_orders:
  list(B.Order.t) =>
  list(B.mkt_symbol_snapshot);

Returns: a list of mkt_symbol_snapshot (NBBOs per symbol).

ParameterTypeDescriptionRequired?
ordersOrder.t listlist of standard limit ordersyes

Example:

reason
gen_mkt_data_from_orders(contras);

 /* Return value */
 - : mkt_symbol_snapshot list =
 [{mkt_sym = "ASDF";
 	 mkt_prices =
 	 [{src = NYSE; bid = 121; offer = 125;
 		 bid_qty = 1000; offer_qty = 1000};
 	 {src = Nasdaq; bid = 121; offer = 125;
 		 bid_qty = 1000; offer_qty = 1000};
 	 {src = CboeEDGX; bid = 121; offer = 125;
 		 bid_qty = 1000; offer_qty = 1000};
 	 {src = Composite; bid = 121; offer = 125;
 		 bid_qty = 1000; offer_qty = 1000}]};
 {mkt_sym = "QWER";
 	 mkt_prices =
 	 [{src = NYSE; bid = 454; offer = 458;
 		 bid_qty = 1000; offer_qty = 1000};
 	 {src = Nasdaq; bid = 454; offer = 458;
 		 bid_qty = 1000; offer_qty = 1000};
 	 {src = CboeEDGX; bid = 454; offer = 458;
 		 bid_qty = 1000; offer_qty = 1000};
 	 {src = Composite; bid = 454; offer = 458;
 		 bid_qty = 1000; offer_qty = 1000}]};
 {mkt_sym = "ZXCV";
 	 mkt_prices =
 	 [{src = NYSE; bid = 787; offer = 791;
 		 bid_qty = 1000; offer_qty = 1000};
 	 {src = Nasdaq; bid = 787; offer = 791;
 		 bid_qty = 1000; offer_qty = 1000};
 	 {src = CboeEDGX; bid = 787; offer = 791;
 		 bid_qty = 1000; offer_qty = 1000};
 	 {src = Composite; bid = 787; offer = 791;
 		 bid_qty = 1000; offer_qty = 1000}]}]


val gen_orders_from_mkt_data

Generates a list of limit orders based on market data specified. The ~side parameter, if provided, sets the side of all orders. If ~side not provided, the side for each order is generated randomly. Limit quantity is set by default to 10000 and the limit price is the midpoint of NBBO (Composite src data must be available in the market data input). Each order is assigned an auto-generated string id.

reason
let gen_orders_from_mkt_data:
  (~side: B.side=?, list(B.mkt_symbol_snapshot)) =>
  list(B.Order.t);

Returns: an Order.t list.

ParameterTypeDescriptionRequired?
mkt_datamkt_symbol_snapshot listlist of NBBOsyes
sidesideorder side, either Buy or Sellno

Example:

reason
let mkt_data: list(mkt_symbol_snapshot) = [
 	 {mkt_sym: "ASDF", mkt_prices: [{bid: 120, offer: 124, src: Composite, bid_qty: 1000, offer_qty: 1000}]},
 	 {mkt_sym: "QWER", mkt_prices: [{bid: 453, offer: 457, src: Composite, bid_qty: 1000, offer_qty: 1000}]},
 	 {mkt_sym: "ZXCV", mkt_prices: [{bid: 786, offer: 790, src: Composite, bid_qty: 1000, offer_qty: 1000}]},
 ];

 gen_orders_from_mkt_data(mkt_data);

 /* Return value */
 - : Order.t list =
 [{Order.id = "siy"; symbol = "ASDF"; side = Buy; qty = 10000; price = 122};
 	 {Order.id = "dkxl"; symbol = "QWER"; side = Sell; qty = 10000; price = 455};
 	 {Order.id = "kti"; symbol = "ZXCV"; side = Sell; qty = 10000; price = 788}]


val gen_orders

Generates a list of limit orders from a list of symbols. Limit quantity is set by deafult to 10000, limit price is a randomly generated value within 100-900, and side is randomly selected between Buy and Sell. Each order is assigned an auto-generated string id.

reason
let gen_orders:
  list(B.symbol) => list(B.Order.t);

Returns: an Order.t list.

ParameterTypeDescriptionRequired?
symbolssymbol listlist of symbolsyes

Example:

reason
gen_orders(["ASDF", "QWER", "ZXCV"]);

 /* Return value */
 - : Order.t list =
 [{Order.id = "cvntv"; symbol = "ASDF"; side = Sell; qty = 10000; price = 801};
 	 {Order.id = "mqox"; symbol = "QWER"; side = Sell; qty = 10000; price = 784};
 	 {Order.id = "bfurh"; symbol = "ZXCV"; side = Sell; qty = 10000; price = 428}]


val gen_contras

Generates contra orders based on a list of target orders, with opposite sides and prices that are +/- 5 of the limit price. Each order is assigned an auto-generated string id.

reason
let gen_contras:
  list(B.Order.t) => list(B.Order.t);

Returns: an Order.t list.

ParameterTypeDescriptionRequired?
ordersOrder.t listlist of standard limit ordersyes

Example:

reason
gen_contras(portfolio_sample_orders);

 /* Return value */
 - : Order.t list =
 [{Order.id = "dndl"; symbol = "ASDF"; side = Buy; qty = 10000; price = 126};
 	 {Order.id = "gfqsi"; symbol = "QWER"; side = Buy; qty = 10000; price = 459};
 	 {Order.id = "aymbk"; symbol = "ZXCV"; side = Buy; qty = 10000; price = 792}]


val upload_generated_orders

Uploads a list standard orders in bulk via upload_standard_order. Each order uploaded is assigned a bidder name based on its order id (e.g. order_autogen_i).

reason
let upload_generated_orders:
  list(B.Order.t) => list(name);

Returns: a list of bidder names.

ParameterTypeDescriptionRequired?
ordersOrder.t listlist of standard limit ordersyes

Example:

reason
upload_generated_orders(portfolio_sample_orders);

 /* Return value */
 - : name list =
 [{short = "order_autogen_o1";
 	 hash = "d53e46ca-775c-431c-88a5-ab448fd040c9";
 	 kind = K_standard};
 {short = "order_autogen_o2";
 	 hash = "695e6cf0-9f28-4009-89df-f89c8e8e5719";
 	 kind = K_standard};
 {short = "order_autogen_o3";
 	 hash = "c49ad223-577a-4258-ba9f-1962050efa77";
 	 kind = K_standard}]


val compute_bids

Computes a list of bidder actions and consumed fuel for each bidder based on the bidder logic and data uploaded.

reason
let compute_bids:
  (~id: int=?, unit) => C.status;

Returns: an auction status, including a list of bidder_responses, corresponding mkt_snapshot, and a list of target_orders referenced.

ParameterTypeDescriptionRequired?
idintcustom auction idno

Example:

reason
compute_bids();

 /* Return values */
 - : status =
 {bidder_responses =
 	 [{Bidder_response.name =
 		 {short = "portfolio_bidder";
 		 hash = "85d1958f1f3e3ee390b285172980078279cf0370";
 		 kind = K_user_defined};
 	 actions =
 		 Ok
 			 [{action=(Place_orders
 				 (Sum
 					 [(Qty {id=o1; sym=(Sell "ASDF");});
 						 (Qty {id=o2; sym=(Sell "QWER");});
 						 (Qty {id=o3; sym=(Sell "ZXCV");})]));
 	 constraints=[(AllEq {sign=true}
 							 [(6 * (Qty {id=o1; sym=(Sell "ASDF");}));
 							 (3 * (Qty {id=o2; sym=(Sell "QWER");}));
 							 (1 * (Qty {id=o3; sym=(Sell "ZXCV");}))])]}];
 	 consumed_fuel = 14};
 	 ...
 	 mkt_data =
 		 {mkt_timestamp = 1620762048;
 		 mkt_prices =
 			 [{mkt_sym: "ASDF", mkt_prices: [{bid: 120, offer: 124, src: Composite, bid_qty: 1000, offer_qty: 1000}]},
 			 {mkt_sym: "QWER", mkt_prices: [{bid: 453, offer: 457, src: Composite, bid_qty: 1000, offer_qty: 1000}]},
 				 {mkt_sym: "ZXCV", mkt_prices: [{bid: 786, offer: 790, src: Composite, bid_qty: 1000, offer_qty: 1000}]},
 	 target_orders =
 		 {Order.id = "o1"; symbol = "ASDF"; side = Sell; qty = 10000; price = 121};
 		{Order.id = "o2"; symbol = "QWER"; side = Sell; qty = 10000; price = 454};
 		 {Order.id = "o3"; symbol = "ZXCV"; side = Sell; qty = 10000; price = 787}]}


val run_auction

Runs the auction based on the bidders, market data, target orders, and bidder data that have been previously set.

reason
let run_auction:
  (~id: int=?, unit) => C.solution;

Returns: an auction result solution, with fill quantities and clearing prices for each symbol in the auction, along with an explanation of the WDP (Winner Determination Problem) model.

ParameterTypeDescriptionRequired?
idintcustom auction idno

Example:

reason
run_auction();

 /* Please visit try.onechronos.com and run `auction_simulator.ipynb` to see the detailed results! */


val clear_state

Clears / resets the auction state (active bidders, market data, target orders).

reason
let clear_state: unit => unit;