Skip to content

Add contingent orders support: OCO, OTO and OTOCO order lists - #85

Merged
Martin-Molinero merged 6 commits into
masterfrom
feature-contingent-orders
Sep 25, 2026
Merged

Martin-Molinero merged 6 commits into
masterfrom
feature-contingent-orders

Conversation

@Martin-Molinero

@Martin-Molinero Martin-Molinero commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

Description

Places contingent order sets through the spot order list endpoints (OCO, OTO and OTOCO):

  • Each order of a list is sent with its own client order id, its lean order id plus a suffix unique to the placement (they have to be unique among the open orders), and the list reports are matched to the lean orders by it.
  • Expirations driven by the list are mapped to cancellations. The spot expiry reason (eR, like OCO_TRIGGER) is mapped apart from the futures one (er): the names only differ in case and it was read as the futures enum, failing to parse the report.
  • The pending orders are reported as triggered once the working order fills.
  • The contingencies of open orders are rebuilt from their order list through OrderContingency.Trigger/Relate.
  • Only spot supports order lists: futures and Binance US reject contingent orders through their brokerage models.

Related Issue

Requires QuantConnect/Lean#9828, which adds contingent orders to Lean. This PR builds against it, so CI will pass once it's merged and released.

Motivation and Context

Support for Lean contingent orders (OCO, OTO, OUO, brackets) using the brokerage's native functionality, so the sets are placed atomically and managed on the brokerage side.

Requires Documentation Change

The supported contingent order shapes of this brokerage, see its brokerage model in Lean.

How Has This Been Tested?

  • Unit tests of the contingent orders support: order list request building.
  • Live on the Binance spot testnet (ETHBTC), through the Lean BrokerageTests contingent orders suite (BinanceBrokerageTests.ContingentOrders.cs):
Case OCO (limit + stop limit) OTO OTOCO
Resting: cancel the working order, the pending ones expire as canceled ✅ ✅ ✅
Resting: open orders rebuilt with the same contingencies ✅ ✅ ✅
Working limit fills right away, pending orders released ✅ ✅
  • Live on the testnet (BTCUSDT), a bracket whose exit fills: the entry fills, the stop loss triggers and fills, the take profit expired by the list is reported canceled.
  • The test prices are within PERCENT_PRICE_BY_SIDE of the average price, otherwise the lists are rejected (-1013), which is reported as invalid.
  • The spot fixture's algorithm transactions now give the brokerage access to the test orders, which it needs to find the orders of its stream events.
  • Existing order tests on the testnet: market and limit orders pass. Stop limit orders are rejected by PERCENT_PRICE_BY_SIDE with the shared fixture prices, a buy stop at the high price is beyond the allowed range; the contingent tests cover stop limit orders on both sides.
  • Futures and Binance US have no order lists (Binance US only an older OCO endpoint), their brokerage models reject contingent orders.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • Refactor (non-breaking change which improves implementation)
  • Performance (non-breaking change which improves performance. Please add associated performance test and results)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Non-functional change (xml comments/documentation/etc)

Checklist:

  • My code follows the code style of this project.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • My branch follows the naming convention bug-<issue#>-<description> or feature-<issue#>-<description>

🤖 Generated with Claude Code

Places contingent order sets through the spot order list endpoints,
maps list driven expirations to cancellations, reports the pending
orders once the working order fills and rebuilds the contingencies of
open orders from their order list.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Martin-Molinero and others added 5 commits September 24, 2026 13:05
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The prices are within the allowed range around the average price, and the brokerage finds the test orders of its order events
through the algorithm transactions. The trigger tests use a working limit order which fills right away.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each order of the list is sent with a client order id made of its lean order id and a suffix unique to the placement, the reports
are matched by it instead of by side and price.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…st expiries failed to parse

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Martin-Molinero
Martin-Molinero merged commit 45f2973 into master Sep 25, 2026
1 check failed
@Martin-Molinero
Martin-Molinero deleted the feature-contingent-orders branch September 25, 2026 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant