AI Guide Map
Stop Loss using Conditional Algo Orders with Binance USD-M APIs & WebSockets
Binance USD-M Algo conditionals are one way to implement stop losses, trailing stops, and explicit conditional take-profit. This guide walks through request shapes, clientAlgoId context lookup, and private stream reconciliation. Ordinary fixed TP should use regular LIMIT orders instead.
Before building Binance request objects, verify the current SDK method names, request types, required fields, and Binance USD-M Algo rules from the installed package and docs.
Default Scope
- Runtime: Node.js LTS
- Recommended language: TypeScript
- Package: binance
- Product: Binance USD-M Futures
- Implement execution for when EXECUTION_MODE=DEMO, TESTNET, or LIVE is enabled and all gates pass, but keep that path disabled by default.
- EXECUTION_MODE=DRY_RUN_PRIVATE is the default local mode for bounded USD-M conditional algo order request candidates.
- Credentials: scoped API keys from environment variables only
Resources
Start with the task-specific resources below, then use SDK and exchange docs to verify exact method names, request fields, topics, and product rules.
Implementation Steps
Follow these in order; use the linked artifacts only where they clarify the current step.
1. Verify the current Algo surface
Before coding, inspect the installed package declarations and source for the USD-M client, Algo methods, request types, response types, user-data update types, and order ID utilities.
- Expected methods include submitNewAlgoOrder(...), getOpenAlgoOrders(...), and cancelAlgoOrder(...).
- Expected types include FuturesNewAlgoOrderParams, FuturesAlgoConditionalOrderTypes, FuturesAlgoOrderResponse, and formatted futures Algo user-data update payloads.
- Record the latest installed package version as metadata plus the exact types checked in the project README.
2. Pick the right order path
For USD-M exits, do not assume every TP/SL belongs on the Algo Service. Check whether the current task belongs on regular order methods or the futures Algo path.
- Use clientAlgoId for USD-M Algo conditionals.
- Treat clientAlgoId as a Binance custom order ID. Generate it with the same SDK order ID utilities as newClientOrderId, preferably generateNewOrderId(...) or client.generateNewOrderId(), and satisfy the same prefix, length, and character checks.
- Keep regular order newClientOrderId handling separate from Algo clientAlgoId handling.
- Fixed TP and DCA use regular LIMIT orders. A DCA LIMIT may fill immediately at limit-or-better or rest at the submitted limit price; both are normal LIMIT behavior. Do not add maker-only, post-only, mark-price, quote-freshness, marketability, or forced-maker gates unless explicitly configured.
- Track active Algo exits separately from regular open orders before deciding whether an exit already exists.
3. Build valid conditional requests
EXECUTION_MODE=DRY_RUN_PRIVATE is the default local mode for sanitized USD-M conditional algo order request objects. Validate the quantized price, quantity, notional, positionSide, and field combinations before any EXECUTION_MODE=DEMO, TESTNET, or LIVE submission path can call Binance.
- For an explicit conditional take-profit, verify and use the current valid Algo type such as TAKE_PROFIT.
- Do not use Algo TAKE_PROFIT for the default fixed TP slot in the position manager.
- For a close-position stop-market, use STOP_MARKET with closePosition=true and omit quantity and reduceOnly.
- Do not use strategy-language values such as TAKE_PROFIT_LIMIT unless current Binance docs and package types explicitly support them.
4. Validate mutually exclusive fields
The costliest mistakes here are field mixes that compile but Binance rejects. Add local validation before building the final request.
- Reject closePosition=true with quantity.
- Reject closePosition=true with reduceOnly unless current docs say the combination is valid.
- Omit undefined optional fields under strict TypeScript settings.
- Keep workingType and priceProtect explicit when the strategy depends on trigger semantics.
5. Use trusted state before replacing exits
Private user-data events are the normal way to keep local account state current. Use REST hydration only for startup, reconnect, scoped recovery, missing prerequisites, unknown outcomes, or conflicting evidence before creating, replacing, or cleaning up exits.
- Use getOpenAlgoOrders(...) during startup, reconnect, and scoped recovery reconciliation; do not call it just because a healthy private user-data event arrived.
- When private ALGO_UPDATE and matching position or account-state evidence have been ingested, plan from the private-stream account state without REST hydration.
- Classify app-owned Algo orders from clientAlgoId context lookup plus persisted metadata.
- Only open app-owned orders and pending confirmations should block new submissions for a logical slot. Historical cancelled, expired, rejected, and filled orders are metadata.
- For the default manager, clientAlgoId follows the custom order ID rule: do not encode product, symbol, role, side, step, order kind, lifecycle, generation, recovery state, or other strategy state in the ID text.
- If scoped open Algo hydration reports an active order while recent history has an older terminal row for the same clientAlgoId, keep the active state and avoid replacement.
6. Process private updates once
Formatted user-data events should update local state from typed fields, not from whole-object JSON serialization.
- Prefer formattedUserDataMessage for private state processing.
- For formatted ALGO_UPDATE, read clientAlgoId, symbol, positionSide, orderType, and status from algoOrder.*, not from root fields.
- If formattedMessage is also inspected, derive and test a stable dedupe key before mutating order state.
- If an Algo update arrives while reconciliation, buffered replay, planning, or submission is active, defer it to the active workflow instead of starting a competing planner.
- A user-data-triggered Algo replan should update from trusted private user-data state before planning. Use REST reconciliation only when startup, reconnect, sync-required, exception, rejection, unknown-state, or conflicting-state evidence makes local state untrusted. If a deferred work item contains only already-applied user-data events and no buffered events remain, skip the duplicate replan.
- Redact user-data wsKey values because they may contain listen-key-like material.
Dry-run request path
Module names can vary; keep this verified request-object flow intact.
- Load symbol, side, positionSide, quantity, trigger, price, and risk settings from config.
- Create USDMClient with strict optional-field and logging options verified from the installed package.
- During startup, reconnect, or scoped recovery, hydrate exchange filters and open Algo orders before first planning from untrusted local state.
- Build request objects in dry-run and validate field combinations locally.
- Log sanitized request summaries with clientAlgoId, role, symbol, side, and reason.
- Enable DEMO, TESTNET, or LIVE submission only after explicit configuration, scoped credentials, and reviewed code.
- After an accepted DEMO, TESTNET, or LIVE request, record pending confirmation state before waiting for private stream confirmation.
Request validation
The guide gives the shape to verify. The installed package types and Binance docs remain the authority for exact literals and field rules.
- Validate tick size, step size, quantity, and notional from hydrated USD-M filters.
- Reject strategy-language order types that are not present in the current Algo type union.
- Reject closePosition=true with quantity or reduceOnly unless current docs say otherwise.
- Use clientAlgoId for Algo conditionals and apply the same SDK generation, prefix, length, and character checks used for newClientOrderId.
User-data handling
Algo updates should reconcile existing state, not start a second overlapping planning pass.
- Classify formatted ALGO_UPDATE from eventType plus algoOrder.clientAlgoId, algoOrder.symbol, algoOrder.positionSide, algoOrder.orderType, and algoOrder.algoStatus.
- Choose formattedUserDataMessage or formattedMessage for private state processing; do not process both.
- After reconnect, enter recoveryRequired for the affected product, hydrate open Algo orders again, replay buffered events, then resume order mutations only when state is coherent.
Request examplesReference
These examples intentionally use variables rather than a hardcoded symbol. Verify the current package types before copying them into EXECUTION_MODE=DRY_RUN_PRIVATE, DEMO, TESTNET, or LIVE code.
Explicit conditional take-profit
Use this only when conditional TP behavior is required. Ordinary fixed TP belongs on regular LIMIT orders. Set useReduceOnly only after the current docs and package types validate reduceOnly for the account mode; otherwise omit it.
await usdm.submitNewAlgoOrder({
algoType: 'CONDITIONAL',
symbol,
side: exitSide,
positionSide,
type: 'TAKE_PROFIT',
timeInForce: 'GTC',
quantity,
price: takeProfitPrice,
triggerPrice: takeProfitTriggerPrice,
workingType: 'MARK_PRICE',
priceProtect: 'true',
...(useReduceOnly ? { reduceOnly: 'true' } : {}),
clientAlgoId,
});Close-position stop-market
For close-position stops, omit quantity and reduceOnly.
await usdm.submitNewAlgoOrder({
algoType: 'CONDITIONAL',
symbol,
side: exitSide,
positionSide,
type: 'STOP_MARKET',
triggerPrice: stopTriggerPrice,
workingType: 'MARK_PRICE',
priceProtect: 'true',
closePosition: 'true',
clientAlgoId,
});Reject these locally
These are examples of request shapes that should be blocked before a DEMO, TESTNET, or LIVE submission.
// Invalid for the current USD-M Algo Service:
type: 'TAKE_PROFIT_LIMIT'
// Invalid close-position combination:
{
closePosition: 'true',
quantity,
reduceOnly: 'true',
}Private user-data lifecycleReference
Use private stream events for account state, not for readiness shortcuts. A transport event can tell you the socket changed; it cannot prove local order state is safe to act on.
Choose one user-data processing path. If you handle formattedUserDataMessage, do not also process the same user-data payload from formattedMessage unless you add an explicit dedupe key. Do not infer stream failure from quiet private account-event traffic while the SDK transport is healthy; use SDK reconnecting, reconnected, and exception events for stream-health decisions.
| Event | Meaning | Readiness use | State use |
|---|---|---|---|
| open | The WebSocket transport opened. | Useful for transport health only. | Do not update account or order state from this event. |
| response | A command or subscription response was received. | Use for public subscription acknowledgements when applicable. | Rarely enough for listen-key stream state by itself. |
| formattedUserDataMessage | A formatted private account, order, fill, balance, position, or Algo event. | Not a readiness gate. | Preferred dedicated path for private state updates. |
| formattedMessage | A formatted message across public and private streams. | Useful for public streams and broad logging. | Use this or formattedUserDataMessage for private events, not both. |
| reconnecting | The SDK is reconnecting the socket. | Pause downstream live actions and keep buffering where possible. | Mark affected state not ready; do not plan from it until reconnect recovery finishes. |
| reconnected | The socket transport reconnected. | Require scoped resync before management resumes. | Mark local state stale until reconciliation finishes. |
| exception | The socket or listener reported an error. | Enter recoveryRequired for the affected product scope. | Log a sanitized summary and reconcile before acting. |
Fixture casesReference
Use these behavior checks in local replay or fixture tests before live order paths are trusted.
| Case | Purpose | Expected result |
|---|---|---|
| Valid explicit conditional take-profit | Proves the project builds the current take-profit Algo request shape. | The request uses TAKE_PROFIT, quantity, price, triggerPrice, clientAlgoId, and only account-mode-valid optional reduceOnly fields. |
| Valid close-position stop-market | Proves close-position stops avoid invalid field mixes. | The request uses STOP_MARKET, triggerPrice, closePosition=true, and omits quantity and reduceOnly. |
| Invalid strategy-language take-profit type | Proves stale order type names are caught locally. | TAKE_PROFIT_LIMIT is rejected before Binance can return an invalid order type error. |
| Invalid close-position field mix | Proves mutually exclusive fields are validated before submission. | closePosition=true with quantity or reduceOnly is rejected locally. |
| Scoped open Algo hydration | Proves startup, reconnect, or scoped recovery reconciliation sees existing app-owned exits. | Existing app-owned TP/SL Algo orders prevent duplicates unless replacement is explicitly required. |
| Scoped open Algo hydration with stale terminal history | Proves older terminal Algo history does not hide a currently open app-owned exit. | Current open Algo state wins, and the planner emits no duplicate replacement for that clientAlgoId. |
| Algo update during active workflow | Proves Algo confirmations do not start a competing planner during reconciliation, replay, planning, or submission. | The update is deferred until the active workflow can replay trusted private state, or run scoped recovery if trust is unresolved, then planning sees current open Algo state. |
| Algo user-data update | Proves private Algo updates reconcile app-owned state once. | The update is classified from typed fields and does not double-apply through two event handlers. |
Algo order invariants
- EXECUTION_MODE=DRY_RUN_PRIVATE is the default local mode for sanitized USD-M conditional algo order request objects.
- USD-M Algo conditionals use clientAlgoId, not newClientOrderId.
- Simple-manager clientAlgoId values follow the custom order ID rule; lifecycle/generation IDs are not the default.
- Limit take-profit and close-position stop-market requests must use the current package types and Binance field rules.
- closePosition=true must not be mixed with quantity or reduceOnly unless current docs explicitly allow it.
- At startup, reconnect, or scoped recovery, Algo open orders are hydrated separately from regular open orders on Binance.
- Private user-data events are processed once and redacted before logging.