Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- **Trade: estimate multi-leg option trading info** — new `TradeContext.estimate_multileg_available_quantity` (`POST /v1/trade/estimate/multileg`) estimating a US option combination strategy's maximum tradable quantity and margin impact before submitting. Takes `side`, `order_type`, `submitted_quantity`, `strategy` (`MultiLegStrategy`), the combination `legs` (each an `EstimateMultiLegOrderLeg` — symbol plus a reserved `side`), and an optional `submitted_price`; returns `max_open_qty`, `unit_margin`, `initial_margin_change` and `maintenance_margin_change`. Unlike the single-leg `estimate_max_purchase_quantity` (a `GET` with scalar query params), the multi-leg estimate carries a `legs` array so it is a `POST` with a JSON body, matching the multi-leg submit endpoint. Added across the Rust core (async + blocking) and mirrored to the C, C++, Java, Node.js and Python layers; the separate Go SDK is tracked separately.

## [5.2.0] - 2026-09-30

### Added
Expand Down
4 changes: 4 additions & 0 deletions c/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ cpp_compat = true
"CPushGridOrderChanged" = "lb_push_grid_order_changed_t"
"CEstimateMaxPurchaseQuantityOptions" = "lb_estimate_max_purchase_quantity_options_t"
"CEstimateMaxPurchaseQuantityResponse" = "lb_estimate_max_purchase_quantity_response_t"
"CEstimateMultiLegOrderLeg" = "lb_estimate_multileg_order_leg_t"
"CEstimateMultiLegAvailableQuantityOptions" = "lb_estimate_multileg_available_quantity_options_t"
"CEstimateMultiLegAvailableQuantityResponse" = "lb_estimate_multileg_available_quantity_response_t"
"CCreateWatchlistGroup" = "lb_create_watchlist_group_t"
"CUpdateWatchlistGroup" = "lb_update_watchlist_group_t"
"CSecuritiesUpdateMode" = "lb_securities_update_mode_t"
Expand Down Expand Up @@ -514,6 +517,7 @@ include = [
# TradeContext: grid trading push (stays on trade side)
"CPushGridOrderChanged",
"CEstimateMaxPurchaseQuantityResponse",
"CEstimateMultiLegAvailableQuantityResponse",
"CSecurityCalcIndex",
"CWarrantInfo",
"CQuotePackageDetail",
Expand Down
85 changes: 85 additions & 0 deletions c/csrc/include/longbridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -4958,6 +4958,57 @@ typedef struct lb_estimate_max_purchase_quantity_options_t {
bool fractional_shares;
} lb_estimate_max_purchase_quantity_options_t;

/**
* A leg of a multi-leg combination for the pre-trade estimate
*/
typedef struct lb_estimate_multileg_order_leg_t {
/**
* Option or underlying-stock symbol, in `ticker.region` format
* (e.g. `QQQ260731C764000.US`)
*/
const char *symbol;
/**
* Leg order side (reserved; not in use for now) (can be null)
*/
const enum lb_order_side_t *side;
} lb_estimate_multileg_order_leg_t;

/**
* Options for estimating a multi-leg option combination's tradable quantity
* and margin impact
*/
typedef struct lb_estimate_multileg_available_quantity_options_t {
/**
* Order side of the combination
*/
enum lb_order_side_t side;
/**
* Order type
*/
enum lb_order_type_t order_type;
/**
* Submitted quantity (number of combinations)
*/
const struct lb_decimal_t *submitted_quantity;
/**
* Multi-leg strategy
*/
enum CMultiLegStrategy strategy;
/**
* Legs of the combination
*/
const struct lb_estimate_multileg_order_leg_t *legs;
/**
* Number of legs
*/
uintptr_t num_legs;
/**
* Submitted price (required for limit order types such as `LO`) (can be
* null)
*/
const struct lb_decimal_t *submitted_price;
} lb_estimate_multileg_available_quantity_options_t;

/**
* Active subscription for a security
*/
Expand Down Expand Up @@ -9069,6 +9120,29 @@ typedef struct lb_estimate_max_purchase_quantity_response_t {
const struct lb_decimal_t *margin_max_qty;
} lb_estimate_max_purchase_quantity_response_t;

/**
* Response for estimating a multi-leg option combination's tradable quantity
* and margin impact
*/
typedef struct lb_estimate_multileg_available_quantity_response_t {
/**
* Maximum open quantity of the combination
*/
const struct lb_decimal_t *max_open_qty;
/**
* Margin required per combination unit
*/
const struct lb_decimal_t *unit_margin;
/**
* Change of the initial margin after placing the order
*/
const struct lb_decimal_t *initial_margin_change;
/**
* Change of the maintenance margin after placing the order
*/
const struct lb_decimal_t *maintenance_margin_change;
} lb_estimate_multileg_available_quantity_response_t;

/**
* Security calc index response
*/
Expand Down Expand Up @@ -16466,6 +16540,17 @@ void lb_trade_context_estimate_max_purchase_quantity(const struct lb_trade_conte
lb_async_callback_t callback,
void *userdata);

/**
* Estimate a multi-leg option combination's maximum tradable quantity and
* margin impact before submitting the order.
*
* @param[in] opts Options for the multi-leg estimate request
*/
void lb_trade_context_estimate_multileg_available_quantity(const struct lb_trade_context_t *ctx,
const struct lb_estimate_multileg_available_quantity_options_t *opts,
lb_async_callback_t callback,
void *userdata);

/**
* Create a decimal value with a 64 bit `m` representation and corresponding
* `e` scale.
Expand Down
63 changes: 56 additions & 7 deletions c/src/trade_context/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use longbridge::{
TradeContext,
trade::{
AttachedOrderType, CancelOrderOptions, EstimateMaxPurchaseQuantityOptions,
EstimateMultiLegAvailableQuantityOptions, EstimateMultiLegOrderLeg,
GetAllExecutionsOptions, GetCashFlowOptions, GetFundPositionsOptions,
GetHistoryExecutionsOptions, GetHistoryOrdersOptions, GetOrderDetailOptions,
GetStockPositionsOptions, GetTodayExecutionsOptions, GetTodayOrdersOptions, PushEvent,
Expand All @@ -23,13 +24,15 @@ use crate::{
types::{
CAccountBalanceOwned, CAllExecutionsResponseOwned, CCashFlowOwned,
CEstimateMaxPurchaseQuantityOptions, CEstimateMaxPurchaseQuantityResponseOwned,
CExecutionOwned, CFundPositionsResponseOwned, CGetAllExecutionsOptions,
CGetCashFlowOptions, CGetFundPositionsOptions, CGetHistoryExecutionsOptions,
CGetHistoryOrdersOptions, CGetStockPositionsOptions, CGetTodayExecutionsOptions,
CGetTodayOrdersOptions, CMarginRatioOwned, COrderDetailOwned, COrderOwned,
CPushGridOrderChanged, CPushGridOrderChangedOwned, CPushOrderChanged,
CPushOrderChangedOwned, CReplaceOrderOptions, CStockPositionsResponseOwned,
CSubmitMultiLegOrderOptions, CSubmitOrderOptions, CSubmitOrderResponseOwned,
CEstimateMultiLegAvailableQuantityOptions,
CEstimateMultiLegAvailableQuantityResponseOwned, CExecutionOwned,
CFundPositionsResponseOwned, CGetAllExecutionsOptions, CGetCashFlowOptions,
CGetFundPositionsOptions, CGetHistoryExecutionsOptions, CGetHistoryOrdersOptions,
CGetStockPositionsOptions, CGetTodayExecutionsOptions, CGetTodayOrdersOptions,
CMarginRatioOwned, COrderDetailOwned, COrderOwned, CPushGridOrderChanged,
CPushGridOrderChangedOwned, CPushOrderChanged, CPushOrderChangedOwned,
CReplaceOrderOptions, CStockPositionsResponseOwned, CSubmitMultiLegOrderOptions,
CSubmitOrderOptions, CSubmitOrderResponseOwned,
},
},
types::{CCow, CVec, ToFFI, cstr_array_to_rust, cstr_to_rust, slice_from_raw_parts},
Expand Down Expand Up @@ -873,3 +876,49 @@ pub unsafe extern "C" fn lb_trade_context_estimate_max_purchase_quantity(
Ok(resp)
});
}

/// Estimate a multi-leg option combination's maximum tradable quantity and
/// margin impact before submitting the order.
///
/// @param[in] opts Options for the multi-leg estimate request
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_trade_context_estimate_multileg_available_quantity(
ctx: *const CTradeContext,
opts: *const CEstimateMultiLegAvailableQuantityOptions,
callback: CAsyncCallback,
userdata: *mut c_void,
) {
let ctx_inner = (*ctx).ctx.clone();
let side = (*opts).side.into();
let order_type = (*opts).order_type.into();
let submitted_quantity = (*(*opts).submitted_quantity).value;
let strategy = (*opts).strategy.into();
let legs = slice_from_raw_parts((*opts).legs, (*opts).num_legs)
.iter()
.map(|leg| {
let mut l = EstimateMultiLegOrderLeg::new(cstr_to_rust(leg.symbol));
if !leg.side.is_null() {
l = l.side((*leg.side).into());
}
l
})
.collect::<Vec<_>>();
let mut opts2 = EstimateMultiLegAvailableQuantityOptions::new(
side,
order_type,
submitted_quantity,
strategy,
legs,
);
if !(*opts).submitted_price.is_null() {
opts2 = opts2.submitted_price((*(*opts).submitted_price).value);
}
execute_async(callback, ctx, userdata, async move {
let resp: CCow<CEstimateMultiLegAvailableQuantityResponseOwned> = CCow::new(
ctx_inner
.estimate_multileg_available_quantity(opts2)
.await?,
);
Ok(resp)
});
}
93 changes: 87 additions & 6 deletions c/src/trade_context/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use longbridge::{
trade::{
AccountBalance, AllExecutionsResponse, AttachedOrderDetail, AttachedOrderType, BalanceType,
CashFlow, CashFlowDirection, CashInfo, ContractDirection,
EstimateMaxPurchaseQuantityResponse, Execution, FrozenTransactionFee, FundPosition,
FundPositionChannel, FundPositionsResponse, MarginRatio, MultiLegInfo, MultiLegOrderLeg,
MultiLegPosition, MultiLegStrategy, Order, OrderChargeDetail, OrderChargeFee,
OrderChargeItem, OrderDetail, OrderHistoryDetail, OrderSide, OrderStatus, OrderTag,
OrderType, PushGridOrderChanged, PushOrderChanged, StockPosition, StockPositionChannel,
StockPositionsResponse, SubmitOrderResponse, TimeInForceType,
EstimateMaxPurchaseQuantityResponse, EstimateMultiLegAvailableQuantityResponse, Execution,
FrozenTransactionFee, FundPosition, FundPositionChannel, FundPositionsResponse,
MarginRatio, MultiLegInfo, MultiLegOrderLeg, MultiLegPosition, MultiLegStrategy, Order,
OrderChargeDetail, OrderChargeFee, OrderChargeItem, OrderDetail, OrderHistoryDetail,
OrderSide, OrderStatus, OrderTag, OrderType, PushGridOrderChanged, PushOrderChanged,
StockPosition, StockPositionChannel, StockPositionsResponse, SubmitOrderResponse,
TimeInForceType,
},
};
use time::OffsetDateTime;
Expand Down Expand Up @@ -2769,6 +2770,86 @@ impl ToFFI for CEstimateMaxPurchaseQuantityResponseOwned {
}
}

/// A leg of a multi-leg combination for the pre-trade estimate
#[repr(C)]
pub struct CEstimateMultiLegOrderLeg {
/// Option or underlying-stock symbol, in `ticker.region` format
/// (e.g. `QQQ260731C764000.US`)
pub symbol: *const c_char,
/// Leg order side (reserved; not in use for now) (can be null)
pub side: *const COrderSide,
}

/// Options for estimating a multi-leg option combination's tradable quantity
/// and margin impact
#[repr(C)]
pub struct CEstimateMultiLegAvailableQuantityOptions {
/// Order side of the combination
pub side: COrderSide,
/// Order type
pub order_type: COrderType,
/// Submitted quantity (number of combinations)
pub submitted_quantity: *const CDecimal,
/// Multi-leg strategy
pub strategy: CMultiLegStrategy,
/// Legs of the combination
pub legs: *const CEstimateMultiLegOrderLeg,
/// Number of legs
pub num_legs: usize,
/// Submitted price (required for limit order types such as `LO`) (can be
/// null)
pub submitted_price: *const CDecimal,
}

/// Response for estimating a multi-leg option combination's tradable quantity
/// and margin impact
#[repr(C)]
pub struct CEstimateMultiLegAvailableQuantityResponse {
/// Maximum open quantity of the combination
pub max_open_qty: *const CDecimal,
/// Margin required per combination unit
pub unit_margin: *const CDecimal,
/// Change of the initial margin after placing the order
pub initial_margin_change: *const CDecimal,
/// Change of the maintenance margin after placing the order
pub maintenance_margin_change: *const CDecimal,
}

#[derive(Debug)]
#[repr(C)]
pub struct CEstimateMultiLegAvailableQuantityResponseOwned {
max_open_qty: CDecimal,
unit_margin: CDecimal,
initial_margin_change: CDecimal,
maintenance_margin_change: CDecimal,
}

impl From<EstimateMultiLegAvailableQuantityResponse>
for CEstimateMultiLegAvailableQuantityResponseOwned
{
fn from(value: EstimateMultiLegAvailableQuantityResponse) -> Self {
CEstimateMultiLegAvailableQuantityResponseOwned {
max_open_qty: value.max_open_qty.into(),
unit_margin: value.unit_margin.into(),
initial_margin_change: value.initial_margin_change.into(),
maintenance_margin_change: value.maintenance_margin_change.into(),
}
}
}

impl ToFFI for CEstimateMultiLegAvailableQuantityResponseOwned {
type FFIType = CEstimateMultiLegAvailableQuantityResponse;

fn to_ffi_type(&self) -> Self::FFIType {
CEstimateMultiLegAvailableQuantityResponse {
max_open_qty: self.max_open_qty.to_ffi_type(),
unit_margin: self.unit_margin.to_ffi_type(),
initial_margin_change: self.initial_margin_change.to_ffi_type(),
maintenance_margin_change: self.maintenance_margin_change.to_ffi_type(),
}
}
}

// ── Grid trading push (stays on the trade context)
// ──────────────────────────────────

Expand Down
7 changes: 7 additions & 0 deletions cpp/include/trade_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ class TradeContext
AsyncCallback<TradeContext, EstimateMaxPurchaseQuantityResponse> callback)
const;

/// Estimate a multi-leg option combination's maximum tradable quantity and
/// margin impact before submitting the order
void estimate_multileg_available_quantity(
const EstimateMultiLegAvailableQuantityOptions& opts,
AsyncCallback<TradeContext, EstimateMultiLegAvailableQuantityResponse>
callback) const;

/// Set grid order changed callback, after receiving the grid order changed
/// event, it will call back to this function.
void set_on_grid_order_changed(
Expand Down
42 changes: 42 additions & 0 deletions cpp/include/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2444,6 +2444,48 @@ struct EstimateMaxPurchaseQuantityResponse
Decimal margin_max_qty;
};

/// A leg of a multi-leg combination for the pre-trade estimate
struct EstimateMultiLegOrderLeg
{
/// Option or underlying-stock symbol, in `ticker.region` format
/// (e.g. `QQQ260731C764000.US`)
std::string symbol;
/// Leg order side (reserved; not in use for now)
std::optional<OrderSide> side;
};

/// Options for estimating a multi-leg option combination's tradable quantity
/// and margin impact
struct EstimateMultiLegAvailableQuantityOptions
{
/// Order side of the combination
OrderSide side;
/// Order type
OrderType order_type;
/// Submitted quantity (number of combinations)
Decimal submitted_quantity;
/// Multi-leg strategy
MultiLegStrategy strategy;
/// Legs of the combination
std::vector<EstimateMultiLegOrderLeg> legs;
/// Submitted price (required for limit order types such as `LO`)
std::optional<Decimal> submitted_price;
};

/// Response for estimating a multi-leg option combination's tradable quantity
/// and margin impact
struct EstimateMultiLegAvailableQuantityResponse
{
/// Maximum open quantity of the combination
Decimal max_open_qty;
/// Margin required per combination unit
Decimal unit_margin;
/// Change of the initial margin after placing the order
Decimal initial_margin_change;
/// Change of the maintenance margin after placing the order
Decimal maintenance_margin_change;
};


/// Grid trading master-order changed message.
struct PushGridOrderChanged
Expand Down
Loading
Loading