openapi: 3.1.0

info:
  title: Yellow Box Markets Trading API
  version: 1.0.0
  summary: REST API for programmatic trading on Yellow Box Markets MT5 accounts.
  description: |
    Binance-style conventions (authentication, signing, headers, error envelope, rate-limit model)
    with native MT5 semantics (lots, bid/ask, tickets, SL/TP on the position, swaps, hedging).

    There is no wire compatibility with Binance.

    Two things differ from every Binance endpoint and are easy to miss:

    1. **Every account-scoped call takes a mandatory `login`** — one API key normally trades many
       MT5 accounts, so the account cannot be inferred from the key.
    2. **`newClientOrderId` is the idempotency key** — a retry with the same id never creates a
       second order.

    Signing: SIGNED endpoints (`x-security-type` `TRADE` or `USER_DATA`) require `timestamp`
    (Unix ms) and `signature` — the lowercase hex HMAC-SHA256, keyed with the API secret, over
    `totalParams` = the query string concatenated verbatim with the request body. `recvWindow`
    defaults to 5000 ms and may not exceed 60000 ms.

    The base URL below is a **placeholder** pending DNS assignment.

    **Pre-release.** Where the current trade server does not yet reach the documented statuses, the
    gap is listed in `known-limitations.md` alongside this file.

    Full documentation: see `README.md` alongside this file.
  license:
    name: Proprietary — Yellow Box Markets
    identifier: LicenseRef-Proprietary
  contact:
    name: Yellow Box Markets API integration
    url: https://www.yellowboxmarkets.com

servers:
  - url: https://trade-api.yellowboxmarkets.com
    description: Production (placeholder hostname, subject to confirmation). Sandbox is the same host with a demo-scoped key.

tags:
  - name: General
    description: Connectivity, server time and trading rules. No authentication required.
  - name: Market Data
    description: Prices, daily statistics, candles and recent ticks. API key only, no signature.
  - name: Trade
    description: Placing, modifying, cancelling and closing orders and positions. Every endpoint takes a mandatory login.
  - name: Account
    description: Account state, key scope and executed deals.
  - name: User Data Stream
    description: listenKey lifecycle for the authenticated WebSocket user data stream.

security: []

paths:

  /v1/ping:
    get:
      tags: [General]
      operationId: ping
      summary: Test connectivity
      description: Tests connectivity to the REST API. Does not contact the trade server.
      x-security-type: NONE
      x-weight: 1
      security: []
      responses:
        '200':
          description: Reachable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Empty'
              examples:
                default:
                  value: {}
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/time:
    get:
      tags: [General]
      operationId: serverTime
      summary: Check server time
      description: The API server clock, used by the recvWindow check. Calibrate `timestamp` against it.
      x-security-type: NONE
      x-weight: 1
      security: []
      responses:
        '200':
          description: Current server time.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServerTime'
              examples:
                default:
                  value:
                    serverTime: 1789012345678
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/exchangeInfo:
    get:
      tags: [General]
      operationId: exchangeInfo
      summary: Exchange information
      description: |
        Trading rules, symbol specifications and rate limits.

        Security type is NONE, but the endpoint honours `X-YBX-APIKEY` when you send one: with a key
        `rateLimits[]` carries that key's values instead of the service defaults.

        The symbol list is NOT filtered to the key's scope — every symbol the trade server exposes is
        returned either way, because the Manager API offers no bulk "symbols permitted for this
        group" read. Treat the list as the venue's instruments, not as your entitlements: an
        instrument your accounts cannot trade is still listed and is rejected with `-4011` on use.

        `symbol` and `symbols` are mutually exclusive.
      x-security-type: NONE
      x-weight: 10
      security: []
      parameters:
        - name: symbol
          in: query
          required: false
          description: One exact, case-sensitive symbol name.
          schema:
            type: string
          example: XAUUSD
        - name: symbols
          in: query
          required: false
          description: 'JSON array of exact symbol names, URL-encoded. Example: %5B%22XAUUSD%22,%22BTCUSD%22%5D'
          schema:
            type: string
      responses:
        '200':
          description: Trading rules and symbol specifications.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExchangeInfo'
              examples:
                default:
                  value:
                    serverTime: 1789012345678
                    serverTimeZoneOffsetMinutes: 180
                    rateLimits:
                      - rateLimitType: REQUEST_WEIGHT
                        interval: MINUTE
                        intervalNum: 1
                        limit: 1200
                      - rateLimitType: ORDERS
                        interval: SECOND
                        intervalNum: 10
                        limit: 100
                      - rateLimitType: ORDERS
                        interval: MINUTE
                        intervalNum: 1
                        limit: 1200
                    symbols:
                      - symbol: XAUUSD
                        description: Gold vs US Dollar
                        digits: 2
                        tickSize: '0.01'
                        tickValue: '1.00'
                        contractSize: '100'
                        volumeMin: '0.01'
                        volumeMax: '50.00'
                        volumeStep: '0.01'
                        stopsLevel: 30
                        freezeLevel: 0
                        tradeMode: FULL
                        executionMode: MARKET
                        fillModes: [FOK, IOC]
                        expirationModes: [GTC, DAY, GTD, GTD_DAY]
                        orderModes: [MARKET, LIMIT, STOP, STOP_LIMIT, SL, TP, CLOSE_BY]
                        currencyBase: XAU
                        currencyProfit: USD
                        currencyMargin: USD
                        swapMode: BY_POINTS
                        swapLong: '-12.5'
                        swapShort: '4.2'
                        swap3Days: WEDNESDAY
                        sessions:
                          SUNDAY: []
                          MONDAY: [{ open: '01:05', close: '23:55' }]
                          TUESDAY: [{ open: '01:05', close: '23:55' }]
                          WEDNESDAY: [{ open: '01:05', close: '23:55' }]
                          THURSDAY: [{ open: '01:05', close: '23:55' }]
                          FRIDAY: [{ open: '01:05', close: '23:55' }]
                          SATURDAY: []
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/ticker/price:
    get:
      tags: [Market Data]
      operationId: tickerPrice
      summary: Symbol price ticker
      description: Latest quote for a symbol, or an array for every symbol when `symbol` is omitted.
      x-security-type: MARKET_DATA
      x-weight: 1 with symbol, 2 without
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/SymbolOptional'
      responses:
        '200':
          description: Latest quote, or an array of them.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/PriceTicker'
                  - type: array
                    items:
                      $ref: '#/components/schemas/PriceTicker'
              examples:
                single:
                  summary: With symbol
                  value:
                    symbol: XAUUSD
                    bid: '2331.15'
                    ask: '2331.42'
                    last: '2331.15'
                    time: 1789012345678
                all:
                  summary: Without symbol
                  value:
                    - symbol: XAUUSD
                      bid: '2331.15'
                      ask: '2331.42'
                      last: '2331.15'
                      time: 1789012345678
                    - symbol: BTCUSD
                      bid: '64120.50'
                      ask: '64135.00'
                      last: '64120.50'
                      time: 1789012345671
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/ticker/bookTicker:
    get:
      tags: [Market Data]
      operationId: tickerBookTicker
      summary: Symbol order book ticker
      description: |
        Best bid and ask, Binance-shaped. For MT5 this is the same data as `GET /v1/ticker/price` —
        the trade server's quote carries no top-of-book size, so `bidQty` and `askQty` are always
        `"0"`. Use the `<SYMBOL>@depth` WebSocket stream for real depth.
      x-security-type: MARKET_DATA
      x-weight: 1 with symbol, 2 without
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/SymbolOptional'
      responses:
        '200':
          description: Best bid/ask, or an array of them.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/BookTicker'
                  - type: array
                    items:
                      $ref: '#/components/schemas/BookTicker'
              examples:
                default:
                  value:
                    symbol: XAUUSD
                    bidPrice: '2331.15'
                    bidQty: '0'
                    askPrice: '2331.42'
                    askQty: '0'
                    time: 1789012345678
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/ticker/24hr:
    get:
      tags: [Market Data]
      operationId: ticker24hr
      summary: 24hr ticker statistics
      description: |
        Daily statistics from the trade server's tick statistics. This is the server's **trading
        day**, not a rolling 24-hour window, and the activity counters are broker-wide.
      x-security-type: MARKET_DATA
      x-weight: 1 with symbol, 20 without
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/SymbolOptional'
      responses:
        '200':
          description: Daily statistics, or an array of them.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Ticker24hr'
                  - type: array
                    items:
                      $ref: '#/components/schemas/Ticker24hr'
              examples:
                default:
                  value:
                    symbol: XAUUSD
                    openPrice: '2325.40'
                    highPrice: '2338.90'
                    lowPrice: '2321.05'
                    lastPrice: '2331.15'
                    bidPrice: '2331.15'
                    askPrice: '2331.42'
                    bidHigh: '2338.90'
                    bidLow: '2321.05'
                    askHigh: '2339.18'
                    askLow: '2321.30'
                    priceChange: '5.75'
                    priceChangePercent: '0.247'
                    volatility: '0.731'
                    tradeDeals: 18422
                    volume: '9214.50'
                    buyOrders: 9611
                    sellOrders: 8811
                    time: 1789012345678
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/klines:
    get:
      tags: [Market Data]
      operationId: klines
      summary: Klines (candlestick data)
      description: |
        Historical candles, aggregated from the trade server's 1-minute bars. Rows are positional
        arrays; index from the front and tolerate extra trailing elements.

        Buckets are aligned to the Unix epoch in **UTC**, so `1d` candles open at 00:00 UTC and do
        not match the broker's trading day. `1w` and `1M` are not supported.

        Use the `<SYMBOL>@kline_<interval>` WebSocket stream for live candles.
      x-security-type: MARKET_DATA
      x-weight: 1 for limit <= 100, 2 for limit <= 500, 5 for limit <= 1000
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/SymbolRequired'
        - name: interval
          in: query
          required: true
          schema:
            $ref: '#/components/schemas/KlineInterval'
          example: 1m
        - name: startTime
          in: query
          required: false
          description: Inclusive, Unix ms.
          schema:
            type: integer
            format: int64
        - name: endTime
          in: query
          required: false
          description: Inclusive, Unix ms.
          schema:
            type: integer
            format: int64
        - name: limit
          in: query
          required: false
          description: Default 500, maximum 1000.
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 500
      responses:
        '200':
          description: Candles, oldest first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KlineList'
              examples:
                default:
                  value:
                    - [1789012320000, '2331.02', '2331.40', '2330.95', '2331.15', 38, 1789012379999, 0]
                    - [1789012380000, '2331.15', '2331.62', '2331.10', '2331.58', 44, 1789012439999, 0]
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/ticks:
    get:
      tags: [Market Data]
      operationId: recentTicks
      summary: Recent ticks
      description: |
        The most recent ticks for a symbol, served from an in-memory ring buffer filled since the
        service last started. **This is not a tick-history endpoint** — there is no archive, and a
        restart empties the buffer. An empty array means the symbol is not currently streamed.
      x-security-type: MARKET_DATA
      x-weight: 2
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/SymbolRequired'
        - name: limit
          in: query
          required: false
          description: Default 100, maximum 1000.
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
      responses:
        '200':
          description: Ticks, oldest first.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Tick'
              examples:
                default:
                  value:
                    - symbol: XAUUSD
                      bid: '2331.14'
                      ask: '2331.41'
                      last: '2331.14'
                      volume: 3
                      bidDirection: -1
                      askDirection: -1
                      time: 1789012345601
                    - symbol: XAUUSD
                      bid: '2331.15'
                      ask: '2331.42'
                      last: '2331.15'
                      volume: 8
                      bidDirection: 1
                      askDirection: 1
                      time: 1789012345678
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/order:
    post:
      tags: [Trade]
      operationId: newOrder
      summary: New order
      description: |
        Opens a market position or places a pending order.

        `newOrderRespType=ACK` (default) returns as soon as the order is durably queued, with
        `status: "ACCEPTED"`. `RESULT` waits up to 5 seconds for the trade server's answer.

        `newClientOrderId` is the idempotency key — a retry with the same id returns the stored
        outcome and never creates a second order. Always set it. A retry is answered from the
        stored record BEFORE any symbol or price validation, so a retried limit order whose price
        the market has since crossed returns its stored status, not `-2021`. An id this key already
        used for a different kind of operation, or for a different `login`, is refused with `-1133`.

        Counts 1 against the ORDERS rate limit.
      x-security-type: TRADE
      x-weight: 1
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        description: Form-encoded. The same parameters may be sent in the query string instead; the query string wins on duplicates.
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/NewOrderRequest'
            examples:
              market:
                summary: Market buy, ACK
                value:
                  login: 100123
                  symbol: XAUUSD
                  side: BUY
                  type: MARKET
                  volume: '0.10'
                  newClientOrderId: bot-001
                  recvWindow: 5000
                  timestamp: 1789012345678
                  signature: b7f6052aa37e7d6de935cce1c099f8103a2b0a0711b77fdf48becfc8b7d2c0e7
      responses:
        '200':
          description: Order accepted, filled or placed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderResponse'
              examples:
                ack:
                  summary: ACK
                  value:
                    login: 100123
                    clientOrderId: bot-001
                    symbol: XAUUSD
                    side: BUY
                    type: MARKET
                    volume: '0.10'
                    status: ACCEPTED
                    transactTime: 1789012345690
                filled:
                  summary: RESULT, market order filled
                  value:
                    login: 100123
                    clientOrderId: bot-001
                    symbol: XAUUSD
                    side: BUY
                    type: MARKET
                    volume: '0.10'
                    status: FILLED
                    orderId: 44412345
                    dealId: 55512345
                    positionId: 44412345
                    price: '2331.42'
                    executedVolume: '0.10'
                    sl: '2320.00'
                    tp: '2350.00'
                    mt5RetCode: 10009
                    transactTime: 1789012345812
                placed:
                  summary: RESULT, pending order placed
                  value:
                    login: 100123
                    clientOrderId: bot-002
                    symbol: XAUUSD
                    side: BUY
                    type: BUY_LIMIT
                    volume: '0.10'
                    status: NEW
                    orderId: 44412350
                    price: '2325.00'
                    timeInForce: GTC
                    mt5RetCode: 10008
                    transactTime: 1789012345812
        '400':
          $ref: '#/components/responses/OrderError'
        default:
          $ref: '#/components/responses/Error'

    put:
      tags: [Trade]
      operationId: modifyOrder
      summary: Modify pending order
      description: |
        Changes a resting pending order. Send exactly one of `orderId` / `origClientOrderId`.

        Omitted fields are left unchanged; send `0` to clear an `sl` or `tp`.

        `timeInForce` is conditionally mandatory. MT5 rewrites every field of an order in one
        operation, so a modify always sends an expiration mode. When it is omitted the service uses
        the value it last sent for that ticket, then the trade server's own value where the order
        record carries one; if neither can answer — typically an order this API did not place — the
        request is refused with `-1102` rather than guessing, because a guess would silently turn a
        DAY order into a GTC one. The same applies to `expiration` when the resolved mode needs one.

        A retry with a `newClientOrderId` this key has already used returns the stored outcome
        without re-validating against the order's current state.

        Counts 1 against the ORDERS rate limit.
      x-security-type: TRADE
      x-weight: 1
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/ModifyOrderRequest'
      responses:
        '200':
          description: Modification accepted or applied.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderResponse'
              examples:
                default:
                  value:
                    login: 100123
                    clientOrderId: bot-002-mod-1
                    origClientOrderId: bot-002
                    orderId: 44412350
                    symbol: XAUUSD
                    side: BUY
                    type: BUY_LIMIT
                    volume: '0.10'
                    status: NEW
                    price: '2323.00'
                    sl: '2315.00'
                    timeInForce: GTC
                    mt5RetCode: 10009
                    transactTime: 1789012400112
        '400':
          $ref: '#/components/responses/OrderError'
        default:
          $ref: '#/components/responses/Error'

    delete:
      tags: [Trade]
      operationId: cancelOrder
      summary: Cancel pending order
      description: |
        Cancels a resting pending order. Send exactly one of `orderId` / `origClientOrderId`.
        Cancelling an order that no longer exists is `-2013`. Waits up to 5 seconds for the trade
        server and answers `ACCEPTED` if the cancel is not confirmed by then. `newClientOrderId` is
        the idempotency key for the cancel itself: a retry with the same id returns the stored
        outcome instead of `-2013`.

        Counts 1 against the ORDERS rate limit.
      x-security-type: TRADE
      x-weight: 1
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/LoginRequired'
        - $ref: '#/components/parameters/OrderId'
        - $ref: '#/components/parameters/OrigClientOrderId'
        - name: newClientOrderId
          in: query
          required: false
          description: Idempotency key for this cancel.
          schema:
            $ref: '#/components/schemas/ClientOrderId'
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: Order cancelled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderResponse'
              examples:
                default:
                  value:
                    login: 100123
                    clientOrderId: bot-002-cancel
                    origClientOrderId: bot-002
                    orderId: 44412350
                    symbol: XAUUSD
                    side: BUY
                    type: BUY_LIMIT
                    volume: '0.10'
                    status: CANCELED
                    mt5RetCode: 10009
                    transactTime: 1789012400500
        '400':
          $ref: '#/components/responses/OrderError'
        default:
          $ref: '#/components/responses/Error'

    get:
      tags: [Trade]
      operationId: queryOrder
      summary: Query order
      description: |
        The current state of one order. For a market order this returns the execution record,
        including `dealId` and `positionId` once the fill is known. Send exactly one of
        `orderId` / `origClientOrderId`. An order on another login is `-2013`, never a permission
        error.
      x-security-type: USER_DATA
      x-weight: 1
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/LoginRequired'
        - $ref: '#/components/parameters/OrderId'
        - $ref: '#/components/parameters/OrigClientOrderId'
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: The order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderDetail'
              examples:
                default:
                  value:
                    login: 100123
                    clientOrderId: bot-001
                    orderId: 44412345
                    dealId: 55512345
                    positionId: 44412345
                    symbol: XAUUSD
                    side: BUY
                    type: MARKET
                    status: FILLED
                    volume: '0.10'
                    executedVolume: '0.10'
                    price: '2331.42'
                    stopLimitPrice: '0'
                    sl: '2320.00'
                    tp: '2350.00'
                    timeInForce: GTC
                    expiration: 0
                    comment: signal-7
                    mt5RetCode: 10009
                    time: 1789012345690
                    updateTime: 1789012345812
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/openOrders:
    get:
      tags: [Trade]
      operationId: openOrders
      summary: Current pending orders
      description: |
        Every pending order currently resting on the account. Market orders never appear here.
        `clientOrderId` is empty for orders this API did not place.
      x-security-type: USER_DATA
      x-weight: 1
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/LoginRequired'
        - $ref: '#/components/parameters/SymbolOptional'
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: Resting pending orders.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/OpenOrder'
              examples:
                default:
                  value:
                    - login: 100123
                      orderId: 44412350
                      clientOrderId: bot-002
                      symbol: XAUUSD
                      side: BUY
                      type: BUY_LIMIT
                      status: NEW
                      volume: '0.10'
                      remainingVolume: '0.10'
                      price: '2325.00'
                      stopLimitPrice: '0'
                      sl: '2315.00'
                      tp: '0'
                      timeInForce: GTC
                      expiration: 0
                      comment: ''
                      time: 1789012345690
                      updateTime: 1789012345690
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/allOrders:
    get:
      tags: [Trade]
      operationId: allOrders
      summary: All orders
      description: |
        Order history including filled, cancelled and expired orders. With no time window the last
        7 days are returned; `endTime - startTime` may not exceed 7 days (`-1127`). Ordered oldest
        first.
      x-security-type: USER_DATA
      x-weight: 5
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/LoginRequired'
        - $ref: '#/components/parameters/SymbolOptional'
        - name: orderId
          in: query
          required: false
          description: Return orders with an id greater than or equal to this, within the time window (order history is read from the trade server, which is always windowed; unlike userTrades, it does not override it).
          schema:
            type: integer
            format: int64
        - $ref: '#/components/parameters/StartTime'
        - $ref: '#/components/parameters/EndTime'
        - $ref: '#/components/parameters/HistoryLimit'
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: Orders, oldest first.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/OpenOrder'
              examples:
                default:
                  value:
                    - login: 100123
                      orderId: 44412350
                      clientOrderId: bot-002
                      symbol: XAUUSD
                      side: BUY
                      type: BUY_LIMIT
                      status: CANCELED
                      volume: '0.10'
                      remainingVolume: '0.10'
                      price: '2325.00'
                      stopLimitPrice: '0'
                      sl: '2315.00'
                      tp: '0'
                      timeInForce: GTC
                      expiration: 0
                      comment: ''
                      time: 1789012345690
                      updateTime: 1789012400500
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/positions:
    get:
      tags: [Trade]
      operationId: positions
      summary: Open positions
      description: |
        Every open position on the account. Positions are hedged — a symbol can hold many, in both
        directions, each with its own `positionId`.

        `profit` is the trade server's floating P&L and refreshes on a server-side cadence of a few
        seconds, not on every tick. It excludes `swap`, and commission is not carried on a position
        at all; it is booked on the deals (`GET /v1/userTrades`).
      x-security-type: USER_DATA
      x-weight: 1
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/LoginRequired'
        - $ref: '#/components/parameters/SymbolOptional'
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: Open positions.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Position'
              examples:
                default:
                  value:
                    - login: 100123
                      positionId: 44412345
                      symbol: XAUUSD
                      side: BUY
                      volume: '0.10'
                      priceOpen: '2331.42'
                      priceCurrent: '2331.15'
                      sl: '2320.00'
                      tp: '2350.00'
                      profit: '-2.7000'
                      swap: '0.0000'
                      openTime: 1789012345000
                      updateTime: 1789012400000
                      comment: signal-7
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/position:
    put:
      tags: [Trade]
      operationId: modifyPosition
      summary: Modify position SL/TP
      description: |
        Sets the stop loss and take profit on an open position. At least one of `sl` / `tp` is
        required. **`0` clears a level; omitting it preserves the current one.**

        Counts 1 against the ORDERS rate limit.
      x-security-type: TRADE
      x-weight: 1
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/ModifyPositionRequest'
      responses:
        '200':
          description: Modification accepted or applied.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PositionModifyResponse'
              examples:
                default:
                  value:
                    login: 100123
                    positionId: 44412345
                    clientOrderId: bot-sltp-1
                    symbol: XAUUSD
                    side: BUY
                    volume: '0.10'
                    sl: '2325.00'
                    tp: '2350.00'
                    status: FILLED
                    mt5RetCode: 10009
                    transactTime: 1789012500110
        '400':
          $ref: '#/components/responses/OrderError'
        default:
          $ref: '#/components/responses/Error'

    delete:
      tags: [Trade]
      operationId: closePosition
      summary: Close position
      description: |
        Closes an open position at market, fully or partially. A partial close keeps the same
        `positionId`. `-5006` if the position closed between your read and this call. The realised
        profit is not on this response — read it from the DEAL event (`rp`) or `GET /v1/userTrades`.

        Counts 1 against the ORDERS rate limit.
      x-security-type: TRADE
      x-weight: 1
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/LoginRequired'
        - name: positionId
          in: query
          required: true
          schema:
            type: integer
            format: int64
          example: 44412345
        - name: volume
          in: query
          required: false
          description: Lots to close. Omit to close the whole position.
          schema:
            type: string
          example: '0.05'
        - name: newClientOrderId
          in: query
          required: false
          description: Idempotency key. Strongly recommended.
          schema:
            $ref: '#/components/schemas/ClientOrderId'
        - name: newOrderRespType
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/NewOrderRespType'
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: Close accepted or executed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClosePositionResponse'
              examples:
                default:
                  value:
                    login: 100123
                    positionId: 44412345
                    clientOrderId: bot-close-1
                    symbol: XAUUSD
                    side: SELL
                    volume: '0.10'
                    status: FILLED
                    orderId: 44412399
                    dealId: 55512400
                    price: '2331.60'
                    executedVolume: '0.10'
                    mt5RetCode: 10009
                    transactTime: 1789012600410
        '400':
          $ref: '#/components/responses/OrderError'
        default:
          $ref: '#/components/responses/Error'

  /v1/allOpenPositions:
    delete:
      tags: [Trade]
      operationId: closeAllOpenPositions
      summary: Close all open positions
      description: |
        Closes every open position on the account, optionally filtered to one symbol. Pending
        orders are not cancelled.

        **Omitting `symbol` flattens the account on every symbol.** The sweep acts on a snapshot
        taken when it runs; positions opened afterwards are not closed.

        **A retry with the same `newClientOrderId` replays the ORIGINAL snapshot** — it returns the
        first sweep's legs and closes nothing opened since, including when the first sweep found
        nothing and answered `requested: 0`. A fresh sweep needs a fresh `newClientOrderId`.

        Counts 1 against the ORDERS rate limit regardless of how many positions close.
      x-security-type: TRADE
      x-weight: 5
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/LoginRequired'
        - $ref: '#/components/parameters/SymbolOptional'
        - name: newClientOrderId
          in: query
          required: false
          description: >-
            Idempotency key for the whole sweep. Reusing one replays that sweep's original snapshot
            rather than taking a new one.
          schema:
            $ref: '#/components/schemas/ClientOrderId'
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: Sweep accepted. `requested` is 0 with an empty array when the account is already flat.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CloseAllResponse'
              examples:
                default:
                  value:
                    login: 100123
                    clientOrderId: bot-flatten-1
                    requested: 2
                    status: ACCEPTED
                    transactTime: 1789012700100
                    positions:
                      - positionId: 44412345
                        symbol: XAUUSD
                        volume: '0.10'
                        status: ACCEPTED
                      - positionId: 44412360
                        symbol: BTCUSD
                        volume: '0.05'
                        status: ACCEPTED
        '400':
          $ref: '#/components/responses/OrderError'
        default:
          $ref: '#/components/responses/Error'

  /v1/batchOrders:
    post:
      tags: [Trade]
      operationId: batchOrders
      summary: Place multiple orders
      description: |
        Up to 100 orders in one signed request, **each carrying its own `login`** — the primitive
        for fanning one signal across many accounts.

        The response is an array in the same order as the request; each element is either a success
        object or an error object. The batch is **not atomic**. HTTP 200 means the batch was
        accepted, even if every item failed; a 4XX means no item was queued.

        Sign the body exactly as transmitted, including the percent-encoded `batchOrders` value.

        Each item follows the single-order idempotency rules: an item whose `newClientOrderId`
        was already used returns its stored outcome, and one already used for a different kind of
        operation or a different `login` is a `-1133` error object in its slot.

        Refused as a whole with `-5015` (HTTP 503), before any item is queued or any ORDERS slot is
        charged, when the execution path is not running.

        Counts 1 per item against both the REQUEST_WEIGHT and ORDERS rate limits.
      x-security-type: TRADE
      x-weight: 1 per item
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/BatchOrdersRequest'
      responses:
        '200':
          description: Batch accepted. One element per request item, in order.
          content:
            application/json:
              schema:
                type: array
                items:
                  oneOf:
                    - $ref: '#/components/schemas/OrderResponse'
                    - $ref: '#/components/schemas/OrderError'
              examples:
                default:
                  value:
                    - login: 100123
                      clientOrderId: sig7-100123
                      symbol: XAUUSD
                      side: BUY
                      type: MARKET
                      volume: '0.10'
                      status: ACCEPTED
                      transactTime: 1789012800100
                    - code: -2018
                      msg: Insufficient margin on the trading account.
                      mt5RetCode: 10019
                      status: REJECTED
                      clientOrderId: sig7-100124
                      login: 100124
                    - code: -2022
                      msg: Login is not in this API key's scope, or the account is not tradable.
                      login: 100125
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/account:
    get:
      tags: [Account]
      operationId: account
      summary: Account information
      description: |
        Balance, equity and margin for one MT5 account, as the trade server reports them. Values are
        in the account's deposit currency; `USC` means a cent account (100 USC = 1 USD) and nothing
        is converted.

        Equity and margin are not carried on the `ACCOUNT_UPDATE` stream event — this endpoint is
        the way to read them.

        An in-scope account whose trading is disabled is still READABLE: this endpoint answers
        normally with `tradeAllowed: false`. `-2024` is returned by the operations that trade, not by
        this read. `-2022` means the login is not in the key's scope, does not exist, has an inactive
        owner, or the account itself is not active.
      x-security-type: USER_DATA
      x-weight: 1
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/LoginRequired'
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: Account state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Account'
              examples:
                default:
                  value:
                    login: 100123
                    group: 'real\BOT\ECN'
                    currency: USD
                    leverage: 100
                    balance: '10000.0000'
                    credit: '0.0000'
                    equity: '10012.3400'
                    margin: '233.1200'
                    freeMargin: '9779.2200'
                    marginLevel: '4294.60'
                    profit: '12.3400'
                    tradeAllowed: true
                    updateTime: 1789012345678
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/accounts:
    get:
      tags: [Account]
      operationId: accounts
      summary: Accounts in key scope
      description: |
        Every MT5 login this API key may trade. A key scoped exclusively to `demo: true` logins is a
        sandbox key. No balances — call `GET /v1/account` per login for those. Exactly the logins the
        other endpoints accept for this key and the user data stream carries: an archived account is
        not listed (it answers `-2022` everywhere); one whose trading is disabled is listed with
        `tradeAllowed: false`.
      x-security-type: USER_DATA
      x-weight: 5
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: Logins in scope. Empty when the key has no accounts assigned.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AccountScopeItem'
              examples:
                default:
                  value:
                    - login: 100123
                      group: 'real\BOT\ECN'
                      currency: USD
                      demo: false
                      tradeAllowed: true
                    - login: 100124
                      group: 'real\BOT\CENT'
                      currency: USC
                      demo: false
                      tradeAllowed: true
                    - login: 500901
                      group: 'demo\BOT\ECN'
                      currency: USD
                      demo: true
                      tradeAllowed: true
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/userTrades:
    get:
      tags: [Account]
      operationId: userTrades
      summary: Account trade list
      description: |
        Executed trading deals for the account, read from the broker's stored deal history. With no
        time window the last 7 days are returned; `endTime - startTime` may not exceed 7 days
        (`-1127`). Ordered oldest first by `dealId`.

        Only trading deals are returned — balance operations, credit grants and commission postings
        are excluded, so this does not reconcile to the account balance on its own. Round-trip P&L
        is `profit + commission + swap` summed over the deals sharing a `positionId`.

        TODAY THIS RETURNS CLOSING DEALS ONLY: the stored history persists only the deals that close
        or reduce a position, so `entry` is OUT, INOUT or OUT_BY and never IN. The opening deal of a
        round trip is on the `DEAL` user-data-stream event at the moment of the fill.

        `side` is the direction AS STORED by the history writer, which is not always the deal's own
        direction — see the `side` property. Use the `DEAL` event's `S` field when you need the deal
        direction unambiguously.
      x-security-type: USER_DATA
      x-weight: 5
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/LoginRequired'
        - $ref: '#/components/parameters/SymbolOptional'
        - $ref: '#/components/parameters/StartTime'
        - $ref: '#/components/parameters/EndTime'
        - name: fromId
          in: query
          required: false
          description: Return deals with a dealId greater than or equal to this. Overrides the time window.
          schema:
            type: integer
            format: int64
        - $ref: '#/components/parameters/HistoryLimit'
        - $ref: '#/components/parameters/RecvWindow'
        - $ref: '#/components/parameters/Timestamp'
        - $ref: '#/components/parameters/Signature'
      responses:
        '200':
          description: Deals, oldest first.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/UserTrade'
              examples:
                default:
                  value:
                    - login: 100123
                      dealId: 55512400
                      orderId: 44412399
                      positionId: 44412345
                      symbol: XAUUSD
                      side: SELL
                      entry: OUT
                      volume: '0.10'
                      price: '2331.60'
                      profit: '1.8000'
                      commission: '-0.7000'
                      swap: '0.0000'
                      comment: signal-7
                      time: 1789012600000
                    - login: 100123
                      dealId: 55512455
                      orderId: 44412450
                      positionId: 44412400
                      symbol: BTCUSD
                      side: BUY
                      entry: OUT
                      volume: '0.05'
                      price: '64135.00'
                      profit: '-12.5000'
                      commission: '-0.4000'
                      swap: '-0.1200'
                      time: 1789012900000
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

  /v1/listenKey:
    post:
      tags: [User Data Stream]
      operationId: createListenKey
      summary: Start user data stream
      description: |
        Creates a listenKey, or returns and extends the key's existing one. Valid for 60 minutes.
        One active listenKey per API key; it covers every login in the key's scope.

        The listenKey is a bearer credential granting read access to every account in scope. It
        does not grant trading.
      x-security-type: USER_STREAM
      x-weight: 1
      security:
        - ApiKeyAuth: []
      responses:
        '200':
          description: The listenKey.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListenKey'
              examples:
                default:
                  value:
                    listenKey: pqia91ma19a5s61cv6a81va65sdf19v8a65a1a5s61cv6a81va65sdf19v8a65a1
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

    put:
      tags: [User Data Stream]
      operationId: keepAliveListenKey
      summary: Keepalive user data stream
      description: |
        Extends the listenKey by 60 minutes from the moment of the call. Send one every 30 minutes
        on a timer — an open WebSocket connection does not extend the key on its own.
      x-security-type: USER_STREAM
      x-weight: 1
      security:
        - ApiKeyAuth: []
      requestBody:
        required: false
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                listenKey:
                  type: string
                  description: Optional. The key's active listenKey is extended when omitted. A listenKey that is not the key's active one is -1125.
      responses:
        '200':
          description: Extended.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Empty'
              examples:
                default:
                  value: {}
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

    delete:
      tags: [User Data Stream]
      operationId: closeListenKey
      summary: Close user data stream
      description: |
        Invalidates the listenKey and closes every WebSocket connection using it. Idempotent —
        deleting a listenKey that is already gone is a success.
      x-security-type: USER_STREAM
      x-weight: 1
      security:
        - ApiKeyAuth: []
      parameters:
        - name: listenKey
          in: query
          required: false
          description: Optional. The key's active listenKey is closed when omitted.
          schema:
            type: string
      responses:
        '200':
          description: Closed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Empty'
              examples:
                default:
                  value: {}
        '400':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'

components:

  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-YBX-APIKEY
      description: |
        The API key. Required by every security type except `NONE`.

        SIGNED endpoints (`x-security-type` `TRADE` or `USER_DATA`) additionally require the
        `timestamp` and `signature` request parameters, which OpenAPI cannot express as a security
        scheme. `signature` is the lowercase hex HMAC-SHA256, keyed with the API secret, over
        `totalParams` = query string concatenated verbatim with the request body, computed on the
        bytes exactly as transmitted. `recvWindow` (default 5000 ms, maximum 60000 ms) bounds how
        stale the request may be.

  parameters:

    LoginRequired:
      name: login
      in: query
      required: true
      description: MT5 login of the account this call applies to. Must be in the key's scope, otherwise -2022.
      schema:
        type: integer
        format: int64
      example: 100123

    SymbolOptional:
      name: symbol
      in: query
      required: false
      description: Exact, case-sensitive MT5 symbol name.
      schema:
        type: string
      example: XAUUSD

    SymbolRequired:
      name: symbol
      in: query
      required: true
      description: Exact, case-sensitive MT5 symbol name.
      schema:
        type: string
      example: XAUUSD

    OrderId:
      name: orderId
      in: query
      required: false
      description: MT5 order ticket. Send this or origClientOrderId, not both.
      schema:
        type: integer
        format: int64

    OrigClientOrderId:
      name: origClientOrderId
      in: query
      required: false
      description: The newClientOrderId the order was placed with. Send this or orderId, not both.
      schema:
        type: string
        maxLength: 36

    StartTime:
      name: startTime
      in: query
      required: false
      description: Inclusive, Unix ms.
      schema:
        type: integer
        format: int64

    EndTime:
      name: endTime
      in: query
      required: false
      description: Inclusive, Unix ms.
      schema:
        type: integer
        format: int64

    HistoryLimit:
      name: limit
      in: query
      required: false
      description: Default 500, maximum 1000.
      schema:
        type: integer
        minimum: 1
        maximum: 1000
        default: 500

    RecvWindow:
      name: recvWindow
      in: query
      required: false
      description: Request validity window in ms. Default 5000, maximum 60000.
      schema:
        type: integer
        format: int64
        minimum: 1
        maximum: 60000
        default: 5000

    Timestamp:
      name: timestamp
      in: query
      required: true
      description: Request creation time, Unix ms.
      schema:
        type: integer
        format: int64

    Signature:
      name: signature
      in: query
      required: true
      description: Lowercase hex HMAC-SHA256 of totalParams, keyed with the API secret.
      schema:
        type: string

  responses:

    Error:
      description: |
        Error. 4XX is a caller problem, 429 is a rate limit, 418 is a temporary key ban, 5XX is
        ours. A 503 with "The execution status is UNKNOWN" may have executed — confirm before
        retrying.
      headers:
        Retry-After:
          description: Seconds to wait. Present on 429 and 418.
          schema:
            type: integer
        X-YBX-USED-WEIGHT-1M:
          description: Request weight used in the current minute.
          schema:
            type: integer
        X-YBX-ORDER-COUNT-10S:
          description: Order-affecting requests in the current 10-second window.
          schema:
            type: integer
        X-YBX-ORDER-COUNT-1M:
          description: Order-affecting requests in the current minute.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            invalidSymbol:
              summary: Validation
              value:
                code: -1121
                msg: Invalid symbol.
            unknownExecution:
              summary: 503, execution status unknown
              value:
                code: -1000
                msg: Unknown error, please check your request or try again later. The execution status is UNKNOWN and could have been a success.

    OrderError:
      description: |
        Order rejected. The standard envelope plus `status`, `clientOrderId` and `login` so the
        failure can be correlated without a second lookup.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OrderError'
          examples:
            default:
              value:
                code: -2018
                msg: Insufficient margin on the trading account.
                mt5RetCode: 10019
                status: REJECTED
                clientOrderId: bot-003
                login: 100123

  schemas:

    Empty:
      type: object
      description: Empty object.
      additionalProperties: false

    Error:
      type: object
      description: The error envelope. Every error at every status has this shape.
      required: [code, msg]
      properties:
        code:
          type: integer
          description: Negative error code. Stable — branch on this.
          examples: [-1121]
        msg:
          type: string
          description: English text. Informational; never parse it.
          examples: ['Invalid symbol.']
        mt5RetCode:
          type: integer
          description: Raw MT5 trade-request return code. Present only when the trade server answered.
          examples: [10019]

    OrderError:
      description: An error from an order endpoint — the standard envelope plus correlation fields.
      allOf:
        - $ref: '#/components/schemas/Error'
        - type: object
          properties:
            status:
              $ref: '#/components/schemas/OrderStatus'
            clientOrderId:
              $ref: '#/components/schemas/ClientOrderId'
            login:
              type: integer
              format: int64

    ServerTime:
      type: object
      required: [serverTime]
      properties:
        serverTime:
          type: integer
          format: int64
          description: Current server time, Unix ms.

    RateLimit:
      type: object
      description: One rate-limit bucket. These numbers are tunable configuration, not contract — read them here rather than hard-coding.
      required: [rateLimitType, interval, intervalNum, limit]
      properties:
        rateLimitType:
          type: string
          enum: [REQUEST_WEIGHT, ORDERS]
        interval:
          type: string
          enum: [SECOND, MINUTE, HOUR, DAY]
        intervalNum:
          type: integer
        limit:
          type: integer

    TradeSession:
      type: object
      description: One trading session, in trade-server time.
      required: [open, close]
      properties:
        open:
          type: string
          description: HH:MM, trade-server time.
        close:
          type: string
          description: HH:MM, trade-server time.

    SymbolInfo:
      type: object
      description: MT5 symbol specification. Every filter error in the -40xx family is checkable against this.
      required: [symbol, digits, contractSize, volumeMin, volumeMax, volumeStep, tradeMode]
      properties:
        symbol:
          type: string
          description: Exact, case-sensitive MT5 symbol name.
        description:
          type: string
          description: Human-readable instrument name. May be empty.
        digits:
          type: integer
          description: Price decimal places. One point is 10^-digits.
        tickSize:
          type: string
          description: Minimum price increment.
        tickValue:
          type: string
          description: Value of one tick per lot, in currencyProfit.
        contractSize:
          type: string
          description: Units of currencyBase per lot.
        volumeMin:
          type: string
          description: Minimum order volume, lots.
        volumeMax:
          type: string
          description: Maximum order volume, lots.
        volumeStep:
          type: string
          description: Volume granularity, lots.
        stopsLevel:
          type: integer
          description: Minimum distance in points between the market and an SL, TP or pending price.
        freezeLevel:
          type: integer
          description: Distance in points within which an order or position may not be modified.
        tradeMode:
          type: string
          enum: [DISABLED, LONG_ONLY, SHORT_ONLY, CLOSE_ONLY, FULL]
        executionMode:
          type: string
          enum: [REQUEST, INSTANT, MARKET, EXCHANGE]
        fillModes:
          type: array
          items:
            type: string
            enum: [FOK, IOC, BOC]
        expirationModes:
          type: array
          items:
            $ref: '#/components/schemas/TimeInForce'
        orderModes:
          type: array
          items:
            type: string
            enum: [MARKET, LIMIT, STOP, STOP_LIMIT, SL, TP, CLOSE_BY]
        currencyBase:
          type: string
        currencyProfit:
          type: string
        currencyMargin:
          type: string
        swapMode:
          type: string
          enum:
            - DISABLED
            - BY_POINTS
            - BY_SYMBOL_CURRENCY
            - BY_MARGIN_CURRENCY
            - BY_GROUP_CURRENCY
            - BY_INTEREST_CURRENT
            - BY_INTEREST_OPEN
            - REOPEN_BY_CLOSE_PRICE
            - REOPEN_BY_BID
            - BY_PROFIT_CURRENCY
        swapLong:
          type: string
          description: Swap per lot per night on long positions, in the unit implied by swapMode.
        swapShort:
          type: string
          description: Swap per lot per night on short positions, in the unit implied by swapMode.
        swap3Days:
          type: string
          description: Weekday triple swap is charged, or DISABLED when the symbol charges none.
          enum: [SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, DISABLED]
        sessions:
          type: object
          description: Trading sessions per weekday, in trade-server time. An empty array means the symbol does not trade that day.
          additionalProperties:
            type: array
            items:
              $ref: '#/components/schemas/TradeSession'

    ExchangeInfo:
      type: object
      required: [serverTime, rateLimits, symbols]
      properties:
        serverTime:
          type: integer
          format: int64
        serverTimeZoneOffsetMinutes:
          type: integer
          description: Trade-server timezone offset from UTC in minutes. utcMinutes = sessionMinutes - this.
        rateLimits:
          type: array
          items:
            $ref: '#/components/schemas/RateLimit'
        symbols:
          type: array
          items:
            $ref: '#/components/schemas/SymbolInfo'

    PriceTicker:
      type: object
      required: [symbol, bid, ask, time]
      properties:
        symbol:
          type: string
        bid:
          type: string
          description: Best bid. What a sell fills at.
        ask:
          type: string
          description: Best ask. What a buy fills at.
        last:
          type: string
          description: Last traded price. Often "0" on forex and CFD symbols.
        time:
          type: integer
          format: int64

    BookTicker:
      type: object
      required: [symbol, bidPrice, askPrice, time]
      properties:
        symbol:
          type: string
        bidPrice:
          type: string
        bidQty:
          type: string
          description: Always "0" — MT5 quotes carry no top-of-book size.
        askPrice:
          type: string
        askQty:
          type: string
          description: Always "0".
        time:
          type: integer
          format: int64

    Ticker24hr:
      type: object
      description: Trade-server trading-day statistics. Not a rolling 24-hour window; activity counters are broker-wide.
      required: [symbol, time]
      properties:
        symbol:
          type: string
        openPrice:
          type: string
        highPrice:
          type: string
          description: Day high, bid side.
        lowPrice:
          type: string
          description: Day low, bid side.
        lastPrice:
          type: string
        bidPrice:
          type: string
        askPrice:
          type: string
        bidHigh:
          type: string
        bidLow:
          type: string
        askHigh:
          type: string
        askLow:
          type: string
        priceChange:
          type: string
          description: lastPrice - openPrice, in price units.
        priceChangePercent:
          type: string
          description: >-
            Day change in percent. Two provenances: the trade server's own statistic when the value
            is read from it, and derived from openPrice and lastPrice when the symbol is currently
            streamed and the figure comes from the live feed (which carries no percent of its own).
            Expect them to agree to rounding, not bit-for-bit.
        volatility:
          type: string
          description: Day volatility percent, as the trade server computes it.
        tradeDeals:
          type: integer
          format: int64
          description: Deals on the symbol today, broker-wide.
        volume:
          type: string
          description: Traded volume today as reported by the trade server, in its volume units (lots for most symbols), broker-wide.
        buyOrders:
          type: integer
          format: int64
        sellOrders:
          type: integer
          format: int64
        time:
          type: integer
          format: int64

    KlineInterval:
      type: string
      description: Candle interval. Buckets are aligned to the Unix epoch in UTC. 1w and 1M are not supported.
      enum: ['1m', '5m', '15m', '30m', '1h', '2h', '4h', '1d']

    KlineList:
      type: array
      description: |
        Candles, oldest first. Each row is positional:
        [openTime, open, high, low, close, tickVolume, closeTime, realVolume].
        Prices are strings; times are Unix ms; volumes are integers. New elements may be appended
        to the end of a row — index from the front.
      items:
        type: array
        items: {}

    Tick:
      type: object
      required: [symbol, bid, ask, time]
      properties:
        symbol:
          type: string
        bid:
          type: string
        ask:
          type: string
        last:
          type: string
        volume:
          type: integer
          format: int64
          description: Tick volume reported with the quote.
        bidDirection:
          type: integer
          description: 1 up, -1 down, 0 unchanged versus the previous tick.
        askDirection:
          type: integer
          description: 1 up, -1 down, 0 unchanged versus the previous tick.
        time:
          type: integer
          format: int64

    OrderSide:
      type: string
      enum: [BUY, SELL]

    OrderType:
      type: string
      enum: [MARKET, BUY_LIMIT, SELL_LIMIT, BUY_STOP, SELL_STOP, BUY_STOP_LIMIT, SELL_STOP_LIMIT]

    TimeInForce:
      type: string
      description: GTD and GTD_DAY require expiration. Must be present in the symbol's expirationModes.
      enum: [GTC, DAY, GTD, GTD_DAY]

    NewOrderRespType:
      type: string
      description: ACK returns once the order is durably queued. RESULT waits up to 5 seconds for the trade server's answer.
      enum: [ACK, RESULT]
      default: ACK

    OrderStatus:
      type: string
      description: |
        Market executions: ACCEPTED, PARTIALLY_FILLED, FILLED, REJECTED, IN_DOUBT.
        PARTIALLY_FILLED means part of the requested volume executed; executedVolume is the sum of
        the deals so far and price is their volume-weighted average.
        Pending orders: NEW, PARTIALLY_FILLED, FILLED, CANCELED, EXPIRED, REJECTED.
        New values may be added — treat an unrecognised status as non-terminal.
      enum: [ACCEPTED, FILLED, REJECTED, IN_DOUBT, NEW, PARTIALLY_FILLED, CANCELED, EXPIRED]

    ClientOrderId:
      type: string
      description: The idempotency key. Unique per API key. A retry with the same id never creates a second order.
      maxLength: 36
      pattern: '^[A-Za-z0-9\-_.]{1,36}$'

    NewOrderRequest:
      type: object
      required: [login, symbol, type, volume, timestamp, signature]
      properties:
        login:
          type: integer
          format: int64
          description: MT5 account.
        symbol:
          type: string
          description: Exact, case-sensitive.
        type:
          $ref: '#/components/schemas/OrderType'
        side:
          $ref: '#/components/schemas/OrderSide'
        volume:
          type: string
          description: Lots. Must satisfy volumeMin, volumeMax and volumeStep.
        price:
          type: string
          description: Mandatory for every pending type. Ignored for MARKET.
        stopLimitPrice:
          type: string
          description: Mandatory for BUY_STOP_LIMIT and SELL_STOP_LIMIT.
        sl:
          type: string
          description: Stop loss. 0 or omitted means none.
        tp:
          type: string
          description: Take profit. 0 or omitted means none.
        timeInForce:
          $ref: '#/components/schemas/TimeInForce'
        expiration:
          type: integer
          format: int64
          description: Unix ms. Mandatory for GTD and GTD_DAY.
        newClientOrderId:
          $ref: '#/components/schemas/ClientOrderId'
        newOrderRespType:
          $ref: '#/components/schemas/NewOrderRespType'
        comment:
          type: string
          maxLength: 24
          description: Stored on the MT5 order and deal. The service appends a 7-character correlation tag, so the comment returned on a deal is longer than the one sent. Not a substitute for newClientOrderId.
        recvWindow:
          type: integer
          format: int64
          maximum: 60000
          default: 5000
        timestamp:
          type: integer
          format: int64
        signature:
          type: string

    ModifyOrderRequest:
      type: object
      required: [login, timestamp, signature]
      description: Send exactly one of orderId / origClientOrderId, and at least one field to change.
      properties:
        login:
          type: integer
          format: int64
        orderId:
          type: integer
          format: int64
        origClientOrderId:
          type: string
          maxLength: 36
        price:
          type: string
        stopLimitPrice:
          type: string
        sl:
          type: string
          description: 0 clears. Omitting preserves the current level.
        tp:
          type: string
          description: 0 clears. Omitting preserves the current level.
        timeInForce:
          $ref: '#/components/schemas/TimeInForce'
        expiration:
          type: integer
          format: int64
        newClientOrderId:
          $ref: '#/components/schemas/ClientOrderId'
        recvWindow:
          type: integer
          format: int64
          maximum: 60000
          default: 5000
        timestamp:
          type: integer
          format: int64
        signature:
          type: string

    ModifyPositionRequest:
      type: object
      required: [login, positionId, timestamp, signature]
      description: At least one of sl / tp. 0 clears a level; omitting it preserves the current one.
      properties:
        login:
          type: integer
          format: int64
        positionId:
          type: integer
          format: int64
        sl:
          type: string
        tp:
          type: string
        newClientOrderId:
          $ref: '#/components/schemas/ClientOrderId'
        recvWindow:
          type: integer
          format: int64
          maximum: 60000
          default: 5000
        timestamp:
          type: integer
          format: int64
        signature:
          type: string

    BatchOrderItem:
      type: object
      description: One item of a batchOrders array. Same fields as a single order, including its own login. No timestamp, signature, recvWindow or newOrderRespType — those are request-level.
      required: [login, symbol, type, volume]
      properties:
        login:
          type: integer
          format: int64
        symbol:
          type: string
        type:
          $ref: '#/components/schemas/OrderType'
        side:
          $ref: '#/components/schemas/OrderSide'
        volume:
          type: string
        price:
          type: string
        stopLimitPrice:
          type: string
        sl:
          type: string
        tp:
          type: string
        timeInForce:
          $ref: '#/components/schemas/TimeInForce'
        expiration:
          type: integer
          format: int64
        newClientOrderId:
          $ref: '#/components/schemas/ClientOrderId'
        comment:
          type: string
          maxLength: 24
          description: Same reserved-tag rule as a single order.

    BatchOrdersRequest:
      type: object
      required: [batchOrders, timestamp, signature]
      properties:
        batchOrders:
          type: array
          description: JSON array of 1-100 order objects, sent as a single URL-encoded form field value.
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/BatchOrderItem'
        newOrderRespType:
          $ref: '#/components/schemas/NewOrderRespType'
        recvWindow:
          type: integer
          format: int64
          maximum: 60000
          default: 5000
        timestamp:
          type: integer
          format: int64
        signature:
          type: string

    OrderResponse:
      type: object
      description: Response to an order placement, modification or cancellation. Fields absent until the trade server supplies them.
      required: [login, status, transactTime]
      properties:
        login:
          type: integer
          format: int64
        clientOrderId:
          $ref: '#/components/schemas/ClientOrderId'
        origClientOrderId:
          type: string
          description: Present on a modification or cancellation identified by client order id.
        symbol:
          type: string
        side:
          $ref: '#/components/schemas/OrderSide'
        type:
          $ref: '#/components/schemas/OrderType'
        volume:
          type: string
          description: Requested volume, lots.
        status:
          $ref: '#/components/schemas/OrderStatus'
        orderId:
          type: integer
          format: int64
        dealId:
          type: integer
          format: int64
          description: Market fills only.
        positionId:
          type: integer
          format: int64
          description: Market fills only. Under hedging it is typically equal to orderId for a newly opened position, but do not rely on that; use the value returned.
        price:
          type: string
          description: Fill price for a market order; order price for a pending order.
        executedVolume:
          type: string
        sl:
          type: string
          description: Absent when none was set.
        tp:
          type: string
          description: Absent when none was set.
        timeInForce:
          $ref: '#/components/schemas/TimeInForce'
        expiration:
          type: integer
          format: int64
          description: Absent when none was set.
        mt5RetCode:
          type: integer
        transactTime:
          type: integer
          format: int64

    OrderDetail:
      type: object
      description: The stored state of one order. orderId is absent until the trade server has assigned one; clientOrderId is empty for an order this API did not place.
      required: [login, clientOrderId, symbol, status]
      properties:
        login:
          type: integer
          format: int64
        clientOrderId:
          type: string
        orderId:
          type: integer
          format: int64
        dealId:
          type: integer
          format: int64
        positionId:
          type: integer
          format: int64
        symbol:
          type: string
        side:
          $ref: '#/components/schemas/OrderSide'
        type:
          $ref: '#/components/schemas/OrderType'
        status:
          $ref: '#/components/schemas/OrderStatus'
        volume:
          type: string
        executedVolume:
          type: string
        price:
          type: string
        stopLimitPrice:
          type: string
        sl:
          type: string
        tp:
          type: string
        timeInForce:
          $ref: '#/components/schemas/TimeInForce'
        expiration:
          type: integer
          format: int64
        comment:
          type: string
        mt5RetCode:
          type: integer
        time:
          type: integer
          format: int64
          description: When the order was accepted by this API, Unix ms.
        updateTime:
          type: integer
          format: int64

    OpenOrder:
      type: object
      description: A pending order. clientOrderId is empty for orders this API did not place.
      required: [login, orderId, symbol, type, status, volume]
      properties:
        login:
          type: integer
          format: int64
        orderId:
          type: integer
          format: int64
        clientOrderId:
          type: string
        symbol:
          type: string
        side:
          $ref: '#/components/schemas/OrderSide'
        type:
          $ref: '#/components/schemas/OrderType'
        status:
          $ref: '#/components/schemas/OrderStatus'
        volume:
          type: string
          description: Initial volume, lots.
        remainingVolume:
          type: string
          description: Volume still resting, lots.
        price:
          type: string
        stopLimitPrice:
          type: string
        sl:
          type: string
        tp:
          type: string
        timeInForce:
          $ref: '#/components/schemas/TimeInForce'
        expiration:
          type: integer
          format: int64
        comment:
          type: string
        time:
          type: integer
          format: int64
        updateTime:
          type: integer
          format: int64

    Position:
      type: object
      description: An open position. Hedged — a symbol can hold many, in both directions.
      required: [login, positionId, symbol, side, volume]
      properties:
        login:
          type: integer
          format: int64
        positionId:
          type: integer
          format: int64
        symbol:
          type: string
        side:
          $ref: '#/components/schemas/OrderSide'
        volume:
          type: string
          description: Open volume, lots. Reduced by a partial close.
        priceOpen:
          type: string
          description: Volume-weighted open price.
        priceCurrent:
          type: string
        sl:
          type: string
        tp:
          type: string
        profit:
          type: string
          description: Floating profit as the trade server computes it. Excludes swap. Refreshes on a server-side cadence of a few seconds, not per tick.
        swap:
          type: string
        openTime:
          type: integer
          format: int64
        updateTime:
          type: integer
          format: int64
        comment:
          type: string

    PositionModifyResponse:
      type: object
      description: volume is the position's volume when the change was made and may be absent on an idempotent replay.
      required: [login, positionId, status, transactTime]
      properties:
        login:
          type: integer
          format: int64
        positionId:
          type: integer
          format: int64
        clientOrderId:
          $ref: '#/components/schemas/ClientOrderId'
        symbol:
          type: string
        side:
          $ref: '#/components/schemas/OrderSide'
        volume:
          type: string
        sl:
          type: string
        tp:
          type: string
        status:
          $ref: '#/components/schemas/OrderStatus'
        mt5RetCode:
          type: integer
        transactTime:
          type: integer
          format: int64

    ClosePositionResponse:
      type: object
      description: side is the side of the closing deal — the opposite of the position's side.
      required: [login, positionId, status, transactTime]
      properties:
        login:
          type: integer
          format: int64
        positionId:
          type: integer
          format: int64
        clientOrderId:
          $ref: '#/components/schemas/ClientOrderId'
        symbol:
          type: string
        side:
          $ref: '#/components/schemas/OrderSide'
        volume:
          type: string
        status:
          $ref: '#/components/schemas/OrderStatus'
        orderId:
          type: integer
          format: int64
        dealId:
          type: integer
          format: int64
        price:
          type: string
        executedVolume:
          type: string
        mt5RetCode:
          type: integer
        transactTime:
          type: integer
          format: int64

    CloseAllPositionItem:
      type: object
      required: [positionId, status]
      properties:
        positionId:
          type: integer
          format: int64
        symbol:
          type: string
        volume:
          type: string
        status:
          $ref: '#/components/schemas/OrderStatus'

    CloseAllResponse:
      type: object
      required: [login, requested, status, transactTime, positions]
      properties:
        login:
          type: integer
          format: int64
        clientOrderId:
          $ref: '#/components/schemas/ClientOrderId'
        requested:
          type: integer
          description: Positions found in the snapshot. 0 with an empty array means the account was already flat, which is a success.
        status:
          $ref: '#/components/schemas/OrderStatus'
        transactTime:
          type: integer
          format: int64
        positions:
          type: array
          items:
            $ref: '#/components/schemas/CloseAllPositionItem'

    Account:
      type: object
      description: Money values are in the account's deposit currency. USC means a cent account (100 USC = 1 USD); nothing is converted.
      required: [login, currency, balance, equity, tradeAllowed]
      properties:
        login:
          type: integer
          format: int64
        group:
          type: string
          description: MT5 group. Determines symbols, leverage, commissions and whether the account is a cent account.
        currency:
          type: string
          description: Deposit currency. USC = cent account.
        leverage:
          type: integer
        balance:
          type: string
          description: Realised balance. Excludes floating profit.
        credit:
          type: string
          description: Non-withdrawable broker credit. Contributes to margin but is not the client's money.
        equity:
          type: string
          description: balance + credit + floating profit.
        margin:
          type: string
        freeMargin:
          type: string
        marginLevel:
          type: string
          description: equity / margin * 100, percent, two decimals. "0.00" when no position is open.
        profit:
          type: string
          description: Total floating profit. Excludes swap and commission.
        tradeAllowed:
          type: boolean
          description: >-
            Whether trading is currently permitted on this account — false when the broker has
            disabled trading on it (TRADE calls answer -2024) or the trade server reports trading
            disabled (orders are rejected by the trade server). This read keeps answering.
        updateTime:
          type: integer
          format: int64

    AccountScopeItem:
      type: object
      required: [login, demo, tradeAllowed]
      properties:
        login:
          type: integer
          format: int64
        group:
          type: string
        currency:
          type: string
          description: USC = cent account.
        demo:
          type: boolean
          description: A key scoped exclusively to demo logins is a sandbox key.
        tradeAllowed:
          type: boolean
          description: Whether the broker permits trading on the account (false ⇒ TRADE calls answer -2024). Does not ask the trade server.

    DealEntry:
      type: string
      description: IN opening or adding, OUT closing or partially closing, INOUT close-and-reverse (netting only), OUT_BY closed by an opposite position.
      enum: [IN, OUT, INOUT, OUT_BY]

    UserTrade:
      type: object
      description: One executed deal. dealId is the natural key — dedupe on it.
      required: [login, dealId, symbol, side, entry, volume, price, time]
      properties:
        login:
          type: integer
          format: int64
        dealId:
          type: integer
          format: int64
        orderId:
          type: integer
          format: int64
        positionId:
          type: integer
          format: int64
        symbol:
          type: string
        side:
          allOf:
            - $ref: '#/components/schemas/OrderSide'
          description: >-
            The direction AS STORED by the broker's history writer. The live deal-stream writer
            stores the direction of the CLOSED POSITION (MT5 exits a long with a SELL deal, so it
            inverts the deal's action deliberately, for display); the older history sync stores the
            deal's own action. Nothing on the row says which wrote it, so the stored value is
            surfaced rather than guessed. Take the deal's true direction from the DEAL event's `S`
            field. Making this field deal-accurate requires the raw deal action to be persisted —
            a planned broker-side follow-up.
        entry:
          allOf:
            - $ref: '#/components/schemas/DealEntry'
          description: OUT, INOUT or OUT_BY. IN does not currently occur here — see the endpoint description.
        volume:
          type: string
        price:
          type: string
        profit:
          type: string
          description: Realised profit. Non-zero only on OUT, INOUT and OUT_BY.
        commission:
          type: string
        swap:
          type: string
        comment:
          type: string
        time:
          type: integer
          format: int64
          description: Execution time, Unix ms (MT5 resolution is seconds).

    ListenKey:
      type: object
      required: [listenKey]
      properties:
        listenKey:
          type: string
          description: Bearer credential for the user data stream WebSocket. Valid 60 minutes. Read-only — it does not grant trading.
