Expressive Bidding
Portfolios
Note: these templates are meant to demonstrate bidder logic code in a simulated environment. Templates may require modification for use in production to meet your specific trading objectives and standards.
Target Portfolio Composition
Objective: Trade components of a given basket of securities simultaneously while enforcing specific quantity ratios between securities.
Each component
is a security defined in bidder data as a symbol, side, and weighting. Symbol and side are used to map bidder data to corresponding target orders. Weighting is used to enforce execution in atomic "slices", i.e., single increments with each component filled in the desired ratio.
Bidder data:
type component = {symbol: string, side: side, weighting: int}
type weightings = {basket: list(component)}
/* example; provided over FIX */
let ratios_data_sample = {
basket: [
{symbol: "ASDF", side: Sell, weighting: 6},
{symbol: "QWER", side: Sell, weighting: 3},
{symbol: "ZXCV", side: Sell, weighting: 1}
]
};
Bidder Logic:
let weighted_basket: bidder(_) = (arg, ~mkt) => {
open Bid;
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 Basket
Objective: 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.
Instead of specifying a limit price for each security in the basket, we specify a limit amount we would need to pay or receive for a combined "unit" of the securities in the basket. Here the basket_component
is identical to the component
type defined above. The price is now represented as unit_net_price
in the basket_data
record.
Bidder data:
type basket_component = {symbol: string, side: side, weighting: int};
type basket_data = {
weightings: list(basket_component),
max_units: int,
unit_net_price: with_side(int)
};
/* example; provided over FIX */
let basket_data_sample = {
weightings: [
{ symbol: "ASDF", side: Sell, weighting: 7 },
{ symbol: "QWER", side: Buy, weighting: 3 },
{ symbol: "ZXCV", side: Sell, weighting: 1 }
],
max_units: 1000,
unit_net_price: (Buy, 2309)
};
Bidder Logic:
let net_price_basket: bidder(_) = (arg, ~mkt) => {
open Bid;
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
))
]);
}
Basket of substitutes
Objective: Buy or sell a basket of stocks (e.g. an industry / sector), with indifference as to symbol-wise composition of the basket.
We can express willingness to accept up to the entire desired notional amount in each stock, while ensuring the total quantity filled remains below the desired amount. In this example, we assume true indifference between each security. For examples that incorporate a measure of correlation (beta
) with the desired sector (or other factor), see the examples in hedging.ipynb
.
Bidder data:
type sector_data = {
symbols: list(string),
notional_max: int,
side: side
};
/* example; provided over FIX */
let sector_data_sample = {
symbols: ["FB", "AMZN", "AAPL", "NFLX", "GOOG"],
notional_max: 100000,
side: Buy
};
Bidder Logic:
let sector_exposure: bidder(_) = (arg, ~mkt) => {
open Bid;
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))
]);
}
Dollar Neutral Basket
Objective: Execute a basket of stocks in any dollar-neutral combination, within some tolerance (provided via FIX along with component orders).
Given a set of target orders to buy and sell a basket of securities, enforce a constraint that any executions must be dollar neutral within a given tolerance
bound provided at runtime as bidder data.
type dollar_neutral_data = {
tolerance: price,
};
let dollar_neutral_wave: bidder(_) = (arg, ~mkt) => {
let side_coeff = fun | Buy => 1 | Sell => -1;
open Bid;
/* 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))]);
};
Next Steps
- Try this code in a simulated auction. Load the simulator walkthrough, and replace the bidder logic and data type with an example of your choosing. Tune the runtime arguments and auction parameters to your liking, and run the auction to see results.
- Modify to your needs and reach out to the OneChronos team at [email protected] for usage in production.