Submit a native bracket order¶
A bracket is a single venue primitive that wraps three legs:
- An entry order (limit or stop) that opens the position.
- A take-profit child that closes at a price better than entry.
- A stop child that closes at a price worse than entry.
The venue ties them together: take-profit and stop only arm after entry fills, and the first child to fill cancels the other. Most modern derivatives venues (Bybit, OKX, Binance UM with "TP/SL on position") offer this as one API call.
flox's submitBracket exposes the same surface in the simulator.
You can hand-wire the three legs via OrderGroup for more
flexibility, but the bracket primitive is the right tool when you
just want the standard pattern with no extra wiring.
Configure¶
A worked example (Python):
"""Submit a native bracket order (entry + take-profit + stop) and walk its states."""
import flox_py as flox
exec = flox.SimulatedExecutor()
# Buy at 100, take profit at 110, stop at 90.
exec.submit_bracket(bracket_id=1, symbol=1,
entry_side="buy", entry_type="limit",
entry_price=100.0, quantity=1.0,
tp_side="sell", tp_type="limit", tp_price=110.0,
stop_side="sell", stop_type="stop_market",
stop_trigger_price=90.0)
print("state after submit:", exec.bracket_state(1))
# Drop to 100; entry fills, TP + stop are now armed.
exec.on_bar(1, 100.0)
print("state after entry fill:", exec.bracket_state(1))
# Rally to 110; TP fills, stop is cancelled.
exec.on_bar(1, 110.0)
print("state after take-profit:", exec.bracket_state(1))
import flox_py as flox
exec = flox.SimulatedExecutor()
exec.submit_bracket(bracket_id=1, symbol=1,
entry_side="buy", entry_type="limit",
entry_price=50000.0, quantity=0.1,
tp_side="sell", tp_type="limit", tp_price=51000.0,
stop_side="sell", stop_type="stop_market",
stop_trigger_price=49500.0)
state = exec.bracket_state(1)
import { SimulatedExecutor } from "@flox-foundation/flox";
const exec = new SimulatedExecutor();
exec.submitBracket({
bracketId: 1,
entrySide: "buy", entryType: "limit",
entryPrice: 50000.0, quantity: 0.1,
tpSide: "sell", tpType: "limit", tpPrice: 51000.0,
stopSide: "sell", stopType: "stop_market",
stopTriggerPrice: 49500.0,
});
const state = exec.bracketState(1);
State machine¶
The bracket transitions through five states:
| state | meaning |
|---|---|
| pending_entry | entry submitted; not filled yet |
| entry_filled | entry fully filled; TP + stop are now armed |
| tp_filled | take-profit filled; stop was cancelled |
| stop_filled | stop filled / triggered; take-profit was cancelled |
| canceled | bracket cancelled before children resolved |
cancelBracket cancels every leg that is still live, regardless of
state.
OrderId derivation¶
To keep the API simple, leg OrderIds are derived from the
bracketId:
- entry:
bracketId * 3 + 0 - take-profit:
bracketId * 3 + 1 - stop:
bracketId * 3 + 2
If your strategy assigns OrderIds manually, allocate bracketIds
out of a space that doesn't collide with the manually-issued
OrderIds. The simulator does not currently enforce this.
Notes¶
- The child-arm policy is set with
set_bracket_child_arm_mode(mode)/setBracketChildArmMode(mode)on theSimulatedExecutor:'on_full_fill'(default) — TP + stop are armed once, on the full entry fill, each sized to the entry quantity. A partially filled entry arms nothing.'on_partial_fill'— children are armed and resized incrementally on every partial entry fill, so a partial entry produces proportionally smaller children.
- Re-entering after a partial fill on the entry leg is left to higher-level strategies.
- Bracket is not chainable: you cannot submit a new bracket whose
entry order id collides with an existing one. Reuse a fresh
bracketIdper attempt. - Trailing-stop variant is filed as a follow-up — it needs trailing logic in the engine, not just composition.