Expressive Bidding

Expressive Bidding Template


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.

About These Templates

OneChronos provides a set of pre-written bidder logic and sample data for some of the most common single-security and multi-security trading strategies. These templates are meant as a starting point for producing production bidder logic, and can be customized to your needs:

NBBO ConsensusFull Template

set the limit price at specific points throughout the bid/ask spread, as a function the proportion of exchanges agreeing on NBBO.

reason
let score = nbbo_deviation_rate(key_mkts_data, nbb_px, nbo_px);    
/* set price within spread price based on scoring (btwn 0..1) */
let dynamic_px = switch (o.order.side) {
    | Buy => nbb_px + (spread * score);
    | Sell => nbo_px - (spread * score);
};
Ok (Bid.([place_notional(dynamic_px * qty(o))]));

Single-Stock Indifference CurveFull Template

Express a willingness to trade different quantities at different price levels, accepting a precise max cost of liquidity for different size fills.

reason
/* Construct an action for a given price/qty tier */
let within_qty_tier = qty(o) >= const(tier.min) 
                   && qty(o) <= const(tier.max);

subject_to(within_qty_tier,
    place_notional(tier.px * qty(o)));

Synthetic Create or RedeemFull Template

substitute N shares of an index ETF for N units of its components (redeem), or vice versa (create).

reason
/* Compute list of index-weighted -fill- qtys */
let weighted_qtys = List.map(o => 
    o.data.weighting * qty_order(o.order), arg.orders);

/* Enforce equality across weighted fill qtys */
Ok ([subject_to(all_eq(weighted_qtys),
    place_orders(arg.orders))]);

Dollar Neutral BasketFull Template

Execute a basket of stocks in any dollar-neutral combination, within some tolerance (provided via FIX along with component orders).

reason
/* Compute net notional dollars -filled- using 'qty()' function */
let net_notional = sum_map(o => 
    side_coeff(o.order.side) * qty(o), arg.orders);

Ok ([subject_to(abs(net_notional) <= const(arg.bidder_data.tolerance),
    place_orders(arg.orders))]);

Midpoint DiscretionFull Template

Trade different quantities at either the midpoint or at the near touch (NBB or NBO).

reason
let nbbo_px = switch (o.order.side) {
  | Buy =>  mkt(o.order.symbol) |> nbb;
  | Sell => mkt(o.order.symbol) |> nbo;
};
let mid_px = mkt(o.order.symbol) |> mid;

Ok ([subject_to(qty(o) <= const(mid_qty),
    place_notional(mid_px * qty(o))),
 subject_to(const(mid_qty) < qty(o) && qty(o) <= const(nbbo_qty),
    place_notional(nbbo_px * qty(o)))]);

Dynamic Percent-of-SpreadFull Template

Express willingness to trade different quantities at different percentages of spread (with 0% meaning near touch, 50% meaning midpoint, and 100% meaning far touch).

reason
let computed_limit (tier) = switch (o.order.side) {
    | Buy => nbo - (tier.pct_of_spread / 100) * (nbo - nbb);
    | Sell => nbb + (tier.pct_of_spread / 100) * (nbo - nbb);
};

Ok (List.map(
    tier => { subject_to(
        qty(o) >= const(tier.min_qty) && qty(o) <= const(tier.max_qty),
        place_notional(computed_limit(tier) * qty(o)))
    },
    arg.bidder_data.tiers
));

Target Portfolio CompositionFull Template

Trade components of a given basket of securities simultaneously while enforcing specific quantity ratios between securities.

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

Ok ([subject_to(all_eq(weighted_qtys),
    place_orders(arg.orders))]);

Net Price BasketFull Template

Buy and/or sell a mix of securities in single "unit" increments and at a net price per unit, where "unit" is a desired fill quantity ratio across securities.

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

Ok ([subject_to(all_eq(weighted_qtys),
    place_basket(
        sum_map(o => o.data.weighting * qty(o), arg.orders),
        arg.bidder_data.unit_net_price,
        arg.bidder_data.max_units
    ))
]);

Pair SpreadFull Template

Trade two securities subject to a constraint that the price spread between them is greater than some amount.

reason
let spread = abs(mid(a) - mid(b));

Ok ([subject_to(const(spread) >= const(arg.bidder_data.spread),
    place_notional(mid(a) * qty(a) + mid(b) * qty(b)))]);

Basket of substitutesFull Template

Buy or sell a basket of stocks (e.g. an industry / sector), with indifference as to symbol-wise composition of the basket.

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

Ok ([subject_to(total_notional <= const(arg.bidder_data.notional_max),
    place_orders(arg.orders))
]);

ETF or Components (Any Combination)Full Template

Trade either the ETF and/or its underlying components, minimizing total cost.

reason
let etf = List.filter(o => o.data.sec_type == ETF, orders);
let components = List.filter(o => o.data.sec_type == Component, orders);

let weighted_components = List.map(o => 
    qty_order(o.order) / o.data.weighting, orders);
let within_max_notional = sum_map(o => 
    o.order.price * (qty(o)), etf @ components) <= const(max_notional);

Ok ([subject_to(all_eq(weighted_components) && within_max_notional,
    place_orders(etf @ components))
]);

Single Stock HedgingFull Template

Given a list of securities with weightings representing the degree to which they hedge each other, execute a single trade in some combination of those securities.

reason
let abs_weighted_qty = sum_map(o => 
    abs(o.data.hedge_qty_coeff * qty(o)), orders);
let hedge_adjusted_qty = sum_map(o => 
    o.data.hedge_qty_coeff * qty(o), orders);
let hedging_constraint =
    abs_weighted_qty == target_hedge_ratio * hedge_adjusted_qty;

Ok ([subject_to(hedging_constraint,
    place_orders(arg.orders))]);

Weighted Long/Short PairFull Template

Buy one security and sell another, ensuring that each is filled in the desired ratio (share quantity).

reason
let weighted_qty_a = a.data.weighting * qty(a);
let weighted_qty_b = b.data.weighting * qty(b);

Ok ([subject_to(weighted_qty_a - weighted_qty_b == const(0),
    place_orders([a,b]))]);

Beta Neutral ExecutionFull Template

seek a trade that is *approximately* beta-neutral, meaning that we can tolerate some deviation from exact equality.

reason
/* proportional change in beta (net-beta divided by gross) within some tolerance */
let beta_constraint = (
    /* rearrange terms as linear: -tol <= net/abs <= tol*/
    arg.bidder_data.beta_tol * beta_net + beta_abs >= const(0) &&
    arg.bidder_data.beta_tol * beta_net - beta_abs <= const(0)
);
Ok ([subject_to(beta_constraint, place_orders(arg.orders))]);

Dollar Neutral PairFull Template

Trade two securities such that the expected notional amount bought in one is equal to the expected notional amount sold in the other.

reason
let net_notional = dollars_filled(a) - dollars_filled(b);
let dollar_neutral = abs(net_notional) <= const(my_tolerance);

/* Place orders if and only if apprx dollar neutral */
Ok ([subject_to(dollar_neutral,
    place_orders([a,b]))]);

Try out any of these in the Auction Simulator by replacing the simulation's existing bidder logic and data type with those of a given template. Tune the runtime arguments and auction parameters to your liking, and run the auction to see results.

If you're interested in using any of these in production, please feel free to reach out to us at [email protected] to discuss how we can help.