> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trdrs.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Protect a position

> Puts a bracket on an open position: up to three take profits and three stop losses, laid out as pairs along the position, nearest the entry first, each pair's take profit and stop loss cancelling each other. Write it as `exits`. Distances are measured from the position's average entry, and every price must clear the market on the side it protects, since a stop already past the market would close the position at once. The bracket protects `qty` of the position, or all of it that no other bracket protects. Send `positionId` to protect one position alone, on an account that names its positions. A stop loss that moves itself, to breakeven or trailing, is moved by trdrs on every provider that holds a bracket. No hold on the account refuses a bracket on a position: it only closes the position it protects and cannot add exposure.

Send `entryOrderId` in place of a position to protect a resting entry before it fills: a working order of yours on the instrument that can open a position and has filled nothing. The bracket covers all of the entry and waits on it, as a bracket placed with its entry does, and each pair rests once the fill that opens its part of the position arrives. Its prices are measured from the entry's own price (a limit's limit, a stop's or a stop-limit's trigger) and lie on their side of it, and none is held to the market, since nothing is open yet. On the paper book, which holds a waiting bracket inside its entry, the entry is replaced with itself carrying the pairs in the same step that records the bracket, and rests afterwards under your `clientOrderId`: the answer's `entryOrderId` names it. A hold that refuses new entries refuses that replacement too.

Required key: Trading API key. A Connect app's backend can also send it with the app's API key, and a venue's backend with its Venue key, which carries `trader:trade`, each naming one of its own traders in `x-trdrs-trader`: it then runs on that trader's own accounts.



## OpenAPI

````yaml /api/openapi.json post /api/trading/exits
openapi: 3.1.0
info:
  title: trdrs Engine API
  version: 1.1.0
  description: >-
    ## API Reference


    Every route the trdrs engine serves, with what to send and what comes back.
    It covers market

    data and news, trading and account state for a trader's own software, the
    Connect API a Connect

    app's backend calls and the Connect dashboard's routes, Connect
    pre-registration, the statements

    that carry a Connect account between environments, the venue routes a prop
    firm or brokerage runs its

    accounts through, and a trader's own challenges.


    Start with the Quickstart for your first call. The API standards hold the
    rules every route

    shares: keys, errors, rate limits, idempotency, paging and streaming.
servers:
  - url: https://app.trdrs.co
    description: Production
  - url: /
    description: This engine
security: []
tags:
  - name: Venue platform preview
    description: >-
      Run your venue: its providers, instruments, conditions, groups, routes,
      stages, venue rules, keys, accounts, usage, balance receipts and webhooks.
      These routes are in preview. They are served on the sandbox to every
      venue, and production access is arranged when a venue qualifies. Every
      route here under `/api/venues/` takes a Venue key. The back office reaches
      the same routes under `/api/back-office/` with a verified owner’s session,
      because the Venue key stays on your server, and both run the same checks.
      The team’s routes under `/api/organizations` serve the back office and the
      Connect dashboard alike, since one organization can run venues and Connect
      apps with one team. Changes to these routes are additive only from here
      on.
  - name: Market data
    description: >-
      Search and look up symbols, read price history and quotes, check the
      server clock, and stream live bars. Crypto prices come from each
      provider’s public feed. Futures prices are licensed to each user and
      stream only from that trader’s own futures source: a login on their
      venue’s production Rithmic system, under their own market data
      subscription. A Rithmic Test login carries no market data. Without a
      source, a futures request answers 503 `feed_requires_connection` and the
      symbol search lists no futures.
  - name: News
    description: >-
      Market news and the economic calendar, from licensed and open sources,
      tagged with futures roots as they arrive. The content is the same for
      everyone, and these routes admit the same callers as market data: a
      licensed origin, a session or a Trading API key. Page headlines by publish
      time, filter them by instrument root, and stream them live over
      server-sent events. Thumbnails come through the image route.
  - name: Trading
    description: >-
      Place, change and cancel orders, protect a position with a bracket and
      change or withdraw it, and close or flatten positions. Every call that
      places an order takes a `clientOrderId` as its idempotency key.
  - name: Account
    description: >-
      Read an account a trader can trade: its balance, positions, working
      orders, fills, profit and loss history, and the live account stream. You
      don’t create accounts here. A trader connects their own account at a
      provider, or opens their own Demo on the paper book, in the trdrs app. A
      venue issues accounts on the paper book with Issue an account into a
      group, and a venue pre-registers accounts at a provider through Connect
      with Pre-register a trader’s account.
  - name: Connect
    description: >-
      Connect is the account picker a trader opens: in the trdrs app, where it
      lists the built-in providers and every listed venue, and in hosted Connect
      on a Connect app’s website, where it lists the tiles the app chose. The
      pre-registration routes let a venue fill Connect in the trdrs app ahead of
      time, for a trader who signs in to trdrs. You tell trdrs that a trader has
      an account at a built-in provider: their sign-in email, and optionally the
      account number and login name. When that trader signs in, Connect shows
      the account ready to link, and they sign in to the provider themselves,
      once. The trader types their own password, and access starts when their
      own login succeeds. You can list who you pre-registered and who has
      linked, and cancel a pre-registration that hasn’t been used. A
      pre-registration expires after 30 days, and sending it again refreshes it.
      These routes take the pre-registration key. The whole flow is in the
      **[Quick Start](/docs/guides/quick-start)**.
  - name: Connect apps preview
    description: >-
      Run a Connect app: your own trading interface, whose traders connect their
      accounts in hosted Connect on your website and trade them on your screens.
      The Connect API is what your backend calls with your app’s API key: Create
      a Connect link, read and close it, and the account, market and trading
      routes with your trader named in `x-trdrs-trader`. Hosted Connect calls
      its own routes with the frame session a Connect link opens. The Connect
      dashboard calls the rest with your Connect sign-in session: your apps,
      each app’s name and logo, API keys, websites, tiles, white-label paper and
      Demos, traders and active traders, invoices, conformance runs and its
      Connect pass. These routes are in preview, served on the sandbox, where
      they are free. Once production opens to Connect apps, it serves the route
      that records an app’s Connect pass, which opens the app’s production.
  - name: Connect accounts
    description: >-
      A Connect client's team signs in to the Connect dashboard with a Connect
      account of its own, never a trader's, and one login reaches both
      environments. Production answers a short statement for the signed-in
      account, naming its verified email and the Connect apps it owns, and
      sandbox exchanges it for a sandbox session and the counterpart app of
      each: the sandbox app that stands for the production one. The environments
      share no credential: production signs the statement with its own key, and
      sandbox checks it with production's public key alone. These routes take
      the Connect dashboard's own session, from the dashboard's origin; a
      trader's session and every key are refused, and a Connect account's
      session reaches no trading, account, market or AI route.
  - name: Challenges
    description: >-
      These routes list evaluation programs and a trader’s own enrollments.
      **Preview: the one group in this reference outside the additive-only
      guarantee.** Their shapes will change when challenges are rebuilt; see
      Stability. **They take a signed-in session, not a key**, and are served
      only where the engine runs with `CHALLENGES_ENABLED`. Without it, the
      routes don’t exist and every one answers `404`. The administration half
      isn’t documented here, because it is trdrs’s own tooling, not part of the
      API.
paths:
  /api/trading/exits:
    post:
      tags:
        - Trading
      summary: Protect a position
      description: >-
        Puts a bracket on an open position: up to three take profits and three
        stop losses, laid out as pairs along the position, nearest the entry
        first, each pair's take profit and stop loss cancelling each other.
        Write it as `exits`. Distances are measured from the position's average
        entry, and every price must clear the market on the side it protects,
        since a stop already past the market would close the position at once.
        The bracket protects `qty` of the position, or all of it that no other
        bracket protects. Send `positionId` to protect one position alone, on an
        account that names its positions. A stop loss that moves itself, to
        breakeven or trailing, is moved by trdrs on every provider that holds a
        bracket. No hold on the account refuses a bracket on a position: it only
        closes the position it protects and cannot add exposure.


        Send `entryOrderId` in place of a position to protect a resting entry
        before it fills: a working order of yours on the instrument that can
        open a position and has filled nothing. The bracket covers all of the
        entry and waits on it, as a bracket placed with its entry does, and each
        pair rests once the fill that opens its part of the position arrives.
        Its prices are measured from the entry's own price (a limit's limit, a
        stop's or a stop-limit's trigger) and lie on their side of it, and none
        is held to the market, since nothing is open yet. On the paper book,
        which holds a waiting bracket inside its entry, the entry is replaced
        with itself carrying the pairs in the same step that records the
        bracket, and rests afterwards under your `clientOrderId`: the answer's
        `entryOrderId` names it. A hold that refuses new entries refuses that
        replacement too.


        Required key: Trading API key. A Connect app's backend can also send it
        with the app's API key, and a venue's backend with its Venue key, which
        carries `trader:trade`, each naming one of its own traders in
        `x-trdrs-trader`: it then runs on that trader's own accounts.
      parameters:
        - name: provider
          in: query
          schema:
            type: string
          description: >-
            The provider the account is at, such as `rithmic` or `paper`. Omit
            it to use your default provider. An unknown value is refused with
            400, never replaced with another provider.
        - name: account
          in: query
          schema:
            type: string
          description: >-
            The account number at that provider. It must be one of your own
            accounts, or the call is refused with 400.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProtectRequest'
      responses:
        '200':
          description: >-
            The bracket rests, or on a resting entry waits on it
            (`pending_entry`), with `entryOrderId` naming the order the entry
            rests as. `refused` is the provider's reason where it refused a
            pair, whose part of the position the bracket then shows as
            unprotected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BracketChange'
        '400':
          description: >-
            `clientOrderId` or `exits` is missing, the position is flat, or the
            provider holds no take profits or stop losses. A bracket the account
            can't run is refused with what it lacks in `missing`, and one that
            doesn't resolve with why in `problems`. `positionId` on a provider
            that doesn't name its positions is refused with
            `position_exits_unsupported`. `entryOrderId` that names no resting
            order of yours on the instrument, or one that only reduces a
            position, is refused, and so is `entryOrderId` sent with
            `positionId` or `qty`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: The key is missing, unknown or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            The position is already protected (`position_protected`, with the
            `bracketId` to change instead, or the part of it that is free in
            `error`), or the `clientOrderId` was already used. The resting entry
            already carries a bracket (`entry_protected`, with the `bracketId`
            to change instead), or part of it has filled
            (`entry_partially_filled`): protect the position it opened. An order
            of a bracket is refused with `bracket_order`, and on the paper book
            an account that changed while the entry was being replaced with
            `version_conflict`: send it again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >-
            The provider refused every order of the bracket, so nothing rests
            and the position is unprotected. The answer is the provider's own
            refusal, with its status (422 unless the refusal states another),
            `code` and `params`, beside the bracket, which ended `failed`. The
            `clientOrderId` is free again for a retry once the cause is fixed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '423':
          description: >-
            On the paper book, the account is locked by risk (`risk_locked`) or
            held for reconciliation (`reconciliation_hold`), which refuses the
            replacement of a resting entry that would carry the bracket. A
            bracket on a position is never refused this way.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            trdrs couldn't record the bracket, so nothing was placed. Retry with
            the same `clientOrderId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - tradingApiKey: []
        - connectApiKey: []
          trader: []
        - venueKey: []
          trader: []
      x-codeSamples:
        - lang: javascript
          label: TypeScript
          source: |-
            const res = await fetch('https://app.trdrs.co/api/trading/exits', {
              method: 'POST',
              headers: {
                'content-type': 'application/json',
                Authorization: `Bearer ${process.env.TRDRS_API_KEY}`,
              },
              body: JSON.stringify({
                "instrument": "ESU6",
                "clientOrderId": "protect-esu6-1787581500",
                "exits": {
                  "takeProfits": [
                    {
                      "quantity": 1,
                      "price": 6485.5
                    }
                  ],
                  "stopLosses": [
                    {
                      "quantity": 2,
                      "price": 6477.5
                    }
                  ]
                }
              }),
            })
            const data = await res.json()
        - lang: shell
          label: cURL
          source: |-
            curl -X POST 'https://app.trdrs.co/api/trading/exits' \
              -H "Authorization: Bearer $TRDRS_API_KEY" \
              -H 'content-type: application/json' \
              -d '{"instrument":"ESU6","clientOrderId":"protect-esu6-1787581500","exits":{"takeProfits":[{"quantity":1,"price":6485.5}],"stopLosses":[{"quantity":2,"price":6477.5}]}}'
components:
  schemas:
    ProtectRequest:
      type: object
      description: >-
        A bracket to put on an open position, or on a resting entry: its take
        profits and stop losses.
      properties:
        instrument:
          type: string
          description: The instrument.
        clientOrderId:
          type: string
          description: >-
            Your own id for this protection, and its idempotency key. On the
            paper book a resting entry rests under this id once the bracket is
            on it.
        exits:
          $ref: '#/components/schemas/ExitLadderInput'
          description: >-
            The take profits and stop losses. Distances are measured from the
            position's average entry, or from a resting entry's own price.
        qty:
          type: number
          description: >-
            How much of the position to protect. Omit it to protect all of it
            that no other bracket protects. A resting entry is protected whole,
            so a protection that names one carries no `qty`.
        positionId:
          type: string
          description: >-
            The one position to protect, by the id the account snapshot gives
            it: a ticket on an account that holds separate tickets, or the net
            position. Only where the account names its positions; anywhere else
            the request is refused, rather than applied to the instrument.
        entryOrderId:
          type: string
          description: >-
            A resting entry to protect before it fills, by its
            `providerOrderId`, in place of a position: a working order of yours
            on the instrument that can open a position and has filled nothing.
            The bracket covers all of the entry and waits on it, and each pair
            rests once the fill that opens its part of the position arrives.
            Send it without `positionId` and `qty`.
          example: '234992311'
      required:
        - instrument
        - clientOrderId
        - exits
      example:
        instrument: ESU6
        clientOrderId: protect-esu6-1787581500
        exits:
          takeProfits:
            - quantity: 1
              price: 6485.5
          stopLosses:
            - quantity: 2
              price: 6477.5
    BracketChange:
      type: object
      description: A bracket after a protection, a change or a withdrawal.
      properties:
        bracket:
          $ref: '#/components/schemas/BracketReceipt'
          description: The bracket.
        refused:
          type:
            - string
            - 'null'
          description: >-
            The provider's reason where it refused a pair, or null. A change of
            a bracket the provider refuses leaves the bracket as it was. On a
            protection, the refused pair's part of the position shows as
            unprotected and the other pairs rest.
        stuck:
          type: integer
          description: >-
            How many orders the change meant to cancel that would not cancel and
            still rest. The bracket keeps them, so the account stream shows
            them.
        entryOrderId:
          type: string
          description: >-
            On a protection of a resting entry, and on a change or a withdrawal
            of a bracket waiting on one: the order the entry rests as
            afterwards. The paper book replaces the entry with itself to change
            the pairs it carries, so there the entry rests under a new id. Every
            other provider keeps the entry's id. Absent on every other answer.
          example: protect-entry-esu6-1787581560
      required:
        - bracket
        - refused
        - stuck
      example:
        bracket:
          id: b41e7d2a-9c58-4f03-a6b1-2e8d5c7f0a94
          state: active
        refused: null
        stuck: 0
    ErrorResponse:
      type: object
      description: >-
        The body of every error response. It always carries `error`, an English
        sentence you can show. A refused trading request also carries `code`,
        one of the refusal codes, and `params`, the details of that refusal.
        Translate by `code` and `params`, and show a generic message for a code
        you don't recognize. Other errors may carry a `code` of their own.
      properties:
        error:
          type: string
          description: What went wrong, as an English sentence.
        code:
          type: string
          description: >-
            A stable machine code. On a refused trading request, it is one of
            the refusal codes.
        params:
          type: object
          description: >-
            The details of the refusal named by `code`, on a refused trading
            request.
      required:
        - error
      example:
        error: invalid_instrument
    ExitLadderInput:
      type: object
      description: >-
        A bracket as you write it: up to three take profits and three stop
        losses. trdrs lays them out as pairs along the position, nearest the
        entry first, each pair's take profit and stop loss cancelling each
        other. Stop losses covering more than the take profits leave a runner,
        and part of the position with no stop loss is reported as unprotected.
      properties:
        unit:
          type: string
          enum:
            - ticks
            - price
            - percent
            - pips
            - currency
          description: >-
            What the distances are in. Required when any leg states a distance.
            `currency` is money for the leg's quantity; `pips` is offered only
            on an instrument that has a pip.
        takeProfits:
          type: array
          maxItems: 3
          items:
            $ref: '#/components/schemas/ExitLeg'
          description: The take profits, nearest the entry first.
        stopLosses:
          type: array
          maxItems: 3
          items:
            $ref: '#/components/schemas/ExitStopLeg'
          description: The stop losses, nearest the entry first.
      required:
        - takeProfits
        - stopLosses
      example:
        takeProfits:
          - quantity: 1
            price: 6485.5
        stopLosses:
          - quantity: 2
            price: 6477.5
    BracketReceipt:
      type: object
      description: >-
        The bracket a request placed or changed. The account's frame carries the
        whole bracket at the same revision.
      properties:
        id:
          type: string
          description: The bracket's id.
        state:
          type: string
          enum:
            - pending_entry
            - active
            - completed
            - cancelled
            - failed
          description: Where it stands.
      required:
        - id
        - state
      example:
        id: b41e7d2a-9c58-4f03-a6b1-2e8d5c7f0a94
        state: active
    ExitLeg:
      type: object
      description: >-
        One take profit. Size it by `quantity` or by `share` of the position in
        percent, and place it at a `price` or a `distance` from the entry in the
        bracket's `unit`: one of each. A leg stated by its share and its
        distance fits any entry.
      properties:
        quantity:
          type: number
          description: >-
            The quantity it closes, in the instrument's own unit: contracts,
            lots or coins.
        share:
          type: number
          description: >-
            The share of the position it closes, in percent, more than 0 and at
            most 100.
        price:
          type: number
          description: Its price, on the instrument's tick.
        distance:
          type: number
          description: How far from the entry it sits, in the bracket's `unit`.
      example:
        share: 50
        distance: 20
    ExitStopLeg:
      type: object
      description: >-
        One stop loss, sized and placed as a take profit is, and optionally
        moving itself: to breakeven once, and along up to three trailing tiers.
      properties:
        quantity:
          type: number
          description: >-
            The quantity it closes, in the instrument's own unit: contracts,
            lots or coins.
        share:
          type: number
          description: >-
            The share of the position it closes, in percent, more than 0 and at
            most 100.
        price:
          type: number
          description: Its price, on the instrument's tick.
        distance:
          type: number
          description: How far from the entry it sits, in the bracket's `unit`.
        breakeven:
          oneOf:
            - $ref: '#/components/schemas/ExitBreakeven'
            - type: 'null'
          description: The move to breakeven, or null for none.
        trail:
          type: array
          maxItems: 3
          items:
            $ref: '#/components/schemas/ExitTrailTier'
          description: >-
            The trailing tiers, lowest trigger first. Empty or absent for no
            trailing stop.
      example:
        share: 100
        distance: 12
        breakeven:
          trigger: 8
          plus: 0
        trail:
          - trigger: 16
            distance: 8
            frequencyTicks: 2
    ExitBreakeven:
      type: object
      description: >-
        A one-time move of the stop to the entry plus `plus`, once the position
        is `trigger` in profit.
      properties:
        trigger:
          type: number
          description: How far in profit the position must be, in the bracket's `unit`.
        plus:
          type: number
          description: >-
            How far beyond the entry the stop goes, in the bracket's `unit`.
            Zero is exact breakeven.
      required:
        - trigger
        - plus
      example:
        trigger: 8
        plus: 0
    ExitTrailTier:
      type: object
      description: >-
        One tier of a trailing stop. Once the position is `trigger` in profit,
        the stop follows the best price `distance` behind it, and moves only
        after the price has advanced a whole `frequencyTicks`. A later tier
        triggers further out and can only tighten the stop.
      properties:
        trigger:
          type: number
          description: >-
            How far in profit the position must be before this tier starts, in
            the bracket's `unit`.
        distance:
          type: number
          description: >-
            How far behind the best price the stop follows, in the bracket's
            `unit`.
        frequencyTicks:
          type: integer
          description: >-
            How far the price must advance before the stop moves again, always
            in whole ticks, because it limits how often trdrs calls the
            provider.
      required:
        - trigger
        - distance
        - frequencyTicks
      example:
        trigger: 16
        distance: 8
        frequencyTicks: 2
  securitySchemes:
    tradingApiKey:
      type: http
      scheme: bearer
      description: >-
        The Trading API key (`trdrs_sk_…`), for market data, orders and account
        state on the accounts its owner holds. Use it server-to-server, and keep
        it on your server. Its scope is named `trading` in the API.
    connectApiKey:
      type: http
      scheme: bearer
      description: >-
        A Connect app's API key (`trdrs_ck_sandbox_…` or
        `trdrs_ck_production_…`), in preview. It belongs to one app in one
        environment, has a name and an expiry, and carries the whole Connect
        API. An owner of the app creates it in the Connect dashboard. It
        creates, reads and closes the app's Connect links under
        `/api/connect/links`, and on the account, market and trading routes it
        reads the app's own traders' accounts and market data and routes their
        orders, each request naming its trader in `x-trdrs-trader`. It stops
        working when it is revoked or expires, or when the owner who created it
        stops being an owner. Keep it on your server.
    trader:
      type: apiKey
      in: header
      name: x-trdrs-trader
      description: >-
        Your own id for one of your traders: the id your Connect links name, or
        the id the accounts your venue issues them use. Send it with your app's
        API key, or with your Venue key carrying `trader:read`, or
        `trader:trade` to also route the trader's orders, and the request runs
        as that trader on their own accounts. An id that names none of your
        traders is refused with `404` `trader_not_found` and a suspended trader
        with `403` `trader_suspended`. A request that also carries a cookie or a
        browser `Origin` is refused with `400` `credential_not_allowed`.
    venueKey:
      type: http
      scheme: bearer
      description: >-
        The Venue key (`trdrs_vk_sandbox_…` or `trdrs_vk_production_…`), in
        preview. It belongs to one venue in one environment and carries the
        scopes it was created with. The venue's verified owner creates it while
        signed in. It works on the `/api/venues/{venueId}` routes and, with
        `trader:read` or `trader:trade`, on the account, market and trading
        routes, reading the venue's own traders' accounts and routing their
        orders, each request naming its trader in `x-trdrs-trader`. A
        `trdrs_sk_…` key can't be used in its place. It reaches its venue's
        routes and its venue's own traders, and the owner manages keys from a
        signed-in session. Keep it on your server.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.