# brokapi: справочник API одним файлом

Текст справочника для агентов: объявления типов, поля, варианты, методы трейтов и их описание (разделы Errors и Contract). Собран из rustdoc, примеры кода опущены: рабочие примеры в книге. Описание в справочнике на английском. Полный справочник с исходниками и ссылками: api/<крейт>/index.html.


## Крейт brokapi_core


### Trait CidCodec

```rust
pub trait CidCodec: Send + Sync {
    // Required methods
    fn caps(&self) -> CidCaps;
    fn encode(&self, cid: ClientOrderId) -> Result<NativeCid, CidError>;
    fn decode(&self, native: &NativeCid) -> Option<ClientOrderId>;
}
```
A pure, stateless mapping between `ClientOrderId` and the broker’s `NativeCid`.

Rules for an implementation:

- `encode` is a pure function: the same cid always gives the same string, with no state and no
network.

- `decode(encode(cid))` gives `Some(cid)` for every cid (lossless). A broker whose field cannot
keep a UUID needs another carrier field, not a lossy codec.

- `decode` gives `None` for a string that this codec did not make (for example the cid of an
order placed from the broker’s app).

- `fn caps(&self) -> CidCaps`
  What the broker’s field can hold. Checked by the connector at start.

- `fn encode(&self, cid: ClientOrderId) -> Result<NativeCid, CidError>`
  The broker form of `cid`.

  Errors

  `CidError::TooLong` or `CidError::InvalidChar` when the result does not fit the field
  described by `caps`. A correct codec never returns them for a UUID.

- `fn decode(&self, native: &NativeCid) -> Option<ClientOrderId>`
  The cid behind a native string, or `None` if this codec did not make that string.

### Trait Connector

```rust
pub trait Connector: Send + Sync {
    type Caps: Copy + Send + Sync + 'static;

    // Required methods
    async fn caps(&self) -> Self::Caps;
    async fn shutdown(
        self,
        mode: ShutdownMode,
    ) -> ShutdownReport
       where Self: Sized + 'async_trait;
}
```
What every connector offers besides its capabilities: it says what it can do, and it stops.

Code that is generic over a connector can ask for the capabilities without knowing the broker, and
shut any connector down the same way. A market data connector has `MarketDataCaps`,
a user data connector has `UserDataCaps`. The two methods are also inherent
methods of each connector, so code that names the type needs no trait in scope.

- `type Caps: Copy + Send + Sync + 'static`
  The type that describes what this connector can do.

- `fn caps(&self) -> Self::Caps`
  What this connector can do. Read it before you use a feature that not every broker has.

- `async fn shutdown( self, mode: ShutdownMode, ) -> ShutdownReportwhere Self: Sized + 'async_trait,`
  Stops the connector: closes its streams, gives up commands that have no answer yet (they become
  `Error::Unknown`, see `ShutdownReport`) and ends its tasks. Takes the connector by value, so it
  cannot be used afterwards.

### Trait ExecutionSource

```rust
pub trait ExecutionSource: Send + Sync {
    // Required methods
    async fn executions(
        &self,
        range: TimeRange,
    ) -> Result<Vec<Execution>, Error>;
    async fn subscribe_executions(
        &self,
    ) -> Result<SubscriptionId, Error>;
}
```
Your own fills: the history and the live stream.

An `Execution` is one fill of one of your orders. Public deals of the market are in
`MarketDataSource`. The stream is
`Backpressure::Lossless`: a fill must not be lost, so an
overflow is announced with `Event::Overflow` and trading stops until
`OrderExecutor::reconcile`.

- `async fn executions( &self, range: TimeRange, ) -> Result<Vec<Execution>, Error>`
  Reads your fills in `range`.

  Use it to find what happened to an order after `Error::Unknown`, or to catch up after a
  gap. `cid` of an `Execution` is `None` when the broker does not echo it (the BCS history
  does not), match by `broker_order_id` then. `commission` is `0.0` where the broker gives none
  (BCS history). How far back it goes is decided by the broker.

  Contract

  - When: at any time. Reads only. The way to catch up on fills after `Error::Unknown`, an
  `Event::Overflow` (from its `lost_from`) or a restart.

  Errors

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later.

  - `Error::Rejected` when the broker refuses the request.

  Examples

- `async fn subscribe_executions( &self, ) -> Result<SubscriptionId, Error>`
  Starts the stream of your fills.

  Each fill arrives once as `Event::Execution`, tagged with the
  returned id. The stream is lossless and has a buffer size from the connector config: keep
  draining your channel. Subscribe before you place: the call returns when the stream is open, so a
  fill after it cannot be missed, but fills that happened before are not delivered (read
  `executions` to catch up). There is no way to end this subscription except
  shutting the connector down.

  Contract

  - When: once per connector, before the first `OrderExecutor::place`. Allowed while the gate
  is closed.

  - Guarantee: returns only when the stream is open, like
  `OrderExecutor::subscribe_order_state`. Lossless like it: a loss is announced with
  `Event::Overflow`.

  Errors

  - `Error::Unsupported` when the broker has no fill stream
  (`UserDataCaps::streams_executions` is `false`).
  Poll `executions` yourself then.

  - `Error::Transport` when the subscription could not be made, also when the stream did not open
  in time.

  Examples

  See the example of the trait, which receives a fill.

### Trait InstrumentSource

```rust
pub trait InstrumentSource: Send + Sync {
    // Required methods
    async fn find(
        &self,
        q: InstrumentQuery,
    ) -> Result<Vec<Instrument>, Error>;
    async fn get(
        &self,
        id: &InstrumentId,
    ) -> Result<Instrument, Error>;
    async fn trading_params(
        &self,
        id: &InstrumentId,
    ) -> Result<TradingParams, Error>;
    async fn trading_status(
        &self,
        id: &InstrumentId,
    ) -> Result<TradingStatus, Error>;
    async fn schedule(
        &self,
        exchange: &str,
        range: TimeRange,
    ) -> Result<Schedule, Error>;
}
```
The instrument directory and the rules for trading an instrument.

Public data, so it lives in the market data connector. The library does NOT check your orders
against it: round quantity and price with `TradingParams` yourself before
`OrderExecutor::place`.

- `async fn find( &self, q: InstrumentQuery, ) -> Result<Vec<Instrument>, Error>`
  Looks instruments up in the directory.

  The result can be empty (nothing matches). A query by kind can be a long list, the connector
  may read it page by page, each page counts against the rate limit.

  Errors

  - `Error::Unsupported` when the broker cannot list that kind (BCS: `Option` and `Other`).

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later. The BCS connector also
  answers `Transport` when a kind has more than its page limit, rather than return part of
  the list.

  - `Error::Rejected` when the broker refuses the request.

  Examples

- `async fn get( &self, id: &InstrumentId, ) -> Result<Instrument, Error>`
  Reads one directory entry.

  Errors

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later. An instrument the
  directory does not have is reported as `Transport` by the BCS connector (message
  `instrument not found`), there is no separate variant for “not found”.

  Examples

- `async fn trading_params( &self, id: &InstrumentId, ) -> Result<TradingParams, Error>`
  Reads lot size, price step and the other numbers needed to size and price an order.

  Errors

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later. The BCS connector also
  answers `Transport` when the directory has no trading parameters for the board.

  Examples

- `async fn trading_status( &self, id: &InstrumentId, ) -> Result<TradingStatus, Error>`
  Reads whether the instrument can be traded now.

  Errors

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later.

  Examples

- `async fn schedule( &self, exchange: &str, range: TimeRange, ) -> Result<Schedule, Error>`
  Reads the trading sessions of an exchange over `range`.

  What `exchange` means depends on the broker: T-Bank takes an exchange name (`MOEX`), Finam and
  BCS take `TICKER@MARKET` because their schedules are per instrument. The BCS answer is for
  one day, the day of `range.from`.

  Errors

  - `Error::Config` when `exchange` is not in the form the broker needs (BCS: not
  `TICKER@CLASSCODE`).

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later.

  Examples

### Trait MarketDataSource

```rust
pub trait MarketDataSource: Send + Sync {
    // Required methods
    async fn last_trades(
        &self,
        id: &InstrumentId,
        limit: u32,
    ) -> Result<Vec<MarketTrade>, Error>;
    async fn subscribe_market_trades(
        &self,
        id: &InstrumentId,
        opts: SubscribeOpts,
    ) -> Result<SubscriptionId, Error>;
    async fn subscribe_quotes(
        &self,
        id: &InstrumentId,
        opts: SubscribeOpts,
    ) -> Result<SubscriptionId, Error>;
    async fn candles(
        &self,
        id: &InstrumentId,
        tf: Timeframe,
        range: TimeRange,
    ) -> Result<Vec<Candle>, Error>;
    async fn subscribe_candles(
        &self,
        id: &InstrumentId,
        tf: Timeframe,
        opts: SubscribeOpts,
    ) -> Result<SubscriptionId, Error>;

    // Provided method
    async fn candles_max_span(&self, tf: Timeframe) -> Option<Duration> { ... }
}
```
Public trades, quotes and candles: history reads and streams.

Reads (`last_trades`, `candles`) answer directly. Streams
(`subscribe_market_trades`,
`subscribe_quotes`, `subscribe_candles`)
return a `SubscriptionId` and send `Event::MarketTrade`,
`Event::Quote` and `Event::Candle` into the
channel given at start. Stop a stream with `OrderBookSource::unsubscribe`, which takes any
subscription of the connector. Your own fills are not here, see `ExecutionSource`.

- `async fn last_trades( &self, id: &InstrumentId, limit: u32, ) -> Result<Vec<MarketTrade>, Error>`
  Reads the newest `limit` deals of the public tape.

  How far back the tape goes depends on the broker (BCS: the current session, T-Bank: the last
  hour). The BCS connector returns the deals oldest first and drops a record whose side is
  unknown. You may get fewer than `limit`.

  Errors

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later.

  - `Error::Rejected` when the broker refuses the request.

  Examples

- `async fn subscribe_market_trades( &self, id: &InstrumentId, opts: SubscribeOpts, ) -> Result<SubscriptionId, Error>`
  Starts a stream of the public deals of one instrument.

  Deals arrive as `Event::MarketTrade` tagged with the returned
  id. Every deal is an event of its own, so `Backpressure::Conflate`
  would lose deals: use `Backpressure::DropNewest` if losing
  some is acceptable, or `Backpressure::Lossless` if not (an
  overflow then sends `Event::Overflow`).

  Errors

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::Unsupported` when `StreamingCaps::market_trades`
  is `false`.

  - `Error::RateLimited`, `Error::Transport` when the subscription could not be made, also when
  the stream did not open in time (see `OrderBookSource::subscribe_order_book`: the call returns when
  the stream is open, the same for every `subscribe_*`).

  - `Error::Rejected` with code `subscription_refused` when the broker refused it.

  Examples

- `async fn subscribe_quotes( &self, id: &InstrumentId, opts: SubscribeOpts, ) -> Result<SubscriptionId, Error>`
  Starts a stream of the best bid and ask of one instrument.

  Quotes arrive as `Event::Quote` tagged with the returned id. Each
  quote replaces the previous one, so `Backpressure::Conflate`
  is the right policy.

  Errors

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::Unsupported` when `StreamingCaps::quotes` is `false`.

  - `Error::RateLimited`, `Error::Transport` when the subscription could not be made, also when
  the stream did not open in time (as in `OrderBookSource::subscribe_order_book`).

  - `Error::Rejected` with code `subscription_refused` when the broker refused it.

  Examples

  See the example of the trait, which receives a quote.

- `async fn candles( &self, id: &InstrumentId, tf: Timeframe, range: TimeRange, ) -> Result<Vec<Candle>, Error>`
  Reads the bars of `tf` that fall in `range`.

  A bar has `open_ts` and `close_ts`, and a price is a `Price`. Whether the ends
  of `range` are inclusive and how far back the broker serves is decided by the broker.

  Errors

  - `Error::Unsupported` when the broker has no such timeframe (BCS has no
  `Timeframe::M10`, capability `candles_m10`). The call is not made.

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later.

  - `Error::Rejected` when the broker refuses the request.

  Examples

- `async fn subscribe_candles( &self, id: &InstrumentId, tf: Timeframe, opts: SubscribeOpts, ) -> Result<SubscriptionId, Error>`
  Starts a stream of the latest bar of `tf` for one instrument.

  Bars arrive as `Event::Candle` tagged with the returned id. Use
  `Backpressure::Conflate`: the latest bar replaces older ones.

  Errors

  - `Error::Unsupported` when `StreamingCaps::candles` is
  `false`, or the broker has no such timeframe (BCS has no `Timeframe::M10`).

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::RateLimited`, `Error::Transport` when the subscription could not be made, also when
  the stream did not open in time (as in `OrderBookSource::subscribe_order_book`).

  - `Error::Rejected` with code `subscription_refused` when the broker refused it.

  Examples

- `fn candles_max_span(&self, tf: Timeframe) -> Option<Duration>`
  The longest span of time one `candles` request may cover for `tf`, or `None`
  when the connector does not know a limit.

  Brokers cap one history request by the number of bars or by the length of the span, and a
  request over the cap is refused (`Error::Rejected`). A long history is therefore read in
  windows of at most this span. Do not slice by hand: `brokapi_runtime::history` does it, waits
  out `Error::RateLimited` and joins the windows. The value comes from the broker’s
  documentation, not from a live check. Pure function, no network call.

  The default is `None` (“unknown”): the caller then chooses a window itself.

### Trait OrderBookSource

```rust
pub trait OrderBookSource: Send + Sync {
    // Required methods
    async fn order_book(
        &self,
        id: &InstrumentId,
        depth: u32,
    ) -> Result<OrderBook, Error>;
    async fn subscribe_order_book(
        &self,
        id: &InstrumentId,
        opts: SubscribeOpts,
    ) -> Result<SubscriptionId, Error>;
    async fn unsubscribe(
        &self,
        sub: SubscriptionId,
    ) -> Result<(), Error>;
}
```
Order book snapshot and stream.

`order_book` reads the book once and answers an
`OrderBook::Snapshot`. `subscribe_order_book` starts a stream:
the books arrive as `Event::OrderBook` in the channel given to the
connector at start, tagged with the returned `SubscriptionId`. After a reconnect the stream
sends `Event::Resync` and a new snapshot. `unsubscribe`
stops a subscription of this connector by its id.

- `async fn order_book( &self, id: &InstrumentId, depth: u32, ) -> Result<OrderBook, Error>`
  Reads the order book once, to `depth` levels per side.

  The answer is an `OrderBook::Snapshot`. Bids are listed best (highest) first and asks best
  (lowest) first. A `depth` above `MarketDataCaps::max_order_book_depth`
  is cut to it (the BCS connector does so without telling).

  Errors

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::RateLimited` when the local limiter or the broker refuses for now. Wait and repeat.

  - `Error::Transport` when the network failed or the answer was unreadable. Repeat later.

  - `Error::Rejected` when the broker refuses the request, for example an unknown instrument
  (BCS: code `validation`).

  Examples

- `async fn subscribe_order_book( &self, id: &InstrumentId, opts: SubscribeOpts, ) -> Result<SubscriptionId, Error>`
  Starts a stream of the order book of one instrument.

  Returns when the stream is open at the broker (see Contract). Books then arrive as
  `Event::OrderBook` with `Envelope::sub` equal to the returned id.
  The stream restores itself after a reconnect and announces it with
  `Event::Resync`. `opts.depth` is the number of levels
  (`None`: the connector’s default, BCS uses its maximum). For books use
  `Backpressure::Conflate`: a slow reader then sees the latest
  book and not a queue of stale ones.

  Contract

  - When: at any time, no reconcile is needed (the market data connector has no trading gate).

  - Guarantee: the call returns only when the stream is open: the broker has confirmed the
  subscription, or, where the protocol has no confirmation, the connection of the stream is up. What
  the broker sends after the return reaches you. A stream that is not open within the connector’s
  `subscribe_ack_timeout` (3 seconds by default) is `Error::Transport`, and nothing is left half
  open, so the call can be repeated.
  (Finam returns `Ok` after the wait and goes on opening the stream, see
  `OrderExecutor::subscribe_order_state`.)

  - Guarantee: the stream restores itself after a reconnect. Each restore is announced with
  `Event::Resync`: drop the book you hold for that instrument and wait
  for the next snapshot.

  Errors

  - `Error::Config` when `id.native` is of a kind this broker does not use.

  - `Error::Unsupported` when the connector has no order book stream
  (`StreamingCaps::order_book` is `false`).

  - `Error::RateLimited`, `Error::Transport` when the subscription could not be made, also when
  the stream did not open in time. The subscription does not exist, ask again later.

  - `Error::Rejected` with code `subscription_refused` when the broker refused it (an unknown
  instrument, no permission). Repeating the call unchanged will not help.

  Examples

- `async fn unsubscribe( &self, sub: SubscriptionId, ) -> Result<(), Error>`
  Stops a subscription of this connector.

  After it returns, no new events of `sub` are produced. Envelopes already in your channel stay
  there, so keep reading until you have what you need. A subscription ends on its own when the
  connector shuts down.

  Errors

  - `Error::Rejected` with code `unknown_subscription` when `sub` is not an active subscription
  of this connector: it was never given out, or it is already stopped. The same at all brokers.

  - `Error::Transport` when the broker could not be told.

  Examples

### Trait OrderExecutor

```rust
pub trait OrderExecutor: Send + Sync {
    // Required methods
    async fn place(
        &self,
        req: PlaceOrderRequest,
    ) -> Result<OrderAck, Error>;
    async fn cancel(
        &self,
        cid: ClientOrderId,
    ) -> Result<(), Error>;
    async fn replace(
        &self,
        req: ReplaceOrderRequest,
    ) -> Result<OrderAck, Error>;
    async fn order_state(
        &self,
        cid: ClientOrderId,
    ) -> Result<OrderSnapshot, Error>;
    async fn active_orders(
        &self,
    ) -> Result<Vec<OrderSnapshot>, Error>;
    async fn subscribe_order_state(
        &self,
    ) -> Result<SubscriptionId, Error>;
    async fn reconcile(
        &self,
        ledger: &[ClientOrderId],
    ) -> Result<ReconcileReport, Error>;
}
```
Orders: put, cancel, replace, look at them, follow them, and reconcile with your ledger.

The rules that matter:

- YOU make the `ClientOrderId` of every order, a fresh UUID, and keep it in your own ledger.

- Trading commands (`place`, `cancel`, `replace`)
are refused until the first successful `reconcile`, so start by calling
`reconcile` (with an empty slice if you have no ledger).

- A command that got no answer is `Error::Unknown` and is NEVER repeated by the library,
because a repeated place could create a second order. Look at the order first.

- Shutdown does not cancel orders at the exchange.

- `async fn place( &self, req: PlaceOrderRequest, ) -> Result<OrderAck, Error>`
  Puts a new order and waits for the broker’s decision.

  `Ok` means the broker accepted the order (it is working, not necessarily filled). The
  `OrderAck` has the broker’s id. Follow the order with `order_state` or
  the stream of `subscribe_order_state`.

  The `cid` in `req` must be new. A connector sends a cid at most once: a second place with the
  same cid is refused with `Error::Rejected` (BCS: code `duplicate_place`) without being sent.
  This is on purpose. After a timeout the first one may have reached the broker, and a retry
  could make a second order. Use a new cid for a new order, after you know what happened to the
  first. The price is checked against the price step and the quantity against the lot when the
  connector knows them (see `price_grid` in the config of the connector).

  Dropping the future does not cancel the order. Once the cid is marked and the request is on
  its way, the connector (T-Bank, Finam) finishes the command in a task of its own: the request, the
  answer and the update of the order’s state happen even if you stop waiting (a `timeout` around the
  call, a lost `select!`). Before that moment (the wait for the quota or for earlier commands on the
  same instrument) dropping the future sends nothing. So after you drop a `place` look at the order,
  as after `Error::Unknown`.

  Contract

  - When: after the first successful `reconcile`, and not while the
  connector is degraded or shutting down. Write `req.cid` to your journal before the call.

  - Guarantee: at most one request with this cid ever leaves the process, whatever happens.

  - Nothing sent: the gate errors, `Config`, a local `RateLimited` and the input checks
  (`missing_price`, `price_off_step`, a wrong quantity) are raised before the request leaves.
  No order exists and the cid is unused. A new cid is always the safe choice for a retry.

  - Maybe sent: after `Error::Unknown` (and, to be safe, `Error::Transport`) the order may
  exist. Ask `order_state` with the same cid.

  - `Ok` is the broker’s acceptance, not an execution. Fills come as events.

  Errors

  - `Error::NotReconciled`, `Error::Degraded`, `Error::ShuttingDown`: the gate is closed,
  nothing was sent. See `reconcile`.

  - `Error::Config` when `req.instrument.native` is of a kind this broker does not use.

  - `Error::RateLimited` when the local limiter or the broker refuses for now. The command was
  not executed. Wait `retry_after`. The safe rule for the retry is a new cid.

  - `Error::Rejected` when the broker (or the connector before sending) says no: a bad price
  or quantity, a limit order without a price (BCS: `missing_price`), a repeated cid
  (`duplicate_place`), a refusal inside an accepted request (BCS: `rejected`). The order does
  not exist. Change the request.

  - `Error::Unknown` when the request may have reached the broker and there is no answer
  (timeout, cut connection, HTTP 5xx). Do NOT place again. Ask the broker with
  `order_state` or `active_orders`.

  - `Error::Transport` when the failure happened before the request left, for example
  authentication. It is not a sign that a repeat is safe: ask
  `Error::retryable_for_command`, which says `true` for
  `Error::RateLimited` only, and not `Error::class`, which is the class of a read.

  Examples

  A limit order that the mock broker refuses, and what the caller sees:

- `async fn cancel( &self, cid: ClientOrderId, ) -> Result<(), Error>`
  Cancels a working order and waits for the broker to confirm.

  `Ok(())` means the broker says the order is cancelled. It cancels orders placed through
  `place` and `replace`. For a stop order use
  `StopOrderExecutor::cancel_stop`. A part that was filled before stays filled.

  Contract

  - When: while the gate is open. The order may come from an earlier run of the process: the
  cid is enough. An order this process did not place is not queued behind other commands on its
  instrument (the instrument is not known).

  - Guarantee: `Ok(())` is the broker’s confirmation. A repeated cancel creates no order, so
  it is the one command that may be repeated by hand after `Error::Unknown`, and only
  after `order_state` shows the order still works.

  Errors

  - `Error::NotReconciled`, `Error::Degraded`, `Error::ShuttingDown`: the gate is closed,
  nothing was sent.

  - `Error::RateLimited`: not executed, wait `retry_after` and repeat.

  - `Error::Rejected` when the broker refuses: the order is unknown to it, it was filled
  first, or the request was refused. The code depends on how the broker answers (BCS codes
  include `not_found`, `already_filled`, `cancel_rejected`, `validation`). Read the order with
  `order_state` to see where it stands.

  - `Error::Unknown` when the answer is missing, or the answer leaves the order alive. The
  order may still be working. Ask `order_state` and cancel again only if
  it is. A repeated cancel is harmless for the order, but it uses rate limit.

  - `Error::Transport` when the failure happened before the request left.

  Examples

- `async fn replace( &self, req: ReplaceOrderRequest, ) -> Result<OrderAck, Error>`
  Changes the quantity or price of a working order.

  The broker cancels the original and puts a new order under `req.cid`, so the answer is a new
  order with a new broker id. The original is not active afterwards. Only where
  `UserDataCaps::supports_replace` is `true`.

  Contract

  - When: while the gate is open and the broker has replace
  (`UserDataCaps::supports_replace`). `req.cid` is new
  (write it to your journal first), `req.original_cid` is the working order.

  - Guarantee: the same cid rules as `place`. After `Error::Unknown` ask
  `order_state` for both cids before any new command on them.

  Errors

  - `Error::Unsupported` when the broker has no replace (Finam). Nothing was sent, the order
  is untouched.

  - `Error::NotReconciled`, `Error::Degraded`, `Error::ShuttingDown`: the gate is closed.

  - `Error::RateLimited`: not executed, wait `retry_after`.

  - `Error::Rejected` when the broker refuses, for example the original is unknown or filled,
  or (BCS) the original is partly filled and `qty` is `None` (`qty_required`).

  - `Error::Unknown` when the outcome is not known. The original may be replaced, still
  working or gone. Ask `order_state` for both cids. Do not replace again
  until you know.

  - `Error::Transport` when the failure happened before the request left.

  Examples

- `async fn order_state( &self, cid: ClientOrderId, ) -> Result<OrderSnapshot, Error>`
  Reads one order by its cid, as the broker shows it now.

  This is how you leave the doubt of `Error::Unknown`. A read is not behind the trading
  gate, it works before the first reconcile. It also finds orders that are no longer active
  (filled, cancelled), unlike `active_orders`.

  Contract

  - When: at any time, also before the first `reconcile` and while the
  trading gate is closed: a read is not gated.

  - Guarantee: reads only, so it may be repeated as often as the rate limit allows. It
  answers for final orders too. A cid the broker does not know is an error (see below), and
  right after an `Error::Unknown` that is not final: the request may still be on its way.

  Errors

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later.

  - `Error::Rejected` with code `not_found` (T-Bank: `NOT_FOUND`, compare without regard to
  case) when the broker does not know the cid. The same at all brokers. After an
  `Error::Unknown` this can mean that the order never reached the broker, or has not reached
  it yet. There is no separate variant for it.

  Examples

- `async fn active_orders( &self, ) -> Result<Vec<OrderSnapshot>, Error>`
  Lists the working orders of the account.

  Plain orders only: stop orders are in `StopOrderExecutor::active_stop_orders`. The list
  includes orders that were not placed through this connector (by hand, by another program).
  Their cid is not yours: where the broker does not give a cid back, the BCS connector makes a
  stable one from the broker id, the same for the same order every time.

  Contract

  - When: at any time, the trading gate does not matter. Reads only.

  - Guarantee: the list is what the broker shows now. Orders of others are in it too: do not
  touch what is not in your journal.

  Errors

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later.

  Examples

- `async fn subscribe_order_state( &self, ) -> Result<SubscriptionId, Error>`
  Starts the stream of changes of your orders.

  Every change (accepted, partly filled, filled, cancelled, refused) arrives as
  `Event::Order`, tagged with the returned id, and carries the cid.

  The stream is `Backpressure::Lossless` with a buffer size set
  in the connector config. If you read too slowly and the buffer fills, an
  `Event::Overflow` is sent and trading is refused with
  `Error::Degraded` until `reconcile`. Keep draining your channel.

  Subscribe BEFORE you place. The call returns when the stream is open (see Contract), so an
  order placed right after it cannot be missed. Changes that happened before the call are not
  delivered: to catch up, read `order_state` or
  `active_orders`. There is no way to end this subscription except
  shutting the connector down.

  Contract

  - When: once per connector, before `reconcile` and before the first
  `place`. Allowed while the gate is closed.

  - Guarantee: the call returns only when the stream is open at the broker. The broker’s own
  confirmation where the protocol has one (T-Bank, ALOR), else the open connection of the stream
  (BCS). Everything that happens to your orders after the return reaches you. A stream that is
  not open within `subscribe_ack_timeout` (3 seconds by default) is `Error::Transport` and nothing
  is left half open, so the call can be repeated. Finam is the exception: it is gRPC and may
  send the headers of a quiet stream only with its first message, so after the wait the call
  returns `Ok` and the stream goes on opening in the background, with no guarantee until it is
  open (`sub_status` of the connector shows it).

  - Guarantee: every change of your orders arrives, in order, or the loss is announced with
  `Event::Overflow` and trading stops until a reconcile. The
  subscription cannot be ended before shutdown.

  Errors

  - `Error::Unsupported` when the connector has no order stream.

  - `Error::Transport` when the subscription could not be made, also when the stream did not open
  in time.

  Examples

  See the example of the trait, which follows an order on the stream.

- `async fn reconcile( &self, ledger: &[ClientOrderId], ) -> Result<ReconcileReport, Error>`
  Compares your ledger with the broker and opens the trading gate.

  Call it once after the connector starts, and again after `Error::Degraded`. A successful
  call is what lifts `Error::NotReconciled` and `Error::Degraded`.

  `ledger` is the list of cids of orders you believe are working. Pass `&[]` if you have none.
  The report sorts the world into three:

  - `confirmed`: in your ledger and active at the broker.

  - `closed_offline`: in your ledger and NOT active at the broker (filled, cancelled, refused,
  or never received). The report does not say which. Ask `order_state`
  for each, and the executions for the fills.

  - `foreign`: active at the broker and not in your ledger. Leave them alone.

  The comparison is with `active_orders`, which lists plain orders.
  Keep the cids of stop orders out of the ledger and check them with
  `StopOrderExecutor::active_stop_orders` (not verified against the live API for every
  connector).

  Contract

  - When: after `subscribe_order_state` at start, again after
  `Error::Degraded` (read your channel to the end first), and whenever you doubt the ledger. It
  only reads, so any number of calls is safe.

  - Guarantee: on `Ok` the gate is open (lifts `Error::NotReconciled` and
  `Error::Degraded`). On an error the gate stays as it was and nothing is known about
  your orders. Orders are never moved or cancelled.

  Errors

  - `Error::RateLimited`, `Error::Transport` when the broker could not be read. The gate
  stays closed. Repeat later. A cid the broker answers “not found” for is not an error, it
  goes to `closed_offline`.

  Examples

### Trait PortfolioSource

```rust
pub trait PortfolioSource: Send + Sync {
    // Required methods
    async fn portfolio(
        &self,
    ) -> Result<Portfolio, Error>;
    async fn positions(
        &self,
    ) -> Result<Vec<Position>, Error>;
    async fn balances(
        &self,
    ) -> Result<Vec<Balance>, Error>;
    async fn subscribe_portfolio(
        &self,
        opts: SubscribeOpts,
    ) -> Result<SubscriptionId, Error>;
}
```
The account: money and positions, as a snapshot and as a stream.

- `async fn portfolio( &self, ) -> Result<Portfolio, Error>`
  Reads money and positions at one moment.

  Errors

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later.

  - `Error::Rejected` when the broker refuses the request.

  Examples

- `async fn positions( &self, ) -> Result<Vec<Position>, Error>`
  Reads the positions only.

  The BCS connector reads the whole portfolio and returns this part, so it costs the same as
  `portfolio`.

  Errors

  The same as `portfolio`.

  Examples

- `async fn balances( &self, ) -> Result<Vec<Balance>, Error>`
  Reads the money balances only.

  The BCS connector reads the whole portfolio and returns this part, so it costs the same as
  `portfolio`.

  Errors

  The same as `portfolio`.

  Examples

- `async fn subscribe_portfolio( &self, opts: SubscribeOpts, ) -> Result<SubscriptionId, Error>`
  Starts a stream of portfolio updates.

  Updates arrive as `Event::Portfolio`, or as
  `Event::Positions` and `Event::Balances`
  where the connector sends the parts separately (the T-Bank connector does). Each is a full
  snapshot of its part and replaces the previous one, so
  `Backpressure::Conflate` fits.

  The broker may push a first snapshot as soon as its portfolio channel connects, which can be
  before you subscribe. Such an update has no subscription (`Envelope::sub` is `None`). Subscribe
  right after the connector starts and then read `portfolio` once for a known
  starting point.

  Errors

  - `Error::Unsupported` when the connector has no portfolio stream.

  - `Error::Transport` when the subscription could not be made, also when the stream did not open
  in time (the call returns when the stream is open, as `subscribe_order_state`).

  Examples

  See the example of the trait, which receives an update.

### Trait StopOrderExecutor

```rust
pub trait StopOrderExecutor: Send + Sync {
    // Required methods
    async fn place_stop(
        &self,
        req: PlaceStopOrderRequest,
    ) -> Result<OrderAck, Error>;
    async fn cancel_stop(
        &self,
        stop: StopOrderRef,
    ) -> Result<(), Error>;
    async fn active_stop_orders(
        &self,
    ) -> Result<Vec<StopOrderSnapshot>, Error>;
    async fn subscribe_stop_orders(
        &self,
    ) -> Result<SubscriptionId, Error>;
}
```
Stop orders, take-profit and stop-limit - an extra capability next to `OrderExecutor`
(`UserDataCaps::supports_stop_orders` tells whether the broker has it).

A connector without stop orders implements the trait and answers `Error::Unsupported` to every
method. The command gate of the connector (`NotReconciled`, `Degraded`, shutdown) applies to
`place_stop` and `cancel_stop` like to `place` and `cancel`, and a stop order that got no answer
is `Error::Unknown`, never repeated by the library. A stop order waits at the broker and is not
an order on the exchange until the price crosses the trigger, see `StopOrderKind`.

- `async fn place_stop( &self, req: PlaceStopOrderRequest, ) -> Result<OrderAck, Error>`
  Puts a stop order and waits for the broker’s decision.

  The `cid` is the same kind of UUID as for plain orders and follows the same rule: it is sent
  at most once, a repeat is refused with `Error::Rejected` (BCS: `duplicate_place`). `Ok`
  means the broker accepted the stop order, it is waiting for its trigger. The library checks
  the shape of the request (a stop-limit needs `limit_price`) and nothing else: the trigger
  direction against the market price is the broker’s to judge.

  Contract

  - When: after the first successful `OrderExecutor::reconcile`, same gate as
  `OrderExecutor::place`. Write `req.cid` to your journal first.

  - Guarantee: at most one request with this cid ever leaves the process. After
  `Error::Unknown` look in `active_stop_orders`, do not place again.

  Errors

  - `Error::Unsupported` when the broker has no stop orders. Nothing was sent.

  - `Error::NotReconciled`, `Error::Degraded`, `Error::ShuttingDown`: the gate is closed,
  nothing was sent.

  - `Error::Rejected` when the request is wrong or the broker says no: a stop-limit without a
  limit price, a repeated cid, an expiry the broker does not have for this kind.

  - `Error::RateLimited`: not executed, wait `retry_after`. Use a new cid for the retry.

  - `Error::Unknown` when the request may have reached the broker and there is no answer. Do
  NOT place again. Look in `active_stop_orders`: a stop order with
  your cid is yours. A stop order with `cid: None` on the same instrument and side may be yours
  too, when the connector could not tell for sure (T-Bank does not return the cid and links it
  only when exactly one stop order fits). Then do not place another one under a new cid either.
  Only an empty list means that nothing is at the broker (look a second time after a few seconds
  first: a slow broker takes the order late), and then a new cid is safe.

  - `Error::Transport` when the failure happened before the request left.

  Examples

  See the example of the trait.

- `async fn cancel_stop( &self, stop: StopOrderRef, ) -> Result<(), Error>`
  Cancels a stop order that has not triggered.

  Name it by the cid you gave (`StopOrderRef::Cid`) or by the broker’s id
  (`StopOrderRef::Broker`). A cid is known from what this process has seen. After a restart a
  connector may not be able to find it, use the broker id then (from
  `active_stop_orders`).

  Contract

  - When: while the gate is open. After a restart name the stop by the broker’s id.

  - Guarantee: a repeated cancel creates nothing. After `Error::Unknown` look in
  `active_stop_orders` first.

  Errors

  - `Error::Unsupported` when the broker has no stop orders.

  - `Error::NotReconciled`, `Error::Degraded`, `Error::ShuttingDown`: the gate is closed.

  - `Error::Rejected` when the broker refuses: the stop order is unknown (BCS: `not_found`),
  or it already triggered.

  - `Error::RateLimited`: not executed, wait `retry_after`.

  - `Error::Unknown` when the answer is missing or leaves the stop order alive. Look in
  `active_stop_orders` before you decide.

  - `Error::Transport` when the failure happened before the request left.

  Examples

  See the example of the trait.

- `async fn active_stop_orders( &self, ) -> Result<Vec<StopOrderSnapshot>, Error>`
  Lists the stop orders that wait for their trigger.

  `cid` of an entry is `None` when the connector cannot tie the broker’s stop order to a cid you
  gave (placed by someone else, or placed before this process started and not echoed). Use
  `broker_id` then.

  Errors

  - `Error::Unsupported` when the broker has no stop orders.

  - `Error::RateLimited`, `Error::Transport`: wait or repeat later.

  Examples

  See the example of the trait.

- `async fn subscribe_stop_orders( &self, ) -> Result<SubscriptionId, Error>`
  Stream of stop order statuses. `Unsupported` where the broker has no such stream.

  The BCS connector has none: its order events channel is documented for plain orders only.
  Poll `active_stop_orders` instead. The library does not poll for you.

  Errors

  - `Error::Unsupported` when the broker has no such stream. BCS: capability
  `stop_order_stream`.

  - `Error::Transport` when the subscription could not be made.

  Examples

### Enum Backpressure

```rust
#[non_exhaustive]pub enum Backpressure {
    Conflate,
    Lossless {
        cap: usize,
    },
    DropNewest {
        cap: usize,
    },
}
```
What a subscription does when you read your channel slower than events arrive.

A connector never blocks its reading task on your channel. Each subscription has a buffer of its
own, and this policy says what happens when it is full:

PolicyMeaningUse for
`Conflate`Keep only the latest value, replace older ones.Order books, quotes, portfolio: full snapshots.
`DropNewest`When full, throw away the new event and count it.Streams you can lose, like a tape.
`Lossless`Must not lose. When full: an `Event::Overflow` is sent and the connector becomes degraded, trading commands then fail with `Error::Degraded` until `reconcile`.Own order states and own fills.

`Lossless` is the only honest answer for own orders and fills: the source cannot be slowed down,
so a hole must be admitted loudly and trading stopped, instead of continuing with a wrong picture.
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.

- `Conflate`
  Full snapshots: a new value replaces the previous one that was not read yet.

- `Lossless`
  Must not drop. When the buffer reaches `cap` the overflow is terminal for trading, see the
  table above.

- `cap: usize`
  How many events the subscription buffer holds.

- `DropNewest`
  When the buffer reaches `cap`, the new event is dropped and counted.

- `cap: usize`
  How many events the subscription buffer holds.

### Struct Balance

```rust
pub struct Balance {
    pub currency: String,
    pub available: Money,
    pub blocked: Money,
    pub ts: Timestamps,
}
```
Money of one currency on the account.

Amounts are in the currency itself (not in lots, not in kopecks), as the broker reports them.

- `currency: String`
  Currency, as the broker writes it (for example `RUB`).

- `available: Money`
  Money free to use.

- `blocked: Money`
  Money held back by the broker (for example for working orders).

- `ts: Timestamps`
  When received, and the broker time if given.

### Struct BookLevel

```rust
pub struct BookLevel {
    pub price: Price,
    pub qty: Qty,
}
```
One price level of an order book or a quote: a price and the total quantity waiting at it.

- `price: Price`
  Price of the level.

- `qty: Qty`
  Total quantity at this price, see `Qty` for the unit.

### Struct BrokerOrderId

```rust
pub struct BrokerOrderId(pub String);
```
The order id that the BROKER assigned, as a string.

Appears in `OrderAck::broker_id` and `OrderSnapshot::broker_id`. It is the id that the
broker’s own app and reports show. Its format differs by broker, so treat it as opaque. For
your own bookkeeping use the `ClientOrderId`, which you control.

- `0: String`

- `pub fn new(id: impl Into<String>) -> Self`
  Wraps a string. Nothing is checked.

- `pub fn as_str(&self) -> &str`
  The id as the broker writes it.

### Struct Candle

```rust
pub struct Candle {
    pub instrument: InstrumentId,
    pub timeframe: Timeframe,
    pub open: Price,
    pub high: Price,
    pub low: Price,
    pub close: Price,
    pub volume: Qty,
    pub open_ts: UnixNanos,
    pub close_ts: UnixNanos,
    pub ts: Timestamps,
}
```
One OHLC bar of an instrument.

From `MarketDataSource::candles` (history) or as
`Event::Candle` from a candle subscription. A subscription sends the
latest bar, so the same bar can arrive several times while it forms (not verified against the
live API for every broker). Use `open_ts` to tell bars apart.

- `instrument: InstrumentId`
  The instrument of the bar.

- `timeframe: Timeframe`
  Length of the bar.

- `open: Price`
  First price of the bar.

- `high: Price`
  Highest price of the bar.

- `low: Price`
  Lowest price of the bar.

- `close: Price`
  Last price of the bar.

- `volume: Qty`
  Traded quantity in the bar, see `Qty` for the unit.

- `open_ts: UnixNanos`
  Start of the bar.

- `close_ts: UnixNanos`
  End of the bar.

- `ts: Timestamps`
  When received, and the broker time if given.

### Enum Charset

```rust
pub enum Charset {
    Numeric,
    AlnumUpper,
    Ascii,
    HexLower,
}
```
The characters a broker accepts in its client order id field.

- `Numeric`
  Digits `0-9` only.

- `AlnumUpper`
  Digits and capital letters `A-Z`.

- `Ascii`
  Any printable ASCII.

- `HexLower`
  Digits and lowercase `a-f`.

### Struct CidCaps

```rust
pub struct CidCaps {
    pub max_len: usize,
    pub charset: Charset,
    pub native_bits: u32,
    pub lossless: bool,
}
```
What the broker’s client order id field can hold, declared by a `CidCodec`.

The connector checks it at start. If the field cannot carry a UUID without loss, that is a
configuration error at start (`Error::Config`), never a degradation at run
time: an order whose cid cannot be read back cannot be tied to your ledger.

- `max_len: usize`
  Longest native string the broker’s field accepts, in characters.

- `charset: Charset`
  Characters the field accepts.

- `native_bits: u32`
  How many bits of the UUID the broker field can keep. 128 means the whole UUID.

- `lossless: bool`
  `true` when `CidCodec::decode` always gives back what `CidCodec::encode` was given.

### Enum CidError

```rust
pub enum CidError {
    TooLong {
        len: usize,
        max: usize,
    },
    InvalidChar {
        ch: char,
        charset: Charset,
    },
    NotLossless,
}
```
Why a `CidCodec` cannot encode or accept a cid.

These are setup problems of a codec, found by the connector at start, so they are reported as
`Error::Config`.

- `TooLong`
  The encoded string is longer than the broker’s field.

- `len: usize`
  Length of the encoded string.

- `max: usize`
  Longest string the field takes, see `CidCaps::max_len`.

- `InvalidChar`
  The encoded string has a character the broker’s field does not accept.

- `ch: char`
  The offending character.

- `charset: Charset`
  The set the field accepts.

- `NotLossless`
  The codec cannot give the UUID back from the native form, but the connector needs that.

### Type Alias ClientOrderId

```rust
pub struct ClientOrderId(/* private fields */);
```
The id of an order that YOU choose: a UUID v4, made by the caller for each new order.

The library never makes one for you. Use a fresh `ClientOrderId::new_v4()` for every new order,
including after an `Error::Unknown` or any other failed place: a cid
that was sent once is never sent again by a connector (a second place with the same cid is
refused with `Error::Rejected`, code `duplicate_place`). Keep the cid in
your own ledger, it is the key for `OrderExecutor::order_state`,
`OrderExecutor::cancel` and
`OrderExecutor::reconcile`.

It is a plain `uuid::Uuid`. The form that goes to a broker is made by a
`CidCodec`.

### Enum ConfigError

```rust
pub enum ConfigError {
    Message(String),
}
```
A configuration problem: a value that is missing, malformed, or of the wrong kind for this broker.

Carried by `Error::Config`. The text is for a human (a log line), it is not a stable code.

- `Message(String)`
  What is wrong, in words.

### Struct Decimal

```rust
pub struct Decimal { /* private fields */ }
```
The exact decimal number inside `Price`, `Qty` and `Money` (from the `rust_decimal` crate).
`Decimal` represents a 128 bit representation of a fixed-precision decimal number.
The finite set of values of type `Decimal` are of the form m / 10e,
where m is an integer such that -296 < m < 296, and e is an integer
between 0 and 28 inclusive.

- `pub const MIN: Decimal = MIN`
  The smallest value that can be represented by this decimal type.

  Examples

  Basic usage:

- `pub const MAX: Decimal = MAX`
  The largest value that can be represented by this decimal type.

  Examples

  Basic usage:

- `pub const ZERO: Decimal = ZERO`
  A constant representing 0.

  Examples

  Basic usage:

- `pub const ONE: Decimal = ONE`
  A constant representing 1.

  Examples

  Basic usage:

- `pub const NEGATIVE_ONE: Decimal = NEGATIVE_ONE`
  A constant representing -1.

  Examples

  Basic usage:

- `pub const TWO: Decimal = TWO`
  A constant representing 2.

  Examples

  Basic usage:

- `pub const TEN: Decimal = TEN`
  A constant representing 10.

  Examples

  Basic usage:

- `pub const ONE_HUNDRED: Decimal = ONE_HUNDRED`
  A constant representing 100.

  Examples

  Basic usage:

- `pub const ONE_THOUSAND: Decimal = ONE_THOUSAND`
  A constant representing 1000.

  Examples

  Basic usage:

- `pub const MAX_SCALE: u32 = MAX_SCALE_U32`
  The maximum supported scale value.

  Some operations, such as `Self::rescale` may accept larger scale values, but these
  operations will result in a final value with a scale no larger than this.

  Note that the maximum scale is not the same as the maximum possible numeric precision in
  base-10.

- `pub fn new(num: i64, scale: u32) -> Decimal`
  Returns a `Decimal` with a 64 bit `m` representation and corresponding `e` scale.

  Arguments

  - `num` - An i64 that represents the `m` portion of the decimal number

  - `scale` - A u32 representing the `e` portion of the decimal number.

  Panics

  This function panics if `scale` is > `Self::MAX_SCALE`.

  Example

- `pub const fn try_new(num: i64, scale: u32) -> Result<Decimal, Error>`
  Checked version of `Self::new`. Will return an error instead of panicking at run-time.

  Example

- `pub fn from_i128_with_scale(num: i128, scale: u32) -> Decimal`
  Creates a `Decimal` using a 128 bit signed `m` representation and corresponding `e` scale.

  Arguments

  - `num` - An i128 that represents the `m` portion of the decimal number

  - `scale` - A u32 representing the `e` portion of the decimal number.

  Panics

  This function panics if `scale` is > `Self::MAX_SCALE` or if `num` exceeds the maximum
  supported 96 bits.

  Example

- `pub const fn try_from_i128_with_scale( num: i128, scale: u32, ) -> Result<Decimal, Error>`
  Checked version of `Decimal::from_i128_with_scale`. Will return `Err` instead
  of panicking at run-time.

  Example

- `pub const fn from_parts( lo: u32, mid: u32, hi: u32, negative: bool, scale: u32, ) -> Decimal`
  Returns a `Decimal` using the instances constituent parts.

  Arguments

  - `lo` - The low 32 bits of a 96-bit integer.

  - `mid` - The middle 32 bits of a 96-bit integer.

  - `hi` - The high 32 bits of a 96-bit integer.

  - `negative` - `true` to indicate a negative number.

  - `scale` - A power of 10 ranging from 0 to `Self::MAX_SCALE`.

  Example

- `pub fn from_scientific(value: &str) -> Result<Decimal, Error>`
  Returns a `Result` which if successful contains the `Decimal` constitution of
  the scientific notation provided by `value`.

  Arguments

  - `value` - The scientific notation of the `Decimal`.

  Example

- `pub fn from_scientific_lossy(value: &str) -> Result<Decimal, Error>`
  Returns a `Result` which if successful contains the `Decimal` constitution of
  the scientific notation provided by `value`. If the exponent is negative and
  the given base and exponent would exceed Decimal::MAX_SCALE then this
  functions attempts to round the base to fit.

  Arguments

  - `value` - The scientific notation of the `Decimal`.

  Example

- `pub fn from_str_radix(str: &str, radix: u32) -> Result<Decimal, Error>`
  Converts a string slice in a given base to a decimal.

  The string is expected to be an optional + sign followed by digits.
  Digits are a subset of these characters, depending on radix, and will return an error if outside
  the expected range:

  - 0-9

  - a-z

  - A-Z

  Examples

  Basic usage:

- `pub fn from_str_exact(str: &str) -> Result<Decimal, Error>`
  Parses a string slice into a decimal. If the value underflows and cannot be represented with the
  given scale then this will return an error.

  Examples

  Basic usage:

- `pub fn array_string(&self) -> impl AsRef<str>`
  Returns a string representation that is similar to `alloc::string::ToString` but
  doesn’t require a heap allocation.

  Examples

- `pub const fn scale(&self) -> u32`
  Returns the scale of the decimal number, otherwise known as `e`.

  Example

- `pub const fn mantissa(&self) -> i128`
  Returns the mantissa of the decimal number.

  Example

- `pub const fn is_zero(&self) -> bool`
  Returns true if this Decimal number is equivalent to zero.

  Example

- `pub fn is_integer(&self) -> bool`
  Returns true if this Decimal number has zero fractional part (is equal to an integer)

  Example

- `pub fn set_sign(&mut self, positive: bool)`
  An optimized method for changing the sign of a decimal number.

  Arguments

  - `positive`: true if the resulting decimal should be positive.

  Example

- `pub fn set_sign_positive(&mut self, positive: bool)`
  An optimized method for changing the sign of a decimal number.

  Arguments

  - `positive`: true if the resulting decimal should be positive.

  Example

- `pub fn set_sign_negative(&mut self, negative: bool)`
  An optimized method for changing the sign of a decimal number.

  Arguments

  - `negative`: true if the resulting decimal should be negative.

  Example

- `pub fn set_scale(&mut self, scale: u32) -> Result<(), Error>`
  An optimized method for changing the scale of a decimal number.

  Arguments

  - `scale`: the new scale of the number

  Example

- `pub fn rescale(&mut self, scale: u32)`
  Modifies the `Decimal` towards the desired scale, attempting to do so without changing the
  underlying number itself.

  Setting the scale to something less then the current `Decimal`s scale will
  cause the newly created `Decimal` to perform rounding using the `MidpointAwayFromZero` strategy.

  Scales greater than the maximum precision that can be represented by `Decimal` will be
  automatically rounded to either `Self::MAX_SCALE` or the maximum precision that can
  be represented with the given mantissa.

  Arguments

  - `scale`: The desired scale to use for the new `Decimal` number.

  Example

- `pub const fn serialize(&self) -> [u8; 16]`
  Returns a serialized version of the decimal number.
  The resulting byte array will have the following representation:

  - Bytes 1-4: flags

  - Bytes 5-8: lo portion of `m`

  - Bytes 9-12: mid portion of `m`

  - Bytes 13-16: high portion of `m`

- `pub fn deserialize(bytes: [u8; 16]) -> Decimal`
  Deserializes the given bytes into a decimal number.
  The deserialized byte representation must be 16 bytes and adhere to the following convention:

  - Bytes 1-4: flags

  - Bytes 5-8: lo portion of `m`

  - Bytes 9-12: mid portion of `m`

  - Bytes 13-16: high portion of `m`

- `pub fn is_negative(&self) -> bool`
  Returns `true` if the decimal is negative.

- `pub fn is_positive(&self) -> bool`
  Returns `true` if the decimal is positive.

- `pub const fn is_sign_negative(&self) -> bool`
  Returns `true` if the sign bit of the decimal is negative.

  Example

- `pub const fn is_sign_positive(&self) -> bool`
  Returns `true` if the sign bit of the decimal is positive.

  Example

- `pub const fn min_value() -> Decimal`
  Returns the minimum possible number that `Decimal` can represent.

- `pub const fn max_value() -> Decimal`
  Returns the maximum possible number that `Decimal` can represent.

- `pub fn trunc(&self) -> Decimal`
  Returns a new `Decimal` integral with no fractional portion.
  This is a true truncation whereby no rounding is performed.

  Example

- `pub fn trunc_with_scale(&self, scale: u32) -> Decimal`
  Returns a new `Decimal` with the fractional portion delimited by `scale`.
  This is a true truncation whereby no rounding is performed.

  Example

- `pub fn fract(&self) -> Decimal`
  Returns a new `Decimal` representing the fractional portion of the number.

  Example

- `pub fn abs(&self) -> Decimal`
  Computes the absolute value of `self`.

  Example

- `pub fn floor(&self) -> Decimal`
  Returns the largest integer less than or equal to a number.

  Example

- `pub fn ceil(&self) -> Decimal`
  Returns the smallest integer greater than or equal to a number.

  Example

- `pub fn max(self, other: Decimal) -> Decimal`
  Returns the maximum of the two numbers.

- `pub fn min(self, other: Decimal) -> Decimal`
  Returns the minimum of the two numbers.

- `pub fn normalize(&self) -> Decimal`
  Strips any trailing zero’s from a `Decimal` and converts -0 to 0.

  Example

- `pub fn normalize_assign(&mut self)`
  An in place version of `normalize`. Strips any trailing zero’s from a `Decimal` and converts -0 to 0.

  Example

- `pub fn round(&self) -> Decimal`
  Returns a new `Decimal` number with no fractional portion (i.e. an integer).
  Rounding currently follows “Bankers Rounding” rules. e.g. 6.5 -> 6, 7.5 -> 8

  Example

- `pub fn round_dp_with_strategy( &self, dp: u32, strategy: RoundingStrategy, ) -> Decimal`
  Returns a new `Decimal` number with the specified number of decimal points for fractional
  portion.
  Rounding is performed using the provided [`RoundingStrategy`]

  Arguments

  - `dp`: the number of decimal points to round to.

  - `strategy`: the [`RoundingStrategy`] to use.

  Example

- `pub fn round_dp(&self, dp: u32) -> Decimal`
  Returns a new `Decimal` number with the specified number of decimal points for fractional portion.
  Rounding currently follows “Bankers Rounding” rules. e.g. 6.5 -> 6, 7.5 -> 8

  Arguments

  - `dp`: the number of decimal points to round to.

  Example

- `pub fn round_sf(&self, digits: u32) -> Option<Decimal>`
  Returns `Some(Decimal)` number rounded to the specified number of significant digits. If
  the resulting number is unable to be represented by the `Decimal` number then `None` will
  be returned.
  When the number of significant figures of the `Decimal` being rounded is greater than the requested
  number of significant digits then rounding will be performed using `MidpointNearestEven` strategy.

  Arguments

  - `digits`: the number of significant digits to round to.

  Remarks

  A significant figure is determined using the following rules:

  - Non-zero digits are always significant.

  - Zeros between non-zero digits are always significant.

  - Leading zeros are never significant.

  - Trailing zeros are only significant if the number contains a decimal point.

  Example

- `pub fn round_sf_with_strategy( &self, digits: u32, strategy: RoundingStrategy, ) -> Option<Decimal>`
  Returns `Some(Decimal)` number rounded to the specified number of significant digits. If
  the resulting number is unable to be represented by the `Decimal` number then `None` will
  be returned.
  When the number of significant figures of the `Decimal` being rounded is greater than the requested
  number of significant digits then rounding will be performed using the provided [RoundingStrategy].

  Arguments

  - `digits`: the number of significant digits to round to.

  - `strategy`: if required, the rounding strategy to use.

  Remarks

  A significant figure is determined using the following rules:

  - Non-zero digits are always significant.

  - Zeros between non-zero digits are always significant.

  - Leading zeros are never significant.

  - Trailing zeros are only significant if the number contains a decimal point.

  Example

- `pub const fn unpack(&self) -> UnpackedDecimal`
  Convert `Decimal` to an internal representation of the underlying struct. This is useful
  for debugging the internal state of the object.

  Important Disclaimer

  This is primarily intended for library maintainers. The internal representation of a
  `Decimal` is considered “unstable” for public use.

  Example

- `pub fn from_f32_retain(n: f32) -> Option<Decimal>`
  Parses a 32-bit float into a Decimal number whilst retaining any non-guaranteed precision.

  Typically when a float is parsed in Rust Decimal, any excess bits (after ~7.22 decimal points for
  f32 as per IEEE-754) are removed due to any digits following this are considered an approximation
  at best. This function bypasses this additional step and retains these excess bits.

  Example

- `pub fn from_f64_retain(n: f64) -> Option<Decimal>`
  Parses a 64-bit float into a Decimal number whilst retaining any non-guaranteed precision.

  Typically when a float is parsed in Rust Decimal, any excess bits (after ~15.95 decimal points for
  f64 as per IEEE-754) are removed due to any digits following this are considered an approximation
  at best. This function bypasses this additional step and retains these excess bits.

  Example

- `pub fn as_i128(&self) -> i128`
  Converts this `Decimal` to an `i128`, truncating any fractional part.

  This is the infallible equivalent of `ToPrimitive::to_i128`.

- `pub fn as_f64(&self) -> f64`
  Converts this `Decimal` to an `f64`.

  This is the infallible equivalent of `ToPrimitive::to_f64`.

- `pub fn checked_add(self, other: Decimal) -> Option<Decimal>`
  Checked addition. Computes `self + other`, returning `None` if overflow occurred.

- `pub fn saturating_add(self, other: Decimal) -> Decimal`
  Saturating addition. Computes `self + other`, saturating at the relevant upper or lower boundary.

- `pub fn checked_mul(self, other: Decimal) -> Option<Decimal>`
  Checked multiplication. Computes `self * other`, returning `None` if overflow occurred.

- `pub fn saturating_mul(self, other: Decimal) -> Decimal`
  Saturating multiplication. Computes `self * other`, saturating at the relevant upper or lower boundary.

- `pub fn checked_sub(self, other: Decimal) -> Option<Decimal>`
  Checked subtraction. Computes `self - other`, returning `None` if overflow occurred.

- `pub fn saturating_sub(self, other: Decimal) -> Decimal`
  Saturating subtraction. Computes `self - other`, saturating at the relevant upper or lower boundary.

- `pub fn checked_div(self, other: Decimal) -> Option<Decimal>`
  Checked division. Computes `self / other`, returning `None` if overflow occurred.

- `pub fn checked_rem(self, other: Decimal) -> Option<Decimal>`
  Checked remainder. Computes `self % other`, returning `None` if overflow occurred.

### Constant ENVELOPE_SCHEMA

```rust
pub const ENVELOPE_SCHEMA: u16 = 1;
```
The version of the `Envelope` format, now `1`.

It grows only forward, with additive changes. If you store or forward envelopes, refuse a
`schema` higher than the one you were built for, and ignore fields you do not know.

### Struct Envelope

```rust
#[non_exhaustive]pub struct Envelope {
    pub schema: u16,
    pub source: Arc<str>,
    pub sub: Option<SubscriptionId>,
    pub ts: Timestamps,
    pub payload: Event,
}
```
One event with its address: which connector, which subscription, when, and what.

This is the item type of the `tokio::sync::mpsc` channel you give to a connector at start. All
subscriptions of the connector write into that one channel, so route by `sub`. Read the channel
steadily: a connector never blocks on it and applies the
`Backpressure` policy of the subscription instead.
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional `Struct { .. }` syntax; cannot be matched against without a wildcard `..`; and struct update syntax will not work.

- `schema: u16`
  Format version, see `ENVELOPE_SCHEMA`.

- `source: Arc<str>`
  The label you gave the connector in its config (for example `"bcs-md"`). The library does
  not interpret it. It lets you tell connectors apart when several write into one channel.

- `sub: Option<SubscriptionId>`
  The subscription this event belongs to. `None` for an event that belongs to no subscription.

- `ts: Timestamps`
  When the envelope was built, which is when it left the subscription buffer, not when the
  bytes arrived. For a slow reader that is later than the arrival. The data inside has its own
  timestamps.

- `payload: Event`
  What happened.

- `pub fn new(source: Arc<str>, ts: Timestamps, payload: Event) -> Self`
  An envelope of the current schema with no subscription. Add one with `with_sub`.

- `pub fn with_sub(self, sub: SubscriptionId) -> Self`
  Sets the subscription the envelope belongs to.

- `pub fn with_sub_opt(self, sub: Option<SubscriptionId>) -> Self`
  Like `with_sub` for a subscription that may be absent: `None` leaves the envelope
  without one.

### Enum Error

```rust
#[non_exhaustive]pub enum Error {
    Config(ConfigError),
    Transport(TransportError),
    RateLimited {
        retry_after: Duration,
    },
    Rejected {
        code: String,
        message: String,
    },
    Unknown,
    Unsupported {
        capability: Cow<'static, str>,
    },
    NotReconciled,
    Degraded,
    ShuttingDown,
}
```
What can go wrong in a connector call.

The texts printed by `Display` are the ones on the errors page of the book (`docs/src/errors.md`),
with what they mean and what to do. A test keeps the two in step.

Each variant says what happened and what the caller does next. `Error::class` groups them.
Reading calls (order book, history, portfolio) return only the variants that make sense for a
read: `Error::Config`, `Error::Transport`, `Error::RateLimited`, `Error::Rejected` and
`Error::Unsupported`. The gate variants (`Error::NotReconciled`, `Error::Degraded`,
`Error::ShuttingDown`) come from trading commands.
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.

- `Config(ConfigError)`
  The setup is wrong: a bad config value, or an input that cannot work with this broker (for
  example an `InstrumentId` whose `NativeId` is of
  another broker’s kind).

  The broker refusing the credentials (a wrong or expired token, an authentication that failed) is
  this variant too, at every broker: fix the token, a repeat will not help.

  Nothing was sent. The call will fail the same way until the config or the input is fixed.

- `Transport(TransportError)`
  The network or the broker’s endpoint failed in a way that is not an answer: connect, TLS,
  timeout of a read, a stream cut, an unreadable reply. (A refused authentication is
  `Error::Config`.)

  For a reading call, repeat it later. For a trading command the connector returns
  `Error::Unknown` instead whenever the command might have reached the broker, so
  `Transport` on a command means the connector has no sign that it did (for example the
  authentication failed). Even then, look at the order before you send a command again, and use
  a new `cid` for a new place.

- `RateLimited`
  A rate limit stopped the call: the local limiter of the connector or the broker’s own answer
  (HTTP 429 or the equivalent).

  Wait `retry_after`, then repeat. A command refused by the local limiter was not sent. A
  connector may still have used up the `cid` (see its docs), so for a place the safe rule is a
  new `cid`. The library does not wait and repeat for you.

- `retry_after: Duration`
  How long to wait before the next try. A hint from the limiter or the broker, not a
  promise that the next call passes.

- `Rejected`
  The broker (or the connector, before sending) said no to this request, and nothing was done.

  The request is wrong or not allowed: a price outside the band, no money, an unknown order,
  an order that is already filled, a missing price on a limit order. Change the request. Do not
  repeat it as it is.

  `code` is a short machine-readable label that the connector sets (for example `validation`,
  `missing_price`, `invalid_qty`, `not_found`, `already_filled`, `duplicate_place`, `http_400`). `message` is
  the broker’s text or the connector’s explanation, for a human. The set of codes belongs to the
  connector and is not fixed across brokers, except `NOT_FOUND` (the same at every broker: use
  `Error::is_not_found`) and the codes the shared set of checks relies on (`duplicate_place`,
  `invalid_qty`). Do not parse `message`.

- `code: String`
  Short label of the reason, set by the connector. Safe to compare with `==`.

- `message: String`
  Explanation for a human, usually the broker’s own text.

- `Unknown`
  A command (place, cancel, replace, place_stop, cancel_stop) was sent, and no answer says
  whether the broker did it: a timeout, a connection cut after sending, an HTTP 5xx, an answer
  that cannot be read.

  The order may exist, may be cancelled, or may never have arrived. The library NEVER repeats
  the command, because a repeated place could create a second order. Do the same:

  - Do not send the command again.

  - Ask the broker: `OrderExecutor::order_state` with the
  `cid`, or `OrderExecutor::active_orders`, or look for
  the fills in `ExecutionSource::executions`.

  - Only when the broker has no such order, place a new one under a NEW `cid`.

  Never turned into `Error::Transport` or `Error::Rejected`: those would say “nothing
  happened”, and here that is not known.

- `Unsupported`
  This connector or this broker does not have the capability, so the call was not made.

  Returned at once, without a network call. `capability` is a short name of what is missing
  (for example `stop_order_stream`). The library does not emulate a missing stream by polling.
  If you need the data, poll yourself. See also `UserDataCaps` and
  `MarketDataCaps`, which declare what a connector can do.

- `capability: Cow<'static, str>`
  Short name of the missing capability. Safe to compare with `==` against a `&str`.
  A `Cow` so that the error survives serialization (for example across a process boundary).

- `NotReconciled`
  The trading gate is closed: this connector has not run `OrderExecutor::reconcile`
  successfully yet.

  Trading commands wait for the first reconcile, so that the connector knows which orders are
  already at the broker before it puts a new one. Call `reconcile` with your ledger (an empty
  slice if you have none), then repeat the command. Nothing was sent.

- `Degraded`
  The trading gate is closed because the connector lost events it could not afford to lose: a
  `Backpressure::Lossless` subscription overflowed (an
  `Event::Overflow` was sent).

  The local state of orders may have a hole. Drain your event channel, then call
  `OrderExecutor::reconcile`: a successful reconcile opens the gate again.
  Nothing was sent.

- `ShuttingDown`
  The connector is shutting down and takes no new trading commands.

  Final for this connector object. Orders already at the exchange are not touched by a
  shutdown. Start a new connector if you need to trade. Nothing was sent.

- `pub fn not_found(message: impl Into<String>) -> Self`
  A `Error::Rejected` with the code `NOT_FOUND`: the broker does not know the order, the cid or
  the instrument. Every connector uses this one for “not found”.

- `pub fn is_not_found(&self) -> bool`
  Whether this is the “not found” refusal (`Error::Rejected` with the code `NOT_FOUND`).

  Examples

- `pub fn class(&self) -> ErrorClass`
  The coarse class of this error: may the call be tried again, or not.

  VariantClass
  `Error::Transport`, `Error::RateLimited``ErrorClass::Retryable`
  `Error::Unknown``ErrorClass::Unknown`
  all others`ErrorClass::Fatal`

  The class says what is possible in principle, and it is the class of a read. A generic retry
  loop must not use it for a trading command: `Error::Transport` is `Retryable` here, and a
  command that failed with it may still have reached the broker. For a command ask
  `retryable_for_command`. The library itself never repeats a
  command, whatever the class.

  Examples

- `pub fn retryable_for_command(&self) -> bool`
  May a trading command that failed with this error be sent again, after the wait the error names?

  `true` only for `Error::RateLimited`: the broker (or the local limiter) refused before it acted,
  so the same cancel can be sent again after `retry_after`. A place needs a new `cid`, because the
  connector may have used the old one up. Everything else is `false`: `Error::Transport` and
  `Error::Unknown` mean the command may have reached the broker, so look at the order first
  (`OrderExecutor::order_state`), and the other variants
  are final until the setup, the gate or the request changes.

  Use this, not `class`, in the retry loop of a command.

  Examples

### Enum ErrorClass

```rust
#[non_exhaustive]pub enum ErrorClass {
    Retryable,
    Fatal,
    Unknown,
}
```
The coarse answer to “may I try this call again”: see `Error::class`.
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.

- `Retryable`
  The call failed for a reason that can pass: the network, a rate limit. Reading calls may be
  repeated, after the delay in `Error::RateLimited` if there is one. For a command this class is
  not a licence to repeat: first find out what the broker has (see `Error::Transport`), and
  ask `Error::retryable_for_command` in a retry loop of commands.

- `Fatal`
  The same call will fail again until something else changes: a setup, a refused request, a
  missing capability, a gate that is closed. Do not repeat it unchanged.

- `Unknown`
  The outcome of a command is not known: `Error::Unknown`. Repeating the command could create
  a second order. The library never repeats it and neither should the caller before it knows
  the state of the order.

### Enum Event

```rust
#[non_exhaustive]pub enum Event {
    OrderBook(OrderBook),
    MarketTrade(MarketTrade),
    Quote(Quote),
    Candle(Candle),
    Order(OrderSnapshot),
    Execution(Execution),
    Portfolio(Portfolio),
    Positions(Vec<Position>),
    Balances(Vec<Balance>),
    Resync {
        sub: SubscriptionId,
        instrument: InstrumentId,
        reason: ResyncReason,
    },
    Overflow {
        sub: SubscriptionId,
        lost_from: UnixNanos,
    },
    Reconnected {
        sub: SubscriptionId,
        gap_from: UnixNanos,
    },
}
```
The payload of an `Envelope`: one thing that happened.

The same set for every broker. Which events you get depends on what you subscribed to. A
subscription’s events carry its `SubscriptionId` in `Envelope::sub`.
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.

- `OrderBook(OrderBook)`
  An order book from an order book subscription.

- `MarketTrade(MarketTrade)`
  A deal of the public tape from a market trades subscription.

- `Quote(Quote)`
  The best bid and ask from a quotes subscription.

- `Candle(Candle)`
  A bar from a candles subscription.

- `Order(OrderSnapshot)`
  A change of one of your orders, from the order state subscription. Carries the cid.

- `Execution(Execution)`
  A fill of one of your orders, from the executions subscription.

- `Portfolio(Portfolio)`
  The whole account, from a portfolio subscription. Replaces the previous one.

- `Positions(Vec<Position>)`
  Positions only, from a portfolio subscription of a connector that sends the parts
  separately (the T-Bank connector does).

- `Balances(Vec<Balance>)`
  Balances only, from a portfolio subscription of a connector that sends the parts
  separately (the T-Bank connector does).

- `Resync`
  What you hold for this instrument on this subscription is void, a new snapshot follows. Sent
  after a sequence gap or a reconnect.

- `sub: SubscriptionId`
  The subscription that must start over.

- `instrument: InstrumentId`
  The instrument whose data is void.

- `reason: ResyncReason`
  Why.

- `Overflow`
  A `Lossless` subscription overflowed: events were lost and
  cannot be recovered from the stream.

  The connector is now degraded and refuses trading commands with
  `Error::Degraded`. Read the state from the broker
  (`OrderExecutor::active_orders`,
  `ExecutionSource::executions`), then call
  `OrderExecutor::reconcile`. The notice can arrive before
  older events of the same subscription that are still waiting in the buffer.

- `sub: SubscriptionId`
  The subscription that lost events.

- `lost_from: UnixNanos`
  Wall clock when the loss was found. The connector sets it to “now” at the overflow, so it
  is an upper bound for where the hole starts, not the time of the first lost event.

- `Reconnected`
  A stream of your orders or fills was cut and opened again: whatever happened to your orders in
  the gap never reached you.

  Sent for the order state and the executions subscriptions (the user data streams that carry
  money), after the stream is open again. Market data streams have `Event::Resync` instead, and
  the portfolio stream needs nothing because every message of it is a whole state.

  The connector is now degraded, like after an `Event::Overflow`: it refuses trading commands
  with `Error::Degraded` until you read the state from the broker
  (`OrderExecutor::active_orders`,
  `OrderExecutor::order_state`,
  `ExecutionSource::executions`) and call
  `OrderExecutor::reconcile`.

- `sub: SubscriptionId`
  The subscription whose stream was cut.

- `gap_from: UnixNanos`
  Wall clock when the connector saw the cut. The hole starts at or before it, so read the
  history from some time before this moment.

### Struct Execution

```rust
pub struct Execution {
    pub cid: Option<ClientOrderId>,
    pub broker_order_id: BrokerOrderId,
    pub instrument: InstrumentId,
    pub side: Side,
    pub price: Price,
    pub qty: Qty,
    pub commission: Money,
    pub trade_id: String,
    pub ts: Timestamps,
}
```
One fill of one of your orders: a part of an order that traded.

From `ExecutionSource::executions` (history) and as
`Event::Execution` from
`ExecutionSource::subscribe_executions`. An order
can have many executions. A public deal of the tape is `MarketTrade`, a
different type.

- `cid: Option<ClientOrderId>`
  Your cid of the order, when the broker gives it back. `None` for history from a broker that
  does not echo it (the BCS history has none). Match by `broker_order_id` then.

- `broker_order_id: BrokerOrderId`
  The broker’s id of the order that traded.

- `instrument: InstrumentId`
  What traded.

- `side: Side`
  Buy or sell.

- `price: Price`
  Price of this fill.

- `qty: Qty`
  Quantity of this fill (not of the whole order), see `Qty` for the unit.

- `commission: Money`
  Commission charged for this fill, in money, as the broker reports it. zero when the broker
  gives none (the BCS history has none), so zero does not prove a free fill.

- `trade_id: String`
  The broker’s or exchange’s id of the fill. Unique per fill, use it to drop duplicates.

- `ts: Timestamps`
  When received, and the broker time if given.

### Struct HexUuidCodec

```rust
pub struct HexUuidCodec;
```
A lossless codec: the UUID as 32 lowercase hex digits, no hyphens.

Meant for tests and for brokers whose field holds at least 128 bits. `decode`
is more tolerant than `encode`: it also reads the hyphenated form and capital letters.

### Struct Instrument

```rust
pub struct Instrument {
    pub id: InstrumentId,
    pub name: String,
    pub isin: Option<String>,
    pub kind: InstrumentKind,
    pub currency: String,
    pub exchange: String,
}
```
A directory entry: what an instrument is.

- `id: InstrumentId`
  The instrument, ready to pass to other methods.

- `name: String`
  Display name.

- `isin: Option<String>`
  ISIN, when the broker gives it.

- `kind: InstrumentKind`
  Class of the instrument.

- `currency: String`
  Currency of trading, as the broker writes it (for example `RUB`).

- `exchange: String`
  Exchange or board, as the broker writes it.

### Struct InstrumentId

```rust
pub struct InstrumentId {
    pub key: InstrumentKey,
    pub native: NativeId,
}
```
An instrument as both the library and the broker name it: the same key for every broker, and the
form the broker wants.

Every connector method that takes an instrument takes an `InstrumentId`. The `key` is for you,
the `native` is for the connector. Get one from `InstrumentSource::find`,
or build it by hand when you know the broker’s naming.

- `key: InstrumentKey`
  The library’s name, the same for every broker.

- `native: NativeId`
  The broker’s name, what the connector sends.

- `pub fn new(key: InstrumentKey, native: NativeId) -> Self`
  An id from a key and the broker’s native form.

### Struct InstrumentKey

```rust
pub struct InstrumentKey {
    pub symbol: String,
    pub mic: String,
}
```
The library’s own name of an instrument, the same for every broker: a symbol and a market.

Printed as `SYMBOL@MIC` by `display`. Use it to compare, log and index
instruments. It is NOT what you send to a broker: that is `NativeId`. The `mic` is the market
code as the connector sets it. The BCS examples in this documentation put the board code
(`TQBR`) there. Not verified that every connector uses an ISO 10383 MIC.

- `symbol: String`
  Ticker of the instrument, for example `SBER`.

- `mic: String`
  Market (or board) code, for example `TQBR`.

- `pub fn new(symbol: impl Into<String>, mic: impl Into<String>) -> Self`
  A key from a symbol and a market code.

- `pub fn display(&self) -> String`
  The key as text, `SYMBOL@MIC`.

### Enum InstrumentKind

```rust
pub enum InstrumentKind {
    Stock,
    Bond,
    Option,
    Future,
    Perpetual,
    Etf,
    MutualFund,
    Currency,
    Other,
}
```
The class of an instrument, for directory search and as a field of `Instrument`.

A connector may map only some kinds in a search. The BCS connector answers
`Error::Unsupported` to a search by `Option` or `Other`.

- `Stock`
  A share.

- `Bond`
  A bond.

- `Option`
  An option.

- `Future`
  A futures contract with an expiry.

- `Perpetual`
  A perpetual futures contract: it has no expiry, and its price is held to the spot price by a funding payment
  (Bybit `LinearPerpetual`). Added for the crypto exchange connector: a dated future and a perpetual are
  different instruments for a bot (a perpetual is never rolled, and it pays or earns funding).

- `Etf`
  An exchange traded fund.

- `MutualFund`
  A mutual fund.

- `Currency`
  A currency.

- `Other`
  Anything the connector could not classify.

### Enum InstrumentQuery

```rust
pub enum InstrumentQuery {
    ByTicker {
        ticker: String,
        exchange: Option<String>,
    },
    ByIsin(String),
    ByKind(InstrumentKind),
}
```
A directory lookup for `InstrumentSource::find`.

- `ByTicker`
  By ticker, optionally narrowed to one exchange. The BCS connector ignores `exchange`.

- `ticker: String`
  Ticker to look for.

- `exchange: Option<String>`
  Exchange to narrow to, if the broker supports it.

- `ByIsin(String)`
  By ISIN.

- `ByKind(InstrumentKind)`
  All instruments of one kind. Can be a long list, a connector may page through it.

### Struct MarketDataCaps

```rust
#[non_exhaustive]pub struct MarketDataCaps {
    pub max_order_book_depth: u32,
    pub incremental_order_book: bool,
    pub streaming: StreamingCaps,
}
```
What a market data connector can do. Read it from the connector’s `caps()` method.
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional `Struct { .. }` syntax; cannot be matched against without a wildcard `..`; and struct update syntax will not work.

- `max_order_book_depth: u32`
  Deepest order book the connector serves. A larger `depth` is cut to this value (the BCS
  connector does so silently).

- `incremental_order_book: bool`
  `true` when a stream may send `OrderBook::Increment`. `false`
  means snapshots only.

- `streaming: StreamingCaps`
  Which streams exist.

- `pub const fn new( max_order_book_depth: u32, incremental_order_book: bool, streaming: StreamingCaps, ) -> Self`
  Caps of a market data connector. The struct is `#[non_exhaustive]`: build it with this function.

  Examples

### Struct MarketTrade

```rust
pub struct MarketTrade {
    pub instrument: InstrumentId,
    pub price: Price,
    pub qty: Qty,
    pub side: Side,
    pub trade_id: Option<String>,
    pub ts: Timestamps,
}
```
A trade on the exchange tape: someone else’s or yours, a public record of one deal.

Your own fills are `Execution`, a different type with different delivery
guarantees.

- `instrument: InstrumentId`
  What was traded.

- `price: Price`
  Price of the deal.

- `qty: Qty`
  Quantity of the deal, see `Qty` for the unit.

- `side: Side`
  Side of the deal as the broker reports it. A history record whose side the broker does not
  give is dropped by the BCS connector.

- `trade_id: Option<String>`
  The exchange’s id of the deal, when the broker gives one.

- `ts: Timestamps`
  When received, and the broker time if given.

### Struct Money

```rust
pub struct Money(/* private fields */);
```
An amount of money in some currency: a balance, a commission, the money value of a price step.

An exact decimal number in a newtype. The currency is not in the value: it sits next to it
(for example `Balance::currency`). Amounts of different currencies
are not meant to be added, and the type does not stop you, so check the currency first.

Amounts are in the currency itself (not in kopecks), as the broker reports them. Why decimal and
not `f64`: see `Price`.

- `pub const ZERO: Self`
  Zero.

- `pub const fn new(value: Decimal) -> Self`
  Wraps a decimal number as an amount of money. No range check is made.

  Make the `Decimal` with `dec!`, `Decimal::new(24430, 2)` or by parsing.

- `pub fn from_scaled(mantissa: i64, scale: u32) -> Self`
  Makes the value from a mantissa and a number of decimal places:
  `from_scaled(24430, 2)` is `244.30`. The trailing zero is kept.

  Panics

  If `scale` is above 28.

- `pub const fn decimal(self) -> Decimal`
  The exact number inside.

- `pub fn from_f64_lossy(value: f64, decimals: u32) -> Result<Self, NumberError>`
  Makes the value from an `f64`, rounded to `decimals` places. This loses
  precision: an `f64` already carries binary noise, and the rounding only hides it.
  Use it for numbers that come from outside as `f64`. Prefer parsing a string.

  `decimals` is the number of places the exchange knows (the scale of the price step).

  Errors

  `NumberError::NotFinite` for NaN and infinities, `NumberError::OutOfRange` for a
  number that does not fit.

- `pub fn to_f64_lossy(self) -> f64`
  The nearest `f64`. This loses precision: use it for a chart or a log line, never
  to compute a price that goes into an order.

- `pub fn is_zero(self) -> bool`
  Whether the value is zero (`0` and `0.00` are both zero).

- `pub fn is_negative(self) -> bool`
  Whether the value is below zero.

- `pub fn is_positive(self) -> bool`
  Whether the value is above zero.

- `pub fn abs(self) -> Self`
  The value without its sign.

- `pub fn scale(self) -> u32`
  The number of decimal places the value is written with: `244.30` has 2.

- `pub fn normalize(self) -> Self`
  The same number without trailing zeros: `244.30` becomes `244.3`. Equality and order
  do not depend on the scale, only the text does.

### Constant NOT_FOUND

```rust
pub const NOT_FOUND: &str = "not_found";
```
The `code` of an `Error::Rejected` that says “the broker does not know this”: an order, a cid, an
instrument. The same string at every broker, so a caller compares with `==` and needs no case folding.

### Struct NativeCid

```rust
pub struct NativeCid(pub String);
```
The form of a client order id as the broker sees it: a string in the broker’s own field.

Produced by `CidCodec::encode` and read back by `CidCodec::decode`. The content depends on
the broker and may carry a prefix or a changed alphabet, so treat it as opaque.

- `0: String`

- `pub fn new(value: impl Into<String>) -> Self`
  Wraps a string. Nothing is checked here: `CidCodec::encode` makes the checks.

- `pub fn as_str(&self) -> &str`
  The string as the broker sees it.

### Enum NativeId

```rust
#[non_exhaustive]pub enum NativeId {
    SymbolMic {
        symbol: String,
        mic: String,
    },
    TickerClass {
        ticker: String,
        class_code: String,
    },
    Figi(String),
    Uid(String),
}
```
How a broker names an instrument in its own API. The kind depends on the broker.

A connector reads only its own kind. An `InstrumentId` with another kind is refused with
`Error::Config` (the BCS connector wants
`TickerClass`). Turning a key into a native id and back needs no network
call.
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.

- `SymbolMic`
  Finam style: symbol and market code.

- `symbol: String`
  Ticker, for example `SBER`.

- `mic: String`
  Market code, for example `MISX`.

- `TickerClass`
  BCS style: ticker and class code (the board).

- `ticker: String`
  Ticker, for example `SBER`.

- `class_code: String`
  Class code of the board, for example `TQBR`.

- `Figi(String)`
  T-Bank style: the FIGI of the instrument.

- `Uid(String)`
  T-Bank style: the broker’s `uid` of the instrument.

### Enum NumberError

```rust
pub enum NumberError {
    Parse(String),
    NotFinite,
    OutOfRange,
    StepNotPositive,
}
```
Why a number could not be made, or why a step calculation was refused.

- `Parse(String)`
  The text is not a decimal number. Holds the text.

- `NotFinite`
  The `f64` is NaN or infinite.

- `OutOfRange`
  The value does not fit into the 96-bit integer part, or a calculation overflowed.

- `StepNotPositive`
  The step of a rounding is zero or negative.

### Struct OrderAck

```rust
pub struct OrderAck {
    pub cid: ClientOrderId,
    pub broker_id: BrokerOrderId,
    pub ts: Timestamps,
}
```
The broker’s confirmation that it took an order: the answer of `place`, `replace` and `place_stop`.

It says the order is accepted, not that it is filled. Follow the order with
`OrderExecutor::order_state` or the order stream.

- `cid: ClientOrderId`
  The cid you sent. Equal to the one in the request.

- `broker_id: BrokerOrderId`
  The id the broker gave to the order.

- `ts: Timestamps`
  When the answer was received, and the broker time if it gave one.

### Enum OrderBook

```rust
pub enum OrderBook {
    Snapshot {
        instrument: InstrumentId,
        bids: Vec<BookLevel>,
        asks: Vec<BookLevel>,
        ts: Timestamps,
    },
    Increment {
        instrument: InstrumentId,
        seq: u64,
        bids: Vec<BookLevel>,
        asks: Vec<BookLevel>,
        ts: Timestamps,
    },
}
```
An order book: either a full snapshot or an incremental update.

`OrderBookSource::order_book` always answers a `Snapshot`.
A stream may send snapshots, or (where `MarketDataCaps::incremental_order_book`
is `true`) a snapshot followed by `Increment`s with a growing `seq`. In both variants the bid
side is listed from the best (highest) price down and the ask side from the best (lowest) price
up. This is checked for the BCS mock in `brokapi-conformance`, not verified against the live API.

How an `Increment` is applied (whether a level with zero quantity removes it) is defined by the
connector that sends it, see its docs. The BCS, T-Bank and Finam connectors declare
`incremental_order_book: false` and send snapshots only.
When a gap in `seq` or a reconnect makes the book untrustworthy the stream sends
`Event::Resync` and a new snapshot.

- `Snapshot`
  The whole book to the depth asked for. Replaces whatever you had.

- `instrument: InstrumentId`
  The instrument of the book.

- `bids: Vec<BookLevel>`
  Buy levels, best (highest) price first.

- `asks: Vec<BookLevel>`
  Sell levels, best (lowest) price first.

- `ts: Timestamps`
  When received, and the broker time if given.

- `Increment`
  A change to apply to the book you have.

- `instrument: InstrumentId`
  The instrument of the book.

- `seq: u64`
  Sequence number of this update. A jump means updates were lost: expect
  `Event::Resync`.

- `bids: Vec<BookLevel>`
  Changed buy levels.

- `asks: Vec<BookLevel>`
  Changed sell levels.

- `ts: Timestamps`
  When received, and the broker time if given.

### Struct OrderSnapshot

```rust
#[non_exhaustive]pub struct OrderSnapshot {
    pub cid: ClientOrderId,
    pub broker_id: Option<BrokerOrderId>,
    pub instrument: InstrumentId,
    pub state: OrderState,
    pub side: Side,
    pub order_type: OrderType,
    pub qty: Qty,
    pub filled: Qty,
    pub price: Option<Price>,
    pub ts: Timestamps,
}
```
An order as the broker shows it at one moment.

Returned by `OrderExecutor::order_state` and
`OrderExecutor::active_orders`, and sent as
`Event::Order` on every change.
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional `Struct { .. }` syntax; cannot be matched against without a wildcard `..`; and struct update syntax will not work.

- `cid: ClientOrderId`
  The cid of the order. For an order that was not placed through this connector and whose cid
  the broker does not give, the BCS connector makes a stable cid of its own from the broker id,
  so such an order is not in your ledger and shows up as `foreign` in a `ReconcileReport`.

- `broker_id: Option<BrokerOrderId>`
  The broker’s id of the order, when known.

- `instrument: InstrumentId`
  What the order trades.

- `state: OrderState`
  Where the order is in its life.

- `side: Side`
  Buy or sell.

- `order_type: OrderType`
  Market or limit.

- `qty: Qty`
  Total quantity of the order, see `Qty` for the unit.

- `filled: Qty`
  Quantity executed so far.

- `price: Option<Price>`
  Limit price. `None` for a market order, or when the broker does not show one.

- `ts: Timestamps`
  When this was received, and the broker time if it gave one.

- `pub fn new( cid: ClientOrderId, instrument: InstrumentId, state: OrderState, side: Side, order_type: OrderType, qty: Qty, ts: Timestamps, ) -> Self`
  A snapshot with the required fields: the order, its instrument, state, side, type, quantity and
  time. `broker_id` and `price` are `None`, `filled` is zero. Set them with
  `with_broker_id`, `with_filled` and
  `with_price`. The struct is `#[non_exhaustive]`: it gets new fields, so build
  it with this function and not with a struct literal.

  Examples

- `pub fn with_broker_id(self, broker_id: Option<BrokerOrderId>) -> Self`
  Sets the broker’s id of the order.

- `pub fn with_filled(self, filled: Qty) -> Self`
  Sets the executed quantity.

- `pub fn with_price(self, price: Option<Price>) -> Self`
  Sets the limit price.

### Enum OrderState

```rust
#[non_exhaustive]pub enum OrderState {
    InFlight {
        cid: ClientOrderId,
        sent_at: Instant,
    },
    Unknown {
        cid: ClientOrderId,
        since: Instant,
    },
    Acked {
        cid: ClientOrderId,
        broker_id: BrokerOrderId,
    },
    PartiallyFilled {
        cid: ClientOrderId,
        filled: Qty,
        left: Qty,
    },
    Filled {
        cid: ClientOrderId,
    },
    Cancelled {
        cid: ClientOrderId,
    },
    Rejected {
        cid: ClientOrderId,
        reason: String,
    },
}
```
Where an order is in its life.

Read it from `OrderSnapshot::state`, from `Event::Order` on the order
stream, or from `OrderExecutor::order_state`. Every variant
carries the cid.

Typical path: `Acked` -> `PartiallyFilled` (zero or more times) -> `Filled`, or `Acked` ->
`Cancelled`, or straight to `Rejected`. `Filled`, `Cancelled` and `Rejected` are final.

The two states about doubt are `InFlight` and `Unknown`. A timeout or a cut connection on a
command gives `Unknown`, never `Rejected`: the order may exist. Leave `Unknown` only by asking
the broker (`OrderExecutor::order_state`,
`OrderExecutor::active_orders` or the executions).
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.

- `InFlight`
  The command was sent and the answer has not come yet. Local state of the connector’s order
  tracking, so a snapshot read from the broker normally does not have it.

- `cid: ClientOrderId`
  The order.

- `sent_at: Instant`
  When the command was sent, monotonic clock. Not serialized (after serde it is “now”).

- `Unknown`
  The outcome of a command is not known (timeout, cut connection), or the broker reported a
  status this library does not know. The order may exist. Ask the broker, do not repeat the
  command, see `Error::Unknown`.

- `cid: ClientOrderId`
  The order.

- `since: Instant`
  Since when the outcome is unknown, monotonic clock. Not serialized (after serde it is “now”).

- `Acked`
  The broker accepted the order and it is working, nothing filled yet.

- `cid: ClientOrderId`
  The order.

- `broker_id: BrokerOrderId`
  The broker’s id of the order.

- `PartiallyFilled`
  Part of the order is filled and the rest is still working.

- `cid: ClientOrderId`
  The order.

- `filled: Qty`
  Quantity executed so far.

- `left: Qty`
  Quantity still to execute (the rest of the order).

- `Filled`
  The order is fully executed. Final.

- `cid: ClientOrderId`
  The order.

- `Cancelled`
  The order is cancelled, by you or by the broker or exchange (for example at the end of the
  day). Part of it may have been filled before. Final.

- `cid: ClientOrderId`
  The order.

- `Rejected`
  The broker refused the order after accepting the request. Final. A refusal that comes as the
  direct answer of `place` is returned as `Error::Rejected` instead.

- `cid: ClientOrderId`
  The order.

- `reason: String`
  The broker’s reason, for a human.

- `pub fn cid(&self) -> ClientOrderId`
  The client order id, the same in every state.

  Examples

- `pub fn name(&self) -> &'static str`
  The name of the state, for logs and error texts (`"Filled"`, `"PartiallyFilled"`, …).

  Examples

- `pub fn is_final(&self) -> bool`
  `true` for the states that never change again: `Filled`, `Cancelled`, `Rejected`.

  Examples

### Enum OrderType

```rust
pub enum OrderType {
    Market,
    Limit,
}
```
How a plain order is priced, see `PlaceOrderRequest`.

Stop orders have their own kinds, see `StopOrderKind`.

- `Market`
  Executes at the best available price now. `price` of the request should be `None`.

- `Limit`
  Executes at the given price or better. `price` of the request is required: the BCS
  connector refuses a limit order without it with `Error::Rejected`
  (code `missing_price`) before anything is sent.

### Enum PlaceIdempotency

```rust
pub enum PlaceIdempotency {
    LabelOnly,
    RejectDuplicate {
        window: Duration,
    },
    Idempotent {
        window: Duration,
    },
}
```
What the broker does when the same place request arrives twice with the same cid.

It matters only for your own decision. The library never repeats a place, whatever the value.
Until a connector’s behaviour is confirmed on a live account it declares `LabelOnly`, the
safest assumption.

- `LabelOnly`
  Cid is a correlation label only. A retry creates a second order.

- `RejectDuplicate`
  Broker rejects duplicate cid within the window.

- `window: Duration`
  How long the broker remembers a cid.

- `Idempotent`
  Broker returns the original order state for duplicate cid.

- `window: Duration`
  How long the broker remembers a cid.

### Struct PlaceOrderRequest

```rust
pub struct PlaceOrderRequest {
    pub cid: ClientOrderId,
    pub instrument: InstrumentId,
    pub side: Side,
    pub order_type: OrderType,
    pub qty: Qty,
    pub price: Option<Price>,
}
```
A request to put a new order: see `OrderExecutor::place`.

The library sends exactly what is here. It does not check the quantity against lot size or the
price against the price step or the account, that is the caller’s job (see
`TradingParams`).

- `cid: ClientOrderId`
  Your id of this order. Fresh for every order, never reused, see `ClientOrderId`.

- `instrument: InstrumentId`
  What to trade.

- `side: Side`
  Buy or sell.

- `order_type: OrderType`
  Market or limit.

- `qty: Qty`
  How much, see `Qty` for the unit.

- `price: Option<Price>`
  Limit price. Required for `OrderType::Limit`, `None` for `OrderType::Market`.

### Struct PlaceStopOrderRequest

```rust
pub struct PlaceStopOrderRequest {
    pub cid: ClientOrderId,
    pub instrument: InstrumentId,
    pub side: Side,
    pub kind: StopOrderKind,
    pub qty: Qty,
    pub stop_price: Price,
    pub limit_price: Option<Price>,
    pub expiry: StopExpiry,
}
```
A request to put a stop order: see `StopOrderExecutor::place_stop`.

The library sends what is here. A `StopOrderKind::StopLimit` without `limit_price` is refused
with `Error::Rejected` before anything is sent.

- `cid: ClientOrderId`
  Your id of this stop order. Fresh, never reused, see `ClientOrderId`.

- `instrument: InstrumentId`
  What to trade.

- `side: Side`
  Buy or sell. With `kind` it fixes the direction of the trigger.

- `kind: StopOrderKind`
  Stop-loss, take-profit or stop-limit.

- `qty: Qty`
  How much, see `Qty` for the unit.

- `stop_price: Price`
  The trigger price.

- `limit_price: Option<Price>`
  Price of the exchange order put at the trigger. `None` = market. Required by `StopLimit`.

- `expiry: StopExpiry`
  How long the stop order lives.

### Struct Portfolio

```rust
pub struct Portfolio {
    pub balances: Vec<Balance>,
    pub positions: Vec<Position>,
    pub ts: Timestamps,
}
```
The account in one piece: money and positions at the same moment.

From `PortfolioSource::portfolio`, and as
`Event::Portfolio` from a portfolio subscription. A snapshot replaces
the previous one.

- `balances: Vec<Balance>`
  Money by currency.

- `positions: Vec<Position>`
  Instruments held.

- `ts: Timestamps`
  When received, and the broker time if given.

### Struct Position

```rust
pub struct Position {
    pub instrument: InstrumentId,
    pub qty: Qty,
    pub avg_price: Option<Price>,
    pub current_price: Option<Price>,
    pub ts: Timestamps,
}
```
How much of one instrument the account holds.

- `instrument: InstrumentId`
  The instrument held.

- `qty: Qty`
  Quantity held, see `Qty` for the unit. Negative for a short position where the broker has it.

- `avg_price: Option<Price>`
  Average price at which the position was built, when the broker gives it.

- `current_price: Option<Price>`
  Last known price, when the broker gives it.

- `ts: Timestamps`
  When received, and the broker time if given.

### Struct Price

```rust
pub struct Price(/* private fields */);
```
A price of one unit of the instrument, in the currency of the instrument.

An exact decimal number (`rust_decimal::Decimal`, 28 digits) in a newtype. Brokers send prices as
decimals (T-Bank as units and billionths, Finam as a decimal string, BCS as a JSON number), and
the exchange grid is decimal. In `f64` the price `244.3` is not on the step `0.01` (the remainder
`244.3 % 0.01` is `6.3e-15`, not zero) and `109.32 / 0.01` is `10931.999999999998` steps. Sums are off
too: `0.1 + 0.2` is `0.30000000000000004`. In `Price` all of these are exact.

How to make one:

- `Price::new(dec!(250.15))`, with `dec!` for a literal;

- `"250.15".parse::<Price>()`, for text from a file or a form;

- `Price::from(250)`, from an integer;

- `Price::from_scaled(25015, 2)`, from a mantissa and the places;

- `Price::from_f64_lossy`, only for an `f64` that already exists. It loses precision and says so.

How to round to the grid of the instrument: `Price::round_to_step` with the
`TradingParams::price_step`, or
`TradingParams::round_price`. The library does not round for
you. The connectors refuse an order price off the grid when they know the step (give it to them
with `price_grid()` of the connector, see `Error::Rejected`, code
`price_off_step`).

Order and equality are numeric, `244.30 == 244.3`. Text and JSON keep the written scale:
`244.30` stays `"244.30"`. In JSON the number is a string, so no reader can round it. A JSON number is
read too, as exactly as the JSON reader hands it over (with `serde_json` and no `arbitrary_precision` an
`f64` comes in, so prefer the string).

- `pub const ZERO: Self`
  Zero.

- `pub const fn new(value: Decimal) -> Self`
  Wraps a decimal number as a price. No range check is made.

  Make the `Decimal` with `dec!`, `Decimal::new(24430, 2)` or by parsing.

- `pub fn from_scaled(mantissa: i64, scale: u32) -> Self`
  Makes the value from a mantissa and a number of decimal places:
  `from_scaled(24430, 2)` is `244.30`. The trailing zero is kept.

  Panics

  If `scale` is above 28.

- `pub const fn decimal(self) -> Decimal`
  The exact number inside.

- `pub fn from_f64_lossy(value: f64, decimals: u32) -> Result<Self, NumberError>`
  Makes the value from an `f64`, rounded to `decimals` places. This loses
  precision: an `f64` already carries binary noise, and the rounding only hides it.
  Use it for numbers that come from outside as `f64`. Prefer parsing a string.

  `decimals` is the number of places the exchange knows (the scale of the price step).

  Errors

  `NumberError::NotFinite` for NaN and infinities, `NumberError::OutOfRange` for a
  number that does not fit.

- `pub fn to_f64_lossy(self) -> f64`
  The nearest `f64`. This loses precision: use it for a chart or a log line, never
  to compute a price that goes into an order.

- `pub fn is_zero(self) -> bool`
  Whether the value is zero (`0` and `0.00` are both zero).

- `pub fn is_negative(self) -> bool`
  Whether the value is below zero.

- `pub fn is_positive(self) -> bool`
  Whether the value is above zero.

- `pub fn abs(self) -> Self`
  The value without its sign.

- `pub fn scale(self) -> u32`
  The number of decimal places the value is written with: `244.30` has 2.

- `pub fn normalize(self) -> Self`
  The same number without trailing zeros: `244.30` becomes `244.3`. Equality and order
  do not depend on the scale, only the text does.

- `pub fn round_to_step( self, step: Self, mode: RoundMode, ) -> Result<Self, NumberError>`
  Rounds to a multiple of `step`. The grid starts at zero, as the exchange grids do.

  The calculation is exact: `244.30` with a step of `0.01` stays `244.30`, and
  `101.03` with a step of `0.05` becomes `101.00` down and `101.05` up or to the nearest, by
  the `RoundMode`. A value already on the grid is returned as it is.

  Errors

  `NumberError::StepNotPositive` if `step` is zero or below,
  `NumberError::OutOfRange` on overflow.

- `pub fn is_on_step(self, step: Self) -> bool`
  Whether the value is a whole number of steps. `false` for a step that is zero or
  below.

### Struct Qty

```rust
pub struct Qty(/* private fields */);
```
A quantity: order size, filled size, position size, trade size, candle volume.

An exact decimal number in a newtype, so a quantity cannot be passed where a price is expected.
Why decimal and not `f64`: see `Price`.

The unit is the same everywhere: units of the instrument, never lots. A share is one share, a
bond is one bond (its price is a percent of the face value, see the book page on money and
quantities), a currency is one unit of it, a futures contract is one contract. The same unit in an
order, an order book level, a deal, a fill, a position and the volume of a candle, at every broker.
That is the rule of NautilusTrader and of the exchange. A broker that counts in lots (T-Bank) is
converted by its connector in both directions, so your code never sees a lot count.

- An order quantity (`PlaceOrderRequest::qty`) is positive and a
whole number of lots. The size of a lot is
`TradingParams::lot_size`, in units. A quantity that the broker
cannot take (a fraction where it takes whole units, a count that is not a multiple of the lot,
zero) is refused before anything is sent with `Error::Rejected`, code
`invalid_qty`, and the cid is not used. The library never rounds a quantity for you.

- A position, a deal, a fill and a volume are as the broker reports them, in units.

The library does not check the value against the account or the minimal size of an exchange board.

- `pub const ZERO: Self`
  Zero.

- `pub const fn new(value: Decimal) -> Self`
  Wraps a decimal number as a quantity. No range check is made.

  Make the `Decimal` with `dec!`, `Decimal::new(24430, 2)` or by parsing.

- `pub fn from_scaled(mantissa: i64, scale: u32) -> Self`
  Makes the value from a mantissa and a number of decimal places:
  `from_scaled(24430, 2)` is `244.30`. The trailing zero is kept.

  Panics

  If `scale` is above 28.

- `pub const fn decimal(self) -> Decimal`
  The exact number inside.

- `pub fn from_f64_lossy(value: f64, decimals: u32) -> Result<Self, NumberError>`
  Makes the value from an `f64`, rounded to `decimals` places. This loses
  precision: an `f64` already carries binary noise, and the rounding only hides it.
  Use it for numbers that come from outside as `f64`. Prefer parsing a string.

  `decimals` is the number of places the exchange knows (the scale of the price step).

  Errors

  `NumberError::NotFinite` for NaN and infinities, `NumberError::OutOfRange` for a
  number that does not fit.

- `pub fn to_f64_lossy(self) -> f64`
  The nearest `f64`. This loses precision: use it for a chart or a log line, never
  to compute a price that goes into an order.

- `pub fn is_zero(self) -> bool`
  Whether the value is zero (`0` and `0.00` are both zero).

- `pub fn is_negative(self) -> bool`
  Whether the value is below zero.

- `pub fn is_positive(self) -> bool`
  Whether the value is above zero.

- `pub fn abs(self) -> Self`
  The value without its sign.

- `pub fn scale(self) -> u32`
  The number of decimal places the value is written with: `244.30` has 2.

- `pub fn normalize(self) -> Self`
  The same number without trailing zeros: `244.30` becomes `244.3`. Equality and order
  do not depend on the scale, only the text does.

- `pub fn round_to_step( self, step: Self, mode: RoundMode, ) -> Result<Self, NumberError>`
  Rounds to a multiple of `step`. The grid starts at zero, as the exchange grids do.

  The calculation is exact: `244.30` with a step of `0.01` stays `244.30`, and
  `101.03` with a step of `0.05` becomes `101.00` down and `101.05` up or to the nearest, by
  the `RoundMode`. A value already on the grid is returned as it is.

  Errors

  `NumberError::StepNotPositive` if `step` is zero or below,
  `NumberError::OutOfRange` on overflow.

- `pub fn is_on_step(self, step: Self) -> bool`
  Whether the value is a whole number of steps. `false` for a step that is zero or
  below.

- `pub fn to_whole(self) -> Option<i64>`
  The quantity as a whole number, or `None` if it has a fractional part or does not fit in an
  `i64`. `10.0` is whole.

  Examples

### Struct Quote

```rust
pub struct Quote {
    pub instrument: InstrumentId,
    pub bid: Option<BookLevel>,
    pub ask: Option<BookLevel>,
    pub ts: Timestamps,
}
```
The best bid and best ask of an instrument: the top of the book without the rest.

Comes from `MarketDataSource::subscribe_quotes` as
`Event::Quote`. There is no one-shot read of a quote in the traits: read
the top of `OrderBookSource::order_book` with depth 1.

- `instrument: InstrumentId`
  The instrument of the quote.

- `bid: Option<BookLevel>`
  Best buy level. `None` when nobody is buying.

- `ask: Option<BookLevel>`
  Best sell level. `None` when nobody is selling.

- `ts: Timestamps`
  When received, and the broker time if given.

### Struct ReconcileReport

```rust
#[non_exhaustive]pub struct ReconcileReport {
    pub confirmed: Vec<OrderSnapshot>,
    pub closed_offline: Vec<ClientOrderId>,
    pub foreign: Vec<OrderSnapshot>,
}
```
The result of `OrderExecutor::reconcile`: your ledger
compared with the active orders at the broker.

Every order of the ledger is in `confirmed` or in `closed_offline`. Every active order that is
not in the ledger is in `foreign`.
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional `Struct { .. }` syntax; cannot be matched against without a wildcard `..`; and struct update syntax will not work.

- `confirmed: Vec<OrderSnapshot>`
  In the ledger and active at the broker. The snapshot is the broker’s current view.

- `closed_offline: Vec<ClientOrderId>`
  In the ledger and NOT active at the broker: filled, cancelled or refused while you were not
  watching, or never seen by the broker. The report does not say which. Ask
  `OrderExecutor::order_state` for the final state, and
  `ExecutionSource::executions` for the fills.

- `foreign: Vec<OrderSnapshot>`
  Active at the broker and not in the ledger: placed by hand, by another program or by an
  earlier run that lost its ledger. The library never touches them, and you should not either
  unless you know whose they are.

- `pub fn new( confirmed: Vec<OrderSnapshot>, closed_offline: Vec<ClientOrderId>, foreign: Vec<OrderSnapshot>, ) -> Self`
  A report of the three lists. The struct is `#[non_exhaustive]`: build it with this function.

### Struct ReplaceOrderRequest

```rust
pub struct ReplaceOrderRequest {
    pub cid: ClientOrderId,
    pub original_cid: ClientOrderId,
    pub qty: Option<Qty>,
    pub price: Option<Price>,
}
```
A request to change a working order: see `OrderExecutor::replace`.

The broker cancels the original order and puts a new one under the new `cid`, so the result is a
different order with a different cid and broker id. Only brokers with
`UserDataCaps::supports_replace` have it.

- `cid: ClientOrderId`
  The cid of the NEW order. Fresh, never reused, see `ClientOrderId`.

- `original_cid: ClientOrderId`
  The cid of the working order to replace.

- `qty: Option<Qty>`
  New quantity. `None` keeps the current one (the BCS connector refuses `None` for a partly
  filled order with `Error::Rejected`, code `qty_required`).

- `price: Option<Price>`
  New price. `None` keeps the current one.

### Enum ResyncReason

```rust
#[non_exhaustive]pub enum ResyncReason {
    SequenceGap,
    SubscriptionReset,
    Manual,
}
```
Why a stream tells you to drop what you have and start over: see `Event::Resync`.
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.

- `SequenceGap`
  An incremental update was missed (the sequence number jumped). The book you hold is wrong.

- `SubscriptionReset`
  The connection was re-established and the subscription restored. What you held before is
  void. The BCS connector sends this before the new snapshot after a reconnect, not on the
  first connect.

- `Manual`
  A resync was asked for by hand, not forced by a gap or a reconnect.

### Enum RoundMode

```rust
pub enum RoundMode {
    Down,
    Up,
    Nearest,
}
```
Which way a value is rounded to a grid, see `Price::round_to_step`.

“Down” and “up” are toward minus and plus infinity: for the positive prices of a stock exchange
they are toward zero and away from zero.

- `Down`
  To the nearest grid point at or below the value. A buy price rounded this way is never worse
  than computed.

- `Up`
  To the nearest grid point at or above the value. A sell price rounded this way is never worse
  than computed.

- `Nearest`
  To the nearest grid point. A value exactly in the middle goes away from zero.

### Struct Schedule

```rust
pub struct Schedule {
    pub exchange: String,
    pub sessions: Vec<SessionInterval>,
    pub ts: Timestamps,
}
```
The trading sessions of an exchange (or, for BCS, of an instrument) over a time range: see
`InstrumentSource::schedule`.

- `exchange: String`
  The key the schedule was asked for, as the connector understands it.

- `sessions: Vec<SessionInterval>`
  Trading sessions of the range. The BCS connector gives none on a non-working day.

- `ts: Timestamps`
  When received, and the broker time if given.

### Struct SessionInterval

```rust
pub struct SessionInterval {
    pub start: UnixNanos,
    pub end: UnixNanos,
    pub kind: SessionKind,
}
```
One trading session of a day.

- `start: UnixNanos`
  Start of the session.

- `end: UnixNanos`
  End of the session.

- `kind: SessionKind`
  Which part of the day it is.

### Enum SessionKind

```rust
pub enum SessionKind {
    PreMarket,
    Main,
    PostMarket,
    Auction,
    Other,
}
```
The part of the trading day a `SessionInterval` is.

- `PreMarket`
  Before the main session.

- `Main`
  The main session.

- `PostMarket`
  After the main session (evening, for example).

- `Auction`
  An opening or closing auction.

- `Other`
  Anything else, or a name the connector could not classify.

### Enum ShutdownMode

```rust
#[non_exhaustive]pub enum ShutdownMode {
    Graceful {
        deadline: Duration,
    },
    Immediate,
}
```
How a connector stops: the argument of `shutdown`.

Shutdown NEVER cancels or changes orders at the exchange. A working order outlives your
process. Cancel orders yourself before shutdown if you want them gone. Shutdown only drops the
local wait for an answer.
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.

- `Graceful`
  Stop taking commands (they get `Error::ShuttingDown`),
  unsubscribe the way the broker’s protocol wants where the connector does that, flush the
  events still waiting, then stop. After `deadline` it turns into `Immediate`.

- `deadline: Duration`
  How long to wait for the polite steps.

- `Immediate`
  Stop now. Skips the unsubscribe and the wait.

### Struct ShutdownReport

```rust
#[non_exhaustive]pub struct ShutdownReport {
    pub unsubscribed: usize,
    pub unsubscribe_failed: usize,
    pub inflight_abandoned: Vec<ClientOrderId>,
}
```
What a shutdown did: the return value of `shutdown`.
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional `Struct { .. }` syntax; cannot be matched against without a wildcard `..`; and struct update syntax will not work.

- `unsubscribed: usize`
  Subscriptions closed in the broker’s protocol.

- `unsubscribe_failed: usize`
  Subscriptions the connector tried to close and could not.

- `inflight_abandoned: Vec<ClientOrderId>`
  Commands that were sent and had no answer yet when the shutdown came. They are abandoned
  locally, which means their outcome is `Error::Unknown`: the
  exchange orders are untouched, look at them after the restart with
  `OrderExecutor::reconcile`.

- `pub fn new( unsubscribed: usize, unsubscribe_failed: usize, inflight_abandoned: Vec<ClientOrderId>, ) -> Self`
  A report with the given counts. The struct is `#[non_exhaustive]`: build it with this function.

- `pub fn empty() -> Self`
  A report of a shutdown that had nothing to do.

### Enum Side

```rust
pub enum Side {
    Buy,
    Sell,
}
```
Direction of an order, a trade or a stop order.

- `Buy`
  Buy: the position grows.

- `Sell`
  Sell: the position shrinks.

### Enum StopExpiry

```rust
pub enum StopExpiry {
    GoodTillCancel,
    EndOfDay,
    GoodTillDate(UnixNanos),
}
```
How long the stop order lives.

- `GoodTillCancel`
  Until cancelled.

- `EndOfDay`
  Until the end of the trading day (not every broker has it).

- `GoodTillDate(UnixNanos)`
  Until the given moment (not every broker has it for every kind).

### Enum StopOrderKind

```rust
pub enum StopOrderKind {
    StopLoss,
    TakeProfit,
    StopLimit,
}
```
What the stop order is for. Together with the side it fixes the direction of the trigger.

- `StopLoss`
  Protects a position: a SELL triggers when the price falls to `stop_price`, a BUY when it
  rises. After the trigger the order is a market order, or a limit order at `limit_price`.

- `TakeProfit`
  Takes a profit: a SELL triggers when the price rises to `stop_price`, a BUY when it falls.
  After the trigger the order is a market order, or a limit order at `limit_price`.

- `StopLimit`
  Stop-limit: triggers like `StopLoss` and always puts a limit order, so `limit_price` is
  required.

- `pub fn triggers_on_rise(self, side: Side) -> bool`
  `true` when the stop triggers on a RISING price for this side (the trigger price is above the
  current price). Falling otherwise.

### Enum StopOrderRef

```rust
pub enum StopOrderRef {
    Cid(ClientOrderId),
    Broker(BrokerOrderId),
}
```
Which stop order a command is about.

- `Cid(ClientOrderId)`
  The cid given to `place_stop`. Resolved from what this connector saw. A connector that cannot
  recover it after a restart refuses with `Rejected` (`unknown_stop_order`), use `Broker`.

- `Broker(BrokerOrderId)`
  The broker’s id, from `OrderAck::broker_id` or `StopOrderSnapshot::broker_id`.

### Struct StopOrderSnapshot

```rust
pub struct StopOrderSnapshot {
    pub cid: Option<ClientOrderId>,
    pub broker_id: BrokerOrderId,
    pub instrument: InstrumentId,
    pub side: Side,
    pub kind: StopOrderKind,
    pub qty: Qty,
    pub stop_price: Price,
    pub limit_price: Option<Price>,
    pub expiry: StopExpiry,
    pub state: StopOrderState,
    pub ts: Timestamps,
}
```
A stop order as the broker shows it: see
`StopOrderExecutor::active_stop_orders`.

- `cid: Option<ClientOrderId>`
  `None` when the connector cannot tie the broker’s stop order to a cid (placed by someone else,
  or the broker does not echo it and this process did not place it, or a placement whose answer
  was lost fits this stop order and another one equally well). A stop order with `cid: None` next
  to a lost answer may be yours.

- `broker_id: BrokerOrderId`
  The broker’s id of the stop order. Use it with `StopOrderRef::Broker`.

- `instrument: InstrumentId`
  What the stop order trades.

- `side: Side`
  Buy or sell.

- `kind: StopOrderKind`
  Stop-loss, take-profit or stop-limit.

- `qty: Qty`
  Quantity of the order put at the trigger, see `Qty` for the unit.

- `stop_price: Price`
  The trigger price.

- `limit_price: Option<Price>`
  Price of the exchange order put at the trigger. `None` = market.

- `expiry: StopExpiry`
  How long the stop order lives.

- `state: StopOrderState`
  Where it is in its life.

- `ts: Timestamps`
  When received, and the broker time if given.

### Enum StopOrderState

```rust
#[non_exhaustive]pub enum StopOrderState {
    Active,
    Triggered,
    Cancelled,
    Expired,
    Other(String),
}
```
Lifecycle of a stop order at the broker.
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.

- `Active`
  Waiting for the trigger.

- `Triggered`
  Triggered: the exchange order was put.

- `Cancelled`
  Cancelled before it triggered, by you or by the broker.

- `Expired`
  The lifetime ended (`StopExpiry`) without a trigger.

- `Other(String)`
  The broker’s status does not fit the others. The text is the broker’s.

### Struct StreamingCaps

```rust
#[non_exhaustive]pub struct StreamingCaps {
    pub order_book: bool,
    pub market_trades: bool,
    pub quotes: bool,
    pub candles: bool,
}
```
Which streams a market data connector offers.

A `false` means the matching `subscribe_*` method answers
`Error::Unsupported`. Poll the REST read yourself if you need the data.
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional `Struct { .. }` syntax; cannot be matched against without a wildcard `..`; and struct update syntax will not work.

- `order_book: bool`
  `OrderBookSource::subscribe_order_book` works.

- `market_trades: bool`
  `MarketDataSource::subscribe_market_trades` works.

- `quotes: bool`
  `MarketDataSource::subscribe_quotes` works.

- `candles: bool`
  `MarketDataSource::subscribe_candles` works.

- `pub const NONE: Self`
  No streams at all.

- `pub const fn new( order_book: bool, market_trades: bool, quotes: bool, candles: bool, ) -> Self`
  Caps with the four streams given: order book, market trades, quotes, candles.

  The struct is `#[non_exhaustive]`: it gets a new field when a stream is added, so build it with this
  function (or `NONE` and set the fields) and not with a struct literal.

### Struct SubscribeOpts

```rust
pub struct SubscribeOpts {
    pub backpressure: Backpressure,
    pub depth: Option<u32>,
}
```
Options of a `subscribe_*` call.

Some subscriptions take no options and fix the policy themselves: `subscribe_order_state` and
`subscribe_executions` of the BCS connector always use `Backpressure::Lossless`, with a buffer
size from the connector config.

- `backpressure: Backpressure`
  What to do when the consumer is slow.

- `depth: Option<u32>`
  How many price levels of an order book to follow. `None` takes the connector’s default (the
  BCS connector uses its maximum, see
  `MarketDataCaps::max_order_book_depth`).
  Ignored by subscriptions that are not order books.

- `pub fn conflate() -> Self`
  Options with the `Backpressure::Conflate` policy and the default depth.

- `pub fn lossless(cap: usize) -> Self`
  Options with the `Backpressure::Lossless` policy, a buffer of `cap` events and the default depth.

### Struct SubscriptionId

```rust
pub struct SubscriptionId(pub u64);
```
The handle of one subscription.

Returned by every `subscribe_*` method, and carried by each `Envelope` that
belongs to the subscription (`Envelope::sub`). It is unique only inside ONE connector. Two
connectors can use the same number, so tell them apart by `Envelope::source`.

- `0: u64`

- `pub fn new(id: u64) -> Self`
  Wraps a number. Connectors make their own ids, this is for tests and tools.

- `pub fn value(self) -> u64`
  The raw number.

### Struct TimeRange

```rust
pub struct TimeRange {
    pub from: UnixNanos,
    pub to: UnixNanos,
}
```
A span of time for history requests: `MarketDataSource::candles`,
`InstrumentSource::schedule` and
`ExecutionSource::executions`.

Whether the ends are inclusive, and how far back a broker serves, are decided by the broker. The
BCS schedule answer is for one day, the day of `from`.

- `from: UnixNanos`
  Start of the span.

- `to: UnixNanos`
  End of the span.

- `pub fn new(from: UnixNanos, to: UnixNanos) -> Self`
  A span from `from` to `to`. Nothing is checked, a reversed range is the caller’s mistake.

### Enum Timeframe

```rust
pub enum Timeframe {
    M1,
    M5,
    M10,
    M15,
    M30,
    H1,
    H4,
    D1,
    W1,
    MN,
}
```
The length of one candle.

Not every broker has every timeframe. The BCS connector has no `M10` and answers
`Error::Unsupported` for it.

- `M1`
  1 minute.

- `M5`
  5 minutes.

- `M10`
  10 minutes.

- `M15`
  15 minutes.

- `M30`
  30 minutes.

- `H1`
  1 hour.

- `H4`
  4 hours.

- `D1`
  1 day.

- `W1`
  1 week.

- `MN`
  1 month.

- `pub fn seconds(self) -> u64`
  The length of one bar in seconds. A month is counted as 30 days: it is a size for planning
  requests, not a calendar.

  Examples

### Struct Timestamps

```rust
pub struct Timestamps {
    pub broker_ts: Option<UnixNanos>,
    pub rx_ts: UnixNanos,
    pub rx_mono: Instant,
}
```
The clocks of one event: what the broker said and when this process got it.

Every domain object (`OrderBook`, `OrderSnapshot`,
`Execution` and so on) and every `Envelope` carries one.
Equality compares `broker_ts` and `rx_ts` only. The monotonic clock is not part of it and is not
serialized: after a round trip through serde it is the time of deserialization.

- `broker_ts: Option<UnixNanos>`
  Time the broker put on the event, `None` when it gave none. May be wrong, may go backwards.
  Never use it to measure latency or timeouts.

- `rx_ts: UnixNanos`
  Wall clock of this process when the event was received. For logs and for matching with other
  systems. It can jump (clock sync), so it is not for measuring durations.

- `rx_mono: Instant`
  Monotonic clock of this process when the event was received. The only valid source for
  latency and timeout arithmetic. Not serialized.

- `pub fn new(rx_ts: UnixNanos, rx_mono: Instant) -> Self`
  Timestamps of an event received now: no broker time yet, see `with_broker_ts`.

- `pub fn with_broker_ts(self, broker_ts: UnixNanos) -> Self`
  Sets the time the broker put on the event.

### Enum TradingAvailability

```rust
pub enum TradingAvailability {
    Open,
    Closed,
    Break,
    Unknown,
}
```
Whether an instrument can be traded now.

- `Open`
  Trading is going on.

- `Closed`
  The market is closed.

- `Break`
  A pause inside the trading day.

- `Unknown`
  The broker’s status is not one of the above. The BCS connector gives this for any status other
  than open or close.

### Struct TradingParams

```rust
pub struct TradingParams {
    pub instrument: InstrumentId,
    pub lot_size: Qty,
    pub min_qty: Qty,
    pub price_step: Price,
    pub price_step_value: Money,
    pub currency: String,
}
```
The rules for sizing and pricing an order of one instrument.

The library does NOT check the quantity against these. Round your quantity and price with them
before `OrderExecutor::place`: `TradingParams::round_price` and
`TradingParams::round_qty` do it exactly. A connector that knows the price step refuses an order
price off the grid before sending anything, see `TradingParams::check_price`.

- `instrument: InstrumentId`
  The instrument the numbers are for.

- `lot_size: Qty`
  Units of the instrument in one lot (shares in a lot of shares, contracts in a lot of futures,
  which is 1). An order quantity is a multiple of it, see `Qty`.

- `min_qty: Qty`
  Smallest order quantity, in units. Every connector sets it to one lot, so it equals `lot_size`.

- `price_step: Price`
  Smallest price move. A price must be a multiple of it.

- `price_step_value: Money`
  What one price step is worth in money, as the broker reports it.

- `currency: String`
  Currency of the prices, as the broker writes it (for example `RUB`).

- `pub fn round_price( &self, price: Price, mode: RoundMode, ) -> Result<Price, NumberError>`
  Rounds a price to the grid of `price_step`, exactly.

  Errors

  `NumberError::StepNotPositive` if the broker gave no usable step.

- `pub fn round_qty(&self, qty: Qty, mode: RoundMode) -> Result<Qty, NumberError>`
  Rounds a quantity to a multiple of `min_qty`, exactly.

  Errors

  `NumberError::StepNotPositive` if the broker gave no usable minimal quantity.

- `pub fn is_price_on_grid(&self, price: Price) -> bool`
  Whether the price is a whole number of price steps. `true` when the step is not usable
  (zero or below), because then nothing can be said against the price.

- `pub fn check_price(&self, price: Price) -> Result<(), Error>`
  Checks a price against the grid, with the error a connector gives for an order.

  Errors

  `Error::Rejected` with the code `price_off_step`.

### Struct TradingStatus

```rust
pub struct TradingStatus {
    pub instrument: InstrumentId,
    pub availability: TradingAvailability,
    pub reason: Option<String>,
    pub ts: Option<UnixNanos>,
}
```
The trading status of one instrument now: see `InstrumentSource::trading_status`.

- `instrument: InstrumentId`
  The instrument.

- `availability: TradingAvailability`
  Open, closed, break or unknown.

- `reason: Option<String>`
  The broker’s explanation or session name, for a human. Not a stable code.

- `ts: Option<UnixNanos>`
  Broker time of the status, when the broker gives it (the BCS connector gives none).

### Enum TransportError

```rust
pub enum TransportError {
    Message(String),
}
```
A network-level problem: connect, TLS, timeout of a read, a broken stream, a malformed answer.

Carried by `Error::Transport`. The text is for a human (a log line), it is not a stable code.

- `Message(String)`
  What went wrong, in words.

### Struct UnixNanos

```rust
pub struct UnixNanos(pub i64);
```
A point in time: nanoseconds since the Unix epoch, UTC, wall clock.

Used for time ranges (`TimeRange`), candle bounds, schedules and the
broker time of an event. It is a signed 64-bit count, so it covers years 1677 to 2262. A wall
clock can jump, so do not subtract two of them to measure how long something took: use
`Timestamps::rx_mono`.

- `0: i64`

- `pub const ZERO: Self`
  The epoch itself, 1970-01-01 00:00:00 UTC. Handy as the start of “all history”.

- `pub fn from_nanos(nanos: i64) -> Self`
  Wraps a raw number of nanoseconds since the epoch. Nothing is checked.

- `pub fn as_nanos(self) -> i64`
  The raw number of nanoseconds since the epoch.

### Struct UserDataCaps

```rust
#[non_exhaustive]pub struct UserDataCaps {
    pub place_idempotency: PlaceIdempotency,
    pub ws_rpc: bool,
    pub ordered_command_channel: bool,
    pub supports_replace: bool,
    pub supports_stop_orders: bool,
    pub streams_executions: bool,
    pub sandbox: bool,
}
```
What a user data connector (orders, fills, portfolio) can do. Read it from the connector’s
`caps()` method.
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional `Struct { .. }` syntax; cannot be matched against without a wildcard `..`; and struct update syntax will not work.

- `place_idempotency: PlaceIdempotency`
  What the broker does on a repeated cid, see `PlaceIdempotency`.

- `ws_rpc: bool`
  Commands can go over a WebSocket with a correlation id (request and response on one socket).
  `false` means commands use HTTP or gRPC.

- `ordered_command_channel: bool`
  Commands are executed by the broker in the order sent, within one channel.

- `supports_replace: bool`
  `OrderExecutor::replace` works. `false` means it answers
  `Error::Unsupported`.

- `supports_stop_orders: bool`
  `StopOrderExecutor` works. `false` means every method of it
  answers `Error::Unsupported`.

- `streams_executions: bool`
  `ExecutionSource::subscribe_executions` works
  as a stream. `false` means only the history call exists.

- `sandbox: bool`
  The connector talks to the broker’s test environment (sandbox), not to real money.

- `pub const fn new( place_idempotency: PlaceIdempotency, ws_rpc: bool, ordered_command_channel: bool, supports_replace: bool, supports_stop_orders: bool, streams_executions: bool, sandbox: bool, ) -> Self`
  Caps of a user data connector, the fields in the order they are declared. The struct is
  `#[non_exhaustive]`: build it with this function.

  Examples

### Function check_price_step

```rust
pub fn check_price_step(price: Price, step: Price) -> Result<(), Error>
```
Refuses a price that is not a whole number of `step`: the check a connector runs before sending an
order when it knows the step. A step that is zero or below means “not known” and passes.

**Errors**

`Error::Rejected` with the code `price_off_step`.

### Macro dec

```rust
dec!() { /* proc-macro */ }
```
Makes a `Decimal` from a literal: `dec!(250.15)`. Needs `Decimal` in scope
(`use brokapi_core::{Decimal, dec};`).
Transform a literal number directly to a `Decimal` at compile time.

Any Rust number format works, for example:

- `dec!(1)`, `dec!(-1)`, `dec!(1_999)`, `dec!(- 1_999)`

- `dec!(0b1)`, `dec!(-0b1_1111)`, `dec!(0o1)`, `dec!(-0o1_777)`, `dec!(0x1)`, `dec!(-0x1_Ffff)`

- `dec!(1.)`, `dec!(-1.111_009)`, `dec!(1e6)`, `dec!(-1.2e+6)`, `dec!(12e-6)`, `dec!(-1.2e-6)`

**Option `radix`**

You can give it integers (not float-like) in any radix from 2 to 36 inclusive, using the letters too:
`dec!(100, radix 2) == 4`, `dec!(-1_222, radix 3) == -53`, `dec!(z1, radix 36) == 1261`,
`dec!(-1_xyz, radix 36) == -90683`

**Option `exp`**

This is the same as the `e` 10’s exponent in float syntax (except as a Rust expression it doesn’t accept
a unary `+`.) You need this for other radixes. Currently, it must be between -28 and +28 inclusive:
`dec!(10, radix 2, exp 5) == 200_000`, `dec!( -1_777, exp -3, radix 8) == dec!(-1.023)`

### macro dec!


## Крейт brokapi_runtime


### Enum AckState

```rust
pub enum AckState {
    Pending,
    Confirmed,
    Refused(String),
}
```
What a connector knows about one new subscription, for `wait_ack`.

- `Pending`
  The request is out, the broker has not answered.

- `Confirmed`
  The broker confirmed the subscription (or the data of it has started to come, which proves it).

- `Refused(String)`
  The broker refused it. The text is the broker’s reason.

### Constant BOUND_CIDS

```rust
pub const BOUND_CIDS: usize = 100_000;
```
How many cids `InstrumentLocks` remembers. The oldest are forgotten first: a cancel of such an old
order is no longer serialized with the other commands of its instrument.

### Struct BackpressureManager

```rust
pub struct BackpressureManager { /* private fields */ }
```
The buffers of all active subscriptions of one connector, plus the connector’s degraded flag.

Used from the parser task: `ingest` puts an event into the buffer of its
subscription without blocking, and `pop_ready` takes the next one out for
delivery. When any `Lossless` buffer overflows, the manager marks the whole connector degraded.
A degraded connector refuses trading commands with [`brokapi_core::Error::Degraded`] (the
caller applies that, for example through `CommandGate`) until a successful
reconcile calls `clear_degraded`.

Most connectors use `Emitter`, which owns a manager and also moves events
into the output channel.

- `pub fn new() -> Self`
  Creates a manager with no subscriptions, not degraded.

- `pub fn register( &mut self, sub: SubscriptionId, instrument: InstrumentId, policy: Backpressure, )`
  Adds a buffer for a subscription.

  Registering the same `sub` twice creates two buffers. Only the first one is ever used, so
  register each subscription id once.

- `pub fn unregister(&mut self, sub: SubscriptionId)`
  Removes the buffer of a subscription. Events still waiting in it are discarded.
  An unknown `sub` is ignored.

- `pub fn is_degraded(&self) -> bool`
  Whether some `Lossless` buffer overflowed since the last `clear_degraded`.

- `pub fn clear_degraded(&mut self)`
  Leaves the degraded state and reopens every overflowed buffer.

  Call it after a successful reconcile, when the consumer has re-read the state it lost.
  Events that were refused during the overflow are not recovered.

- `pub fn ingest( &mut self, sub: SubscriptionId, event: Event, lost_from: UnixNanos, ) -> PushResult`
  Offers an event to the buffer of `sub` without blocking.

  An `PushResult::Overflow` also sets the degraded flag of the manager. The `lost_from`
  argument is accepted for the caller’s convenience and is not used by the manager: the
  caller builds the [`Event::Overflow`] notice itself (see `SubscriptionBuffer::overflow_event`).

  An event for a `sub` that is not registered is discarded and the result is still
  `PushResult::Delivered`. Register the subscription before its first event can arrive.

- `pub fn pop_ready(&mut self, sub: SubscriptionId) -> Option<Event>`
  Takes the next event of `sub` that is ready for delivery, if any.

  A `Conflate` subscription yields its latest value of each kind, one per call. Others yield their queue front, oldest
  first. Returns `None` for an empty buffer and for an unknown `sub`.

  Examples

### Struct BrokerLimiter

```rust
pub struct BrokerLimiter { /* private fields */ }
```
Shared multi-bucket rate limiter: one instance per broker user.

Every request a connector sends first calls `acquire` with the name of the
method. The limiter spends one token from each bucket on the method’s route, or spends nothing
and refuses. It never waits and never sends anything. The caller decides whether to wait for
`retry_after` and try again.

Give the same `Arc<BrokerLimiter>` to every connector of one broker user (market data and user
data together). The broker counts calls per user, so two limiters for one user would each let
through the full rate and together exceed it. A different user (another token) needs its own
limiter.

The limiter is thread-safe. Its internal lock is held only for a short, non-blocking update.

- `pub fn new(cfg: &BrokerLimits) -> Arc<Self>`
  Creates a limiter from a policy. Every bucket starts full.

  The limiter copies the policy, so later changes to `cfg` have no effect on it. Returns an
  `Arc` because the limiter is meant to be shared between connectors.

  Examples

- `pub fn conn_quota(&self) -> ConnQuota`
  The connection quota from the policy this limiter was built with.

  The limiter does not enforce it, see `ConnQuota`.

  Examples

- `pub fn usage(&self, now: Instant) -> Vec<(BucketId, u32, u32)>`
  How much of each bucket is left at `now`: `(bucket id, tokens left, capacity)`, sorted by id. Nothing is spent. For
  a program that shows how close a client is to its limits.

  Examples

- `pub fn acquire(&self, method: &str, now: Instant) -> Result<(), Error>`
  Spends one token from every bucket on the route of `method`, all or nothing.

  The route is the method’s own entry, else the `DEFAULT_ROUTE` entry, else empty (then the
  call is free). Buckets are taken in sorted id order. If any bucket is empty, the tokens
  already taken from the earlier buckets are given back (up to each bucket’s `burst`) and the
  call is refused. A refused call therefore costs nothing.

  `now` is the current time. Pass `Instant::now()` in production. Tests pass a made-up
  instant to move time without sleeping. Time must not go backwards: an instant earlier than
  the last one seen refills nothing.

  The `retry_after` of a refusal is the time the first empty bucket (in sorted order) needs
  to get one token back. Another bucket on the route may need longer, so a retry after
  `retry_after` can be refused again.

  A refusal is local: nothing was sent to the broker, so repeating the call later cannot
  create a duplicate order.

  Errors

  - [`brokapi_core::Error::RateLimited`] when a bucket on the route has less than one token.

  - [`brokapi_core::Error::Config`] when the route names a bucket that is not defined. No
  token is spent in this case.

  Examples

### Struct BrokerLimits

```rust
pub struct BrokerLimits {
    pub buckets: HashMap<BucketId, BucketCfg>,
    pub route: HashMap<MethodId, Vec<BucketId>>,
    pub conns: ConnQuota,
}
```
The full limit policy of one broker user: token buckets, routes and connection quotas.

Normally read from the `[broker]` section of a limits file by `from_toml_str`
or `from_toml_section`, then handed to `BrokerLimiter::new`.

Limits belong to the broker user (the token or account), not to a connector. Build one
`BrokerLimiter` per user and share it between all connectors of that user. Two limiters for
one broker limit would each allow the full rate, so together they would exceed the quota the
broker gave.

- `buckets: HashMap<BucketId, BucketCfg>`
  The token buckets by name.

- `route: HashMap<MethodId, Vec<BucketId>>`
  For each method name, the buckets one call spends from (one token from each). A bucket
  named here that is missing from `buckets` is a configuration error found at
  `BrokerLimiter::acquire`.

- `conns: ConnQuota`
  Connection and subscription quotas.

- `pub fn from_toml_str(raw: &str) -> Result<Self, ConfigError>`
  Reads a limits file that holds exactly one broker section.

  The file layout is one top-level table per broker, with `buckets`, `route` and `conns`
  below it: `[bcs.buckets.global]`, `[bcs.route]`, `[bcs.conns]`. A `per` value is an integer
  followed by `ms`, `s`, `m`, `h` or `d` (`"250ms"`, `"1s"`, `"24h"`). Zero, fractions (`"1.5s"`), other
  units (`"1w"`) and spaces inside (`"1 s"`) are refused. `buckets` and `route` must be
  present. Keys the parser does not know are ignored (an older file with
  `dedicated_command_conns` still reads), so a misspelled optional key is silently lost.

  Use `from_toml_section` for a file that holds several brokers.

  Errors

  Returns [`brokapi_core::ConfigError::Message`] when the text is not valid TOML, when a
  required key is missing or has the wrong type, when `per` cannot be parsed, or when the file
  has zero or several broker sections.

  Examples

- `pub fn from_toml_section(raw: &str, section: &str) -> Result<Self, ConfigError>`
  Reads one named broker section (for example `"tbank"`) of a limits file.

  Other sections are parsed too, so they must be valid, but they are not returned. The
  format is the same as in `from_toml_str`.

  Errors

  Returns [`brokapi_core::ConfigError::Message`] for invalid TOML or an invalid section, and
  when the file has no `[section]`.

  Examples

- `pub fn from_toml_path(path: &str) -> Result<Self, ConfigError>`
  Reads a limits file from disk. The file must hold exactly one broker section.

  Same as `from_toml_str` after reading the file.

  Errors

  Returns [`brokapi_core::ConfigError::Message`] when the file cannot be read (the message
  names the path), and for every reason `from_toml_str` lists.

  Examples

- `pub fn buckets_for(&self, method: &str) -> Vec<BucketId> ⓘ`
  The buckets a call to `method` spends from: its own route, else the `DEFAULT_ROUTE`
  route, else none.

  The ids come back sorted and without repeats. This is the order in which
  `BrokerLimiter::acquire` takes the tokens. The ids are not checked against
  `buckets`.

  Examples

### Struct BucketCfg

```rust
pub struct BucketCfg {
    pub rate: u32,
    pub per: Duration,
    pub burst: u32,
}
```
Token-bucket configuration for one limit bucket.

A bucket starts full with `burst` tokens. It gains `rate` tokens every `per`, continuously (not
in steps), and never holds more than `burst`. One call spends one token. So a quiet client may
send `burst` calls at once, and a busy one is held to `rate` calls per `per`.

In a limits file the three keys are written as `rate = 10`, `per = "1s"`, `burst = 3`. The
`per` value is an integer followed by `ms`, `s`, `m`, `h` or `d` (see `BrokerLimits::from_toml_str`).

- `rate: u32`
  Tokens added per `per`. Must be greater than zero for the bucket to ever refill.

- `per: Duration`
  The period in which `rate` tokens are added.

- `burst: u32`
  Bucket capacity, and the number of tokens it holds at start.

### Type Alias BucketId

```rust
pub struct BucketId { /* private fields */ }
```
Name of a token bucket in a limits file, for example `"global"` or `"orders"`.

### Struct CandlePages

```rust
pub struct CandlePages<'a, S: MarketDataSource + ?Sized> { /* private fields */ }
```
A reader of candle history, one window per `next_page` call.

Pages come in time order. Inside the whole read every bar is delivered once: a bar that two
neighbouring windows both return (brokers differ on whether the end of a range is included) is
kept in the first. A page can be empty (a weekend, a halt): that is not the end, the end is `None`.

The type borrows the connector and the instrument and holds no network state of its own, so a
crash loses only the progress you did not save. Save `resume_from` after
each page you wrote.

- `pub fn new( src: &'a S, id: &'a InstrumentId, tf: Timeframe, range: TimeRange, opts: HistoryOpts, ) -> Self`
  Prepares a read of `tf` bars of `id` over `range`. Nothing is sent yet.

- `pub fn resume_from(&self) -> UnixNanos`
  The start of the next window: where to begin again after a stop. Equals the end of the range
  when everything is read.

- `pub fn requests(&self) -> u32`
  How many `candles` calls were made, repeats included. A call that the local rate limiter refused is counted
  too, although it never reached the broker: count the requests that went out on the broker’s side if you
  need them.

- `pub fn rate_waits(&self) -> u32`
  How many times a rate limit was waited out inside this reader (a wait of your own loop is not counted).

- `pub async fn next_page(&mut self) -> Result<Option<Vec<Candle>>, HistoryError>`
  Reads the next window. `Ok(None)` when the range is finished.

  Errors

  `HistoryError` with the error that stopped the read and `resume_from`:

  - [`Error::RateLimited`] after `HistoryOpts::max_rate_waits` pauses: the quota is spent by
  others too. Wait longer and start again from `resume_from`.

  - [`Error::Transport`] after `HistoryOpts::transport_retries` repeats: the broker is
  unreachable. Start again later.

  - [`Error::Rejected`], [`Error::Config`], [`Error::Unsupported`]: the same read fails again.
  Check the instrument, the timeframe and the window. Do not loop.

### Struct CommandGate

```rust
pub struct CommandGate { /* private fields */ }
```
Decides whether a trading command may go out now.

A connector asks the gate before it sends a place, cancel or replace. The gate refuses in three
cases, checked in this order: the connector is shutting down, it is degraded, it has not been
reconciled yet. The gate starts closed (not reconciled), so the first reconcile with the broker
opens it. Why: before the client knows which orders already exist at the broker, a new command
could duplicate or fight an order it cannot see.

The gate only holds flags. The connector sets them: `mark_reconciled`
after a successful reconcile, `mark_degraded` when the
`Emitter` reports an overflow, `begin_shutdown` when
closing. Reads (order book, portfolio, active orders) are not gated.

- `pub fn new() -> Self`
  Creates a closed gate. Same as `Default::default`.

- `pub fn is_reconciled(&self) -> bool`
  Whether `mark_reconciled` was called.

- `pub fn is_degraded(&self) -> bool`
  Whether the gate is in the degraded state.

- `pub fn mark_reconciled(&mut self)`
  Records a successful reconcile. There is no way to undo it.

- `pub fn mark_degraded(&mut self)`
  Records that events were lost (see `Emitter::is_degraded`).
  Commands are refused until `clear_degraded`.

- `pub fn clear_degraded(&mut self)`
  Leaves the degraded state. Call it after the reconcile that made up for the lost events.

- `pub fn begin_shutdown(&mut self)`
  Closes the gate for good: every later check fails with [`Error::ShuttingDown`].

- `pub fn check_command(&self) -> Result<(), Error>`
  Says whether a trading command may be sent now.

  Errors

  In this order of priority:

  - [`Error::ShuttingDown`] after `begin_shutdown`.

  - [`Error::Degraded`] while the gate is degraded.

  - [`Error::NotReconciled`] until `mark_reconciled`.

  All three mean nothing was sent, so the command may be repeated once the cause is gone
  (except after a shutdown).

### Struct ConnQuota

```rust
pub struct ConnQuota {
    pub max_conns: usize,
    pub max_subs_per_conn: usize,
    pub supports_multiplex: bool,
}
```
Connection and subscription quotas of one broker user.

`BrokerLimiter` only stores and returns these numbers (see `BrokerLimiter::conn_quota`).
It does not open or close connections. A connector decides what to do with them. For example
the Finam connector reads `max_subs_per_conn` and refuses a new stream with
[`brokapi_core::Error::Rejected`] once the quota is used. The other fields are carried for the
connectors that need them and are not enforced by the runtime.

- `max_conns: usize`
  How many connections the broker lets one user hold at the same time.

- `max_subs_per_conn: usize`
  How many subscriptions (streams) one connection may carry.

- `supports_multiplex: bool`
  Whether one connection can carry several subscriptions at once.

### Struct ConnectionHealth

```rust
pub struct ConnectionHealth {
    pub last_rx: Instant,
    pub rtt: Option<Duration>,
    pub seq_gaps: u64,
}
```
Receive-side health signals of one connection.

- `last_rx: Instant`
  When the last frame arrived. Starts at the creation time.

- `rtt: Option<Duration>`
  Round-trip time of the last ping, if the owner measures it. The runtime does not fill it.

- `seq_gaps: u64`
  Number of sequence gaps seen on this connection. The runtime does not count them by itself.

- `pub fn new(now: Instant) -> Self`
  Creates the health record of a connection that is considered just heard from at `now`.

- `pub fn on_rx(&mut self, now: Instant)`
  Records that a frame arrived at `now`.

- `pub fn classify( &self, now: Instant, dead_after: Duration, degraded_after: Duration, ) -> HealthClass`
  Classifies the connection by the time since the last frame.

  Silence of `dead_after` or more gives `HealthClass::Dead`. Silence of `degraded_after` or
  more gives `HealthClass::Degraded`. Otherwise `HealthClass::Healthy`. The caller should
  pass `degraded_after < dead_after`, otherwise a connection is never reported as degraded.
  A `now` earlier than `last_rx` counts as no silence.

### Struct ConnectionState

```rust
pub struct ConnectionState {
    pub id: u64,
    pub desired: SubscriptionSet,
    pub actual: HashMap<SubscriptionId, SubState>,
    pub health: ConnectionHealth,
}
```
The desired and actual subscriptions of one connection, and its health.

The owner sets what it wants (`set_desired`) and records what happens on
the wire (`mark_pending`, `mark_active`,
`mark_failed`). The difference between the two is what
`needs_resubscribe` and `needs_unsubscribe`
report. The struct does no I/O.

- `id: u64`
  Connection id, unique inside a `Supervisor`.

- `desired: SubscriptionSet`
  The subscriptions the owner wants on this connection.

- `actual: HashMap<SubscriptionId, SubState>`
  What is known about each subscription on the wire.

- `health: ConnectionHealth`
  Receive-side health of the connection.

- `pub fn new(id: u64, now: Instant) -> Self`
  Creates a connection with nothing desired and nothing active, last heard from at `now`.

- `pub fn set_desired(&mut self, desired: SubscriptionSet)`
  Replaces the whole desired set. The `actual` map is left as it is.

- `pub fn mark_pending(&mut self, sub: &DesiredSubscription, now: Instant)`
  Records that the subscribe request for `sub` was sent at `now`.

- `pub fn mark_active(&mut self, sub_id: SubscriptionId, seq: u64)`
  Records that the broker confirmed `sub_id`, with the sequence number `seq`.

- `pub fn mark_failed(&mut self, sub_id: SubscriptionId, reason: impl Into<String>)`
  Records that `sub_id` was refused or broke, with a cause for logs.

- `pub fn remove(&mut self, sub_id: SubscriptionId)`
  Forgets the actual state of `sub_id`. It does not touch the desired set.

- `pub fn classify( &self, now: Instant, dead_after: Duration, degraded_after: Duration, ) -> HealthClass`
  Classifies the connection health, see `ConnectionHealth::classify`.

- `pub fn needs_resubscribe(&self) -> Vec<DesiredSubscription>`
  Desired subscriptions that are not confirmed active, to be (re)sent without a full reconnect.

  A subscription is listed when it has no state, has failed, or is still pending. Pending ones
  are included whatever their age, so call this when you are ready to send again, not on every
  tick, or you will resend requests that are merely slow.

- `pub fn needs_unsubscribe( &self, previous: &SubscriptionSet, ) -> Vec<DesiredSubscription>`
  Subscriptions that were in `previous` but are no longer desired, to be cancelled.

### Constant DEFAULT_ROUTE

```rust
pub const DEFAULT_ROUTE: &str = "*";
```
Route key for “any method that has no route of its own”.

The limits files set it to the broker’s general bucket, so a call made through `raw()` for a
method the library never named still spends quota instead of going out for free. If the route
table has no `"*"` entry, an unnamed method spends nothing.

### Struct Emitter

```rust
pub struct Emitter { /* private fields */ }
```
Moves events from a connector’s reader task into the consumer’s channel, without ever blocking.

This is the egress half of a connector. Each subscription has a buffer with its
[`Backpressure`] policy. `emit` puts events into the buffers and moves as many as
the channel accepts into it with `try_send`. When the consumer is slow the channel fills and the
buffers take the strain:

PolicyA slow consumer causes
[`Backpressure::Conflate`]only the latest value is kept, older ones are replaced
[`Backpressure::DropNewest`]new events are thrown away once the buffer is full
[`Backpressure::Lossless`]the buffer overflows: an [`Event::Overflow`] notice is queued once, the event is lost, the emitter becomes degraded

Degraded means events were lost that must not be lost. The connector copies the flag
(`is_degraded`) into its command gate, so trading commands fail with
[`brokapi_core::Error::Degraded`] until a successful reconcile calls
`clear_degraded`. Why: after a hole in the order or fill stream the
client’s picture of its orders is wrong, and trading on it could be harmful.

Things to know:

- Events with no subscription (`sub` is `None`) skip the buffers. If the channel is full or
closed they are dropped and counted in `dropped_unrouted`.

- Resync notices ([`Event::Resync`], made by the protocol cores after a sequence gap or a
reconnect) travel like any other event of their subscription. The emitter does not create
them and does not treat them specially.

- Each envelope is stamped with the time it is built, which is when it leaves the buffer, not
when the event was parsed. For a slow consumer the stamp is later than the arrival.

- The [`Event::Overflow`] notice has its own queue, which is sent before the buffers. So it can
reach the consumer before older events of the same subscription that are still waiting. Treat
it as “events of this subscription are missing, re-read the state”, not as an ordered marker.

- Events for a subscription that was not `register`ed are discarded.

- All methods take `&self` and are safe to call from several tasks. Share the emitter as an
`Arc`.

- `pub fn new(source: Arc<str>, out: Sender<Envelope>) -> Arc<Self>`
  Creates an emitter that writes into `out`.

  `source` is the connector label from its config (for example `"bcs-ud"`). It is put into
  every [`Envelope`] so the consumer can tell connectors apart.

- `pub fn source(&self) -> &Arc<str>`
  The `source` label given to `new`.

- `pub fn register( &self, sub: SubscriptionId, instrument: Option<InstrumentId>, policy: Backpressure, )`
  Starts buffering a subscription with the given policy.

  Call it before the first event of `sub` can arrive. `instrument` is the subscription’s
  instrument, or `None` for account-level streams such as order state or portfolio.

- `pub fn unregister(&self, sub: SubscriptionId)`
  Stops buffering a subscription. Events still waiting for it are discarded.

  An [`Event::Overflow`] notice already queued for the subscription is still sent.

- `pub fn is_degraded(&self) -> bool`
  Whether the connector lost events and no reconcile has cleared it yet: a `Lossless` subscription
  overflowed, or a stream of orders or fills was cut and reopened ([`Event::Reconnected`]).

- `pub fn mark_degraded(&self)`
  Enters the degraded state by hand: the owner knows that events were lost for a reason the emitter
  cannot see (a message that could not be processed). The trading gate closes until the next
  successful reconcile, like after an overflow. Send an [`Event::Overflow`] for the subscription
  to tell the consumer.

- `pub fn clear_degraded(&self)`
  Leaves the degraded state. Call it from a successful reconcile.

  Overflowed `Lossless` buffers accept events again and a new overflow sends a new
  [`Event::Overflow`]. Events lost during the overflow are not recovered, the reconcile is
  what makes up for them. Events already waiting in the buffers are still delivered.

- `pub fn has_backlog(&self) -> bool`
  Whether some events or notices are waiting because the channel was full.

  While it is `true`, call `flush` again later. `emit` also
  retries on every call.

- `pub fn dropped_unrouted(&self) -> u64`
  How many events with no subscription were dropped because the channel was full or closed.

- `pub fn emit(&self, events: Vec<CoreEvent>)`
  Buffers events and moves what the channel accepts. Never blocks.

  Events with a subscription go through that subscription’s buffer and policy. A `Lossless`
  overflow queues one [`Event::Overflow`] (with `lost_from` set to the current wall clock)
  per subscription and marks the emitter degraded. Events with no subscription are sent
  straight to the channel or dropped, see the type docs. Nothing is returned: the outcome is
  visible through `is_degraded`, `has_backlog` and
  `dropped_unrouted`.

- `pub fn flush(&self)`
  Retries delivery of buffered events and notices. Never blocks.

  Call it periodically while `has_backlog` is `true`, for example when
  the consumer has read from the channel.

### Struct EnvelopeReader

```rust
pub struct EnvelopeReader<R: BufRead> { /* private fields */ }
```
Reads envelopes back from any `BufRead`, in the order they were written.

An iterator of `Result<Envelope, LogError>`. After the first error the iterator ends: what
follows a bad line cannot be trusted to be in order.

- `pub fn new(input: R) -> Result<Self, LogError>`
  Opens a log: reads and checks the header line.

  Errors

  - `LogError::Format` when the first line is not a log header (an empty file included).

  - `LogError::Schema` when the file is of a newer format or holds newer envelopes.

  - `LogError::Io` when the read fails.

### Struct EnvelopeWriter

```rust
pub struct EnvelopeWriter<W: Write> { /* private fields */ }
```
Appends envelopes to any `Write`: a file, a buffer, a compressor.

Wrap a file in `std::io::BufWriter` yourself when you write many envelopes: the writer does not
buffer. `flush` after the last write, or `finish`, so the tail
is not left in a buffer when the program ends.

- `pub fn new(out: W) -> Result<Self, LogError>`
  Starts a new log in `out`: writes the header line.

  Errors

  `LogError::Io` when the header cannot be written.

- `pub fn write(&mut self, envelope: &Envelope) -> Result<(), LogError>`
  Appends one envelope as one line.

  Errors

  `LogError::Io` when the write fails. The line may be half written then: do not append to
  the same file after an error, start a new one.

- `pub fn written(&self) -> u64`
  How many envelopes were written (the header is not counted).

- `pub fn flush(&mut self) -> Result<(), LogError>`
  Flushes the underlying writer.

  Errors

  `LogError::Io` when the flush fails.

- `pub fn finish(self) -> Result<W, LogError>`
  Flushes and gives the underlying writer back.

  Errors

  `LogError::Io` when the flush fails.

### Struct FinishedWindow

```rust
pub struct FinishedWindow<K> { /* private fields */ }
```
A memory window for things that belong to finished orders: the cid maps of a connector.

A connector keeps small maps next to its `OrderRuntime` (cid to instrument, broker id to cid, …).
When an order is final, the connector `push`es its cid here. Whatever is older than the
window, or beyond the count limit, comes back from `expired`, and the connector drops
the entries of those cids. So the maps stay as big as a day of trading and not as the whole life of the
process.

- `pub fn new() -> Self`
  A window of `RETENTION` and `RETAINED_FINISHED`.

- `pub fn with_limits(window: Duration, max: usize) -> Self`
  A window that keeps a key for `window`, and at most `max` keys.

- `pub fn push(&mut self, now: Instant, key: K)`
  Notes that the order `key` finished at `now`.

- `pub fn expired(&mut self, now: Instant) -> Vec<K>`
  Takes out the keys that are older than the window as of `now`, or beyond the count limit, oldest
  first. The caller drops what it kept for them.

- `pub fn len(&self) -> usize`
  How many keys are held.

- `pub fn is_empty(&self) -> bool`
  `true` when no key is held.

### Enum HealthClass

```rust
pub enum HealthClass {
    Healthy,
    Degraded,
    Dead,
}
```
How healthy a connection looks, judged by how long it has been silent.

The `Supervisor` maps each class to an action.

- `Healthy`
  Data arrived recently. Nothing to do.

- `Degraded`
  The connection has been quiet for longer than the degraded threshold but not yet long
  enough to be declared dead. Missing subscriptions should be retried.

- `Dead`
  Quiet for at least the dead threshold. The connection should be replaced.

### Struct HistoryError

```rust
pub struct HistoryError {
    pub error: Error,
    pub resume_from: UnixNanos,
}
```
A history read stopped. Carries the error and the place to start from again.

- `error: Error`
  What stopped the read. [`Error::class`] says whether it makes sense to try again later.

- `resume_from: UnixNanos`
  The start of the window that failed. Everything before it was delivered. Start a new
  `CandlePages` with a range from here and nothing is read twice.

### Struct HistoryOpts

```rust
pub struct HistoryOpts {
    pub window: Option<Duration>,
    pub max_rate_waits: u32,
    pub max_rate_wait: Duration,
    pub transport_retries: u32,
    pub transport_backoff: Duration,
}
```
How `CandlePages` cuts the range and how patient it is.

- `window: Option<Duration>`
  The span of one request. `None`: take [`MarketDataSource::candles_max_span`], and when the
  connector does not know it, read the whole range in one request.

- `max_rate_waits: u32`
  How many times one window waits out [`Error::RateLimited`] before the error is returned.
  Default 10.

- `max_rate_wait: Duration`
  The longest single pause on [`Error::RateLimited`]. The pause is the `retry_after` of the
  error, cut to this value. Default 60 seconds.

- `transport_retries: u32`
  How many times one window is read again after [`Error::Transport`]. Default 2.

- `transport_backoff: Duration`
  The pause before the first repeat after [`Error::Transport`]. It doubles for each next
  repeat. Default 1 second.

### Type Alias InstrumentGuard

```rust
pub struct InstrumentGuard { /* private fields */ }
```
Held while a command on the instrument is on the wire.

### Struct InstrumentLocks

```rust
pub struct InstrumentLocks { /* private fields */ }
```
Per-instrument command serialization: one trading command at a time on each instrument.

The lock is keyed by [`InstrumentKey`] (symbol and mic), not by the whole [`InstrumentId`]: the
same instrument reached through a ticker, a uid or a figi is still one instrument. Waiters are
served in the order they asked (the lock is FIFO), so a cancel sent before a place reaches the
broker before it. Why: the broker’s command channel is not ordered, and a place that overtakes
a cancel would act on a different book than the caller intended.

Different instruments do not wait for each other. The lock is released when the guard is dropped,
on every exit path, including errors and cancelled futures.

`bind` and `instrument_of` remember which instrument a cid
belongs to, for commands that carry only a cid (cancel, replace).

- `pub fn new() -> Self`
  Creates an empty set of locks.

- `pub async fn lock(&self, instrument: &InstrumentId) -> InstrumentGuard`
  Waits for earlier commands on `instrument`, then holds it until the guard drops.

- `pub fn is_busy(&self, instrument: &InstrumentId) -> bool`
  Whether a command on the instrument is on the wire right now. For tests and diagnostics: the
  answer can be stale by the time the caller acts on it.

- `pub fn bind(&self, cid: ClientOrderId, instrument: &InstrumentId)`
  Remembers the instrument of an order we placed.

- `pub fn forget(&self, cid: ClientOrderId)`
  Forgets the instrument of a cid, for an order that is long finished. A later cancel of that cid is
  no longer serialized with the other commands of its instrument.

- `pub fn bound(&self) -> usize`
  How many cids are bound to an instrument. For tests and metrics.

- `pub fn instrument_of(&self, cid: ClientOrderId) -> Option<InstrumentId>`
  The instrument of a cid, if this process placed (or replaced) it.

- `pub async fn lock_cid(&self, cid: ClientOrderId) -> Option<InstrumentGuard>`
  Locks the instrument of `cid` when it is known. `None`: an order from before this process
  started, its commands are not serialized with the others.

### Constant LOG_FORMAT

```rust
pub const LOG_FORMAT: u32 = 1;
```
The version of the file format. It is in the header line of every file.

### Enum LogError

```rust
pub enum LogError {
    Io(Error),
    Format {
        line: u64,
        message: String,
    },
    Schema {
        found: u32,
        supported: u32,
    },
}
```
Why a log could not be written or read.

- `Io(Error)`
  The file or the stream failed. Check the disk and the path.

- `Format`
  A line is not what the format says. The file is damaged or is not a brokapi log. `line` counts
  from 1, the header is line 1.

- `line: u64`
  The number of the bad line.

- `message: String`
  What is wrong.

- `Schema`
  The file was written by a newer version. Update the library to read it.

- `found: u32`
  What the file declares.

- `supported: u32`
  The newest this build reads.

### Type Alias MethodId

```rust
pub struct MethodId { /* private fields */ }
```
Name of an operation in the `route` table, for example `"PlaceOrder"`.

The names are chosen by the connector that calls `BrokerLimiter::acquire`. A name that has no
entry in the route table falls back to `DEFAULT_ROUTE`.

### Struct OrderRuntime

```rust
pub struct OrderRuntime { /* private fields */ }
```
Runtime order book: one state machine per client order id, a timeout sweep, and a guard that
never lets the same place command go out twice.

The rule: a place command that got no answer is never repeated. A retry could create a second
order at the broker. Instead the order goes to [`OrderState::Unknown`] and is settled by reading
the broker (reconcile or order state). `try_place_once` enforces the
“once”, and `sweep_timeouts` turns silent in-flight orders into
`Unknown`.

**Memory**

A finished order (filled, cancelled, rejected) is kept for a window and then forgotten, so a bot that
trades for months does not grow without end. The window is `RETENTION` (24 hours) and at most
`RETAINED_FINISHED` finished orders, whichever ends first (see
`with_retention`). The guarantee “a cid is placed once” holds inside
the window. A cid older than the window is no longer remembered here, so a place with it would pass
the local check (the broker may still refuse it: T-Bank does, by its idempotency key). Never reuse a
cid: make a new one for every order.

- `pub fn new() -> Self`
  Creates an empty order book with a command timeout of 30 seconds and the default memory window
  (`RETENTION`, `RETAINED_FINISHED`).

- `pub fn with_timeout(command_timeout: Duration) -> Self`
  Creates an empty order book with the given command timeout, the age after which an in-flight
  order is considered unanswered by `sweep_timeouts`.

- `pub fn set_command_timeout(&mut self, command_timeout: Duration)`
  Changes the command timeout of `sweep_timeouts`. A connector sets it a
  little above its request timeout, so that an order is not given up while its call is still running.

- `pub fn with_retention(self, window: Duration, max_finished: usize) -> Self`
  Sets the memory window of finished orders: they are forgotten after `window`, or earlier when
  more than `max_finished` are kept (the oldest go first). See the type docs for what this means for
  the “a cid is placed once” guarantee.

  Examples

- `pub fn prune(&mut self, now: Instant)`
  Forgets the finished orders that are older than the window, or beyond the count limit, as of `now`.
  Called by `apply_signal` itself, so a caller rarely needs it.

- `pub fn tracked(&self) -> usize`
  How many orders are tracked now, in any state. For tests and metrics.

- `pub fn remembered_places(&self) -> usize`
  How many cids are remembered as used for a place. For tests and metrics.

- `pub fn in_flight_count(&self) -> usize`
  How many orders are `InFlight` now: sent, with no answer yet.

- `pub fn register_place(&mut self, cid: ClientOrderId, now: Instant)`
  Starts tracking a place command that is being sent at `now`, in state `InFlight`.

  Records the first (and only allowed) attempt for `cid`. Registering a cid again restarts its
  state machine from `InFlight`.

- `pub fn register_cancel(&mut self, cid: ClientOrderId, now: Instant)`
  Starts tracking a cid known only from a cancel, if it is not tracked yet.

  An order placed by an earlier run is not in memory. A cid that is already tracked keeps its
  state. A new one starts as `InFlight`, and does not count as a place attempt.

- `pub fn may_send_place(&mut self, cid: ClientOrderId) -> bool`
  Returns `true` and records the attempt when `cid` has never been used for a place, `false`
  when it has.

  Why: the library must not retry a place on its own. Compare
  `try_place_once`, which does the same.

- `pub fn try_place_once(&mut self, cid: ClientOrderId) -> bool`
  Returns `true` only on the first place attempt for this cid, and records that attempt.

  Call it before sending. The attempt is recorded even when the send then fails locally (for
  example a rate-limit refusal), so the same cid is never used twice. To try again, make a new
  cid. After `register_place` the answer is always `false`. The record is
  kept for the memory window of the type, see there.

  Examples

- `pub fn apply_signal( &mut self, cid: ClientOrderId, signal: OrderSignal, now: Instant, ) -> Option<OrderState>`
  Feeds a signal (ack, fill, cancel, reject, timeout) into the order’s state machine.

  Returns the new state when the state changed. Returns `None` when nothing changed, when
  the cid is not tracked, and when the signal is not valid in the current state (for example
  a cancel after `Filled`). An invalid signal is logged at warn level. A caller that needs to tell
  these apart uses `try_apply_signal`.

- `pub fn try_apply_signal( &mut self, cid: ClientOrderId, signal: OrderSignal, now: Instant, ) -> Result<Option<OrderState>, FsmError>`
  Like `apply_signal` but tells the reason when the signal is not applied.

  Errors

  [`FsmError::UnknownOrder`] for a cid that is not tracked, [`FsmError::InvalidTransition`] for a
  signal that contradicts the state (a cancel of a filled order). The state is left as it was.

  Examples

- `pub fn sweep_timeouts( &mut self, now: Instant, ) -> Vec<(ClientOrderId, OrderState)>`
  Moves every `InFlight` order older than the command timeout to `Unknown`.

  Returns the orders that changed, with their new state. It does not cancel or retry
  anything: the exchange order may well exist.

- `pub fn state(&self, cid: ClientOrderId) -> Option<&OrderState>`
  The current state of a tracked order, or `None` for an unknown cid.

- `pub fn abandon_inflight(&mut self, now: Instant) -> Vec<ClientOrderId> ⓘ`
  Moves every `InFlight` order to `Unknown` now, whatever its age. Used at shutdown.

  Returns the cids that changed. Orders at the broker are untouched, only the local wait for
  an answer is given up.

### Struct PriceGrid

```rust
pub struct PriceGrid { /* private fields */ }
```
The price step of each instrument the connector was told about.

A connector that sends orders refuses a price that is not a whole number of steps before it
sends anything: the error is [`Error::Rejected`] with the code `price_off_step`, no quota is spent
and the cid stays unused. An instrument whose step is not known passes unchecked: the broker
decides then. The grid starts empty. Fill it from [`TradingParams`], which you read once per
instrument with
`InstrumentSource::trading_params`.

The grid also keeps the lot size of an instrument. Quantities are in units of the instrument at every
broker (see [`Qty`]), and an exchange trades whole lots, so a quantity that is not a whole number of
lots is refused the same way, before anything is sent: [`Error::Rejected`] with the code
`invalid_qty`. An instrument whose lot is not known passes unchecked (the T-Bank connector looks the
lot up itself, it does not need the grid for that).

The handle is cheap to clone, and all clones are one grid: share it between the connectors of one
broker.

- `pub fn set(&self, key: InstrumentKey, step: Price)`
  Remembers the price step of an instrument. A step of zero or below forgets it.

- `pub fn learn(&self, params: &TradingParams)`
  Remembers the price step and the lot size from the trading parameters of an instrument.

- `pub fn set_lot(&self, key: InstrumentKey, lot: Qty)`
  Remembers the lot size (units of the instrument in one lot). A size of zero or below forgets it.

- `pub fn lot(&self, key: &InstrumentKey) -> Option<Qty>`
  The lot size of an instrument, if it is known.

- `pub fn check_qty(&self, key: &InstrumentKey, qty: Qty) -> Result<(), Error>`
  Checks an order quantity against the lot size of the instrument.

  Errors

  [`Error::Rejected`] with the code `invalid_qty` when the quantity is not positive, or when the
  lot size is known and the quantity is not a whole number of lots.

- `pub fn step(&self, key: &InstrumentKey) -> Option<Price>`
  The step of an instrument, if it is known.

- `pub fn check(&self, key: &InstrumentKey, price: Price) -> Result<(), Error>`
  Checks a price against the step of the instrument.

  Errors

  [`Error::Rejected`] with the code `price_off_step` when the step is known and the price is not a
  whole number of steps.

### Enum PushResult

```rust
pub enum PushResult {
    Delivered,
    Conflated,
    DroppedNewest,
    Overflow,
}
```
What happened to one event offered to a buffer. Returned by `SubscriptionBuffer::try_push` and
`BackpressureManager::ingest`.

The meaning depends on the subscription’s [`Backpressure`] policy:

PolicyPossible results
[`Backpressure::Conflate`]always `Conflated`
[`Backpressure::Lossless`]`Delivered`, then `Overflow` for ever
[`Backpressure::DropNewest`]`Delivered` or `DroppedNewest`

- `Delivered`
  The event is in the buffer and will be delivered in order.

- `Conflated`
  The event is the buffer’s “latest” value of its kind. It replaced the previous one of that kind, if any.
  This is the only result of a `Conflate` subscription, even for its first event.

- `DroppedNewest`
  The buffer was full and the new event was thrown away. Older events are kept. The loss is
  counted in `SubscriptionBuffer::dropped`. The connector does not become degraded.

- `Overflow`
  A `Lossless` buffer was full. The event was not stored. This is terminal until the buffer is
  reset: every later event of this subscription is refused too, and the owner must tell the
  consumer with [`Event::Overflow`] and mark the connector degraded (see `BackpressureManager`).

### Constant RETAINED_FINISHED

```rust
pub const RETAINED_FINISHED: usize = 100_000;
```
How many finished orders `OrderRuntime` remembers at most (100 000).

### Constant RETENTION

```rust
pub const RETENTION: Duration;
```
How long a finished order is remembered by `OrderRuntime` (24 hours).

### Enum ReconcileAction

```rust
pub enum ReconcileAction {
    ApplyDesired,
    ResubscribeMissing,
    ScheduleReconnect,
}
```
What the owner should do for one connection after a `Supervisor::reconcile` pass.

The supervisor only names the action. The owner performs it.

- `ApplyDesired`
  The connection is healthy. Make the wire match the desired set: send what
  `Supervisor::resubscribe_targets` lists and cancel what was removed.

- `ResubscribeMissing`
  The connection has been quiet for a while. Re-send the subscriptions that are missing,
  without a reconnect.

- `ScheduleReconnect`
  The connection is dead. Close it and open a new one, then apply the desired set.

### Struct ReplayReport

```rust
pub struct ReplayReport {
    pub sent: u64,
    pub stopped_by_consumer: bool,
}
```
What a finished `replay` did.

- `sent: u64`
  Envelopes sent into the channel.

- `stopped_by_consumer: bool`
  `true` when the replay ended because the receiver was dropped, not because the log ended.

### Enum ReplaySpeed

```rust
pub enum ReplaySpeed {
    Instant,
    Paced {
        factor: f64,
        max_gap: Duration,
    },
}
```
How fast `replay` plays.

- `Instant`
  As fast as the consumer reads. For backtests and tests.

- `Paced`
  At the recorded pace divided by `factor`: `1.0` is real time, `10.0` is ten times faster.
  The pace is the difference of `rx_ts` of neighbouring envelopes. A pause is never longer
  than `max_gap`, so a night between two sessions does not stop the replay.

- `factor: f64`
  The speed-up. Must be above zero.

- `max_gap: Duration`
  The longest single pause.

### Struct RuntimeMetrics

```rust
pub struct RuntimeMetrics {
    pub resync_count: u64,
    pub seq_gaps: u64,
    pub unknown_orders: u64,
    pub unknown_resolved_ms: u64,
    pub rate_limit_hits: u64,
    pub rate_limit_by_bucket: Vec<(String, u64)>,
    pub buffer_conflated: u64,
    pub buffer_dropped: u64,
    pub buffer_overflow: u64,
    pub egress_latency_p50_ms: Duration,
    pub egress_latency_p99_ms: Duration,
}
```
A plain set of counters about the connector runtime.

It is a snapshot-style struct for the owner to fill and read. The runtime does not update it by
itself and there is no exporter. All counts start at zero and only grow.

- `resync_count: u64`
  Number of resyncs recorded with `record_resync`.

- `seq_gaps: u64`
  Number of sequence gaps recorded with `record_seq_gap`.

- `unknown_orders: u64`
  Number of orders that entered the `Unknown` state. The owner counts them, no method here does.

- `unknown_resolved_ms: u64`
  Milliseconds spent resolving `Unknown` orders. The owner decides how to sum it, the runtime
  does not fill it.

- `rate_limit_hits: u64`
  Number of calls refused by the rate limiter.

- `rate_limit_by_bucket: Vec<(String, u64)>`
  The same refusals split by bucket name, in order of first appearance.

- `buffer_conflated: u64`
  Events replaced by newer ones in `Conflate` buffers, added with `record_conflated`.

- `buffer_dropped: u64`
  Events thrown away by `DropNewest` buffers, added with `record_dropped`.

- `buffer_overflow: u64`
  Overflows of `Lossless` buffers, counted with `record_overflow`.

- `egress_latency_p50_ms: Duration`
  Median time from parsing an event to handing it to the consumer. Not filled by the runtime.

- `egress_latency_p99_ms: Duration`
  99th percentile of the same time. Not filled by the runtime.

- `pub fn new() -> Self`
  Creates a zeroed set. Same as `Default::default`.

- `pub fn record_resync(&mut self)`
  Adds one to `resync_count`.

- `pub fn record_seq_gap(&mut self)`
  Adds one to `seq_gaps`.

- `pub fn record_rate_limit(&mut self, bucket: &str)`
  Counts one rate-limit refusal, in total and for `bucket`.

- `pub fn record_conflated(&mut self, n: u64)`
  Adds `n` to `buffer_conflated`.

- `pub fn record_dropped(&mut self, n: u64)`
  Adds `n` to `buffer_dropped`.

- `pub fn record_overflow(&mut self)`
  Adds one to `buffer_overflow`.

### Constant SUBSCRIPTION_REFUSED

```rust
pub const SUBSCRIPTION_REFUSED: &str = "subscription_refused";
```
The code of the [`Error::Rejected`] that a refused subscription gives. The same at every broker.

### Struct ShutdownController

```rust
pub struct ShutdownController {
    pub root: CancellationToken,
    /* private fields */
}
```
Shutdown coordinator for one connector instance: it closes the command gate, gives up waiting for
in-flight commands and cancels the background tasks.

The controller does no network I/O. The connector supplies the work that needs the network (the
unsubscribe calls) as a closure. Orders at the broker are never cancelled by a shutdown: only
the local wait for an answer is given up (orders in `InFlight` become `Unknown`).

- `root: CancellationToken`
  Parent cancellation token. Cancelled by `shutdown_local`.

- `pub fn new(gate: CommandGate) -> Self`
  Creates a controller that owns the connector’s command gate.

- `pub fn child_token(&self) -> CancellationToken`
  A token for one background task. It is cancelled when the controller shuts down.

- `pub fn gate(&self) -> &CommandGate`
  The command gate, for checking commands.

- `pub fn gate_mut(&mut self) -> &mut CommandGate`
  The command gate, for marking it reconciled or degraded.

- `pub fn shutdown_local( &mut self, mode: ShutdownMode, now: Instant, orders: &mut OrderRuntime, unsubscribe: impl FnMut() -> (usize, usize), ) -> ShutdownReport`
  Runs the local shutdown steps in order and returns a report.

  - Closes the command gate: new commands fail with [`brokapi_core::Error::ShuttingDown`].

  - In [`ShutdownMode::Graceful`] calls `unsubscribe` once. It returns how many
  subscriptions were closed and how many failed. In [`ShutdownMode::Immediate`] it is not
  called and both counts are zero.

  - Moves in-flight orders to `Unknown` (see `OrderRuntime::abandon_inflight`).

  - Cancels the root token, which stops every task made with
  `child_token`.

  The `deadline` of `Graceful` is not applied here. This method runs to completion. Use
  `wait_graceful_deadline` to wait for it. Calling the method
  twice is harmless: the second report has no abandoned orders.

- `pub async fn wait_graceful_deadline(&self, mode: ShutdownMode)`
  Sleeps for the `deadline` of a `Graceful` mode, and returns at once for `Immediate`.

  It always sleeps the full deadline. It does not return early when the work is done, so use
  it only to bound a wait together with your own completion signal.

### Enum SubState

```rust
pub enum SubState {
    Pending {
        since: Instant,
    },
    Active {
        seq: u64,
    },
    Failed {
        reason: String,
    },
}
```
What the connection knows about one subscription: the “actual” side of desired-versus-actual.

- `Pending`
  The subscribe request was sent and no answer has arrived yet.

- `since: Instant`
  When the request was sent. The runtime does not time out pending subscriptions by
  itself.

- `Active`
  The broker confirmed the subscription.

- `seq: u64`
  Sequence number of the last message seen for it, as supplied by the owner.

- `Failed`
  The broker refused the subscription or it broke.

- `reason: String`
  Human-readable cause for logs.

### Struct SubscriptionBuffer

```rust
pub struct SubscriptionBuffer { /* private fields */ }
```
Egress buffer of one subscription. It applies the subscription’s [`Backpressure`] policy.

The buffer never blocks and never waits. It is a plain in-memory queue (or single slot), not
thread-safe by itself. `BackpressureManager` owns many of them, and the emitter wraps the manager in a
lock.

- `pub fn new( sub: SubscriptionId, instrument: InstrumentId, policy: Backpressure, ) -> Self`
  Creates an empty buffer for a subscription with the given policy.

  A `cap` of `0` in `Lossless` or `DropNewest` makes a buffer that accepts nothing: the first
  event already overflows (or is dropped).

- `pub fn sub_id(&self) -> SubscriptionId`
  The subscription this buffer serves.

- `pub fn instrument(&self) -> &InstrumentId`
  The instrument of the subscription, as given to `new`.

- `pub fn is_overflowed(&self) -> bool`
  Whether a `Lossless` buffer has overflowed and not been reset yet.

- `pub fn conflated(&self) -> u64`
  How many events a `Conflate` buffer replaced before they were taken.

- `pub fn dropped(&self) -> u64`
  How many events a `DropNewest` buffer threw away.

- `pub fn len(&self) -> usize`
  How many events wait in the buffer. For `Conflate` this is `0` or `1`.

- `pub fn is_empty(&self) -> bool`
  Whether the buffer holds no event.

- `pub fn try_push(&mut self, event: Event) -> PushResult`
  Offers an event to the buffer without blocking.

  - `Conflate`: the event becomes the latest value of its kind (an order book, a quote, a portfolio
  and so on: one slot per kind). A value of the same kind that was still waiting is
  replaced and counted in `conflated`. Result: `PushResult::Conflated`.

  - `Lossless { cap }`: stored while fewer than `cap` events wait. The first event that finds
  the buffer full is not stored, the buffer is flagged overflowed and the result is
  `PushResult::Overflow`. After that every event gets `Overflow` and is not stored.

  - `DropNewest { cap }`: stored while fewer than `cap` events wait. Otherwise the new event is
  thrown away and counted in `dropped`.

  Why `Lossless` stops for good: it is used for events that must not have a hole (orders,
  fills). Once one is lost, later events would hide the gap, so the consumer must be told and
  re-read the state.

  Examples

- `pub fn overflow_event(&self, lost_from: UnixNanos) -> Event`
  Builds the [`Event::Overflow`] notice for this subscription.

  `lost_from` is the wall-clock time from which events may be missing. The buffer does not
  send the notice itself, the owner does.

  Examples

- `pub fn drain(&mut self) -> Vec<Event>`
  Takes everything out of the buffer: the latest conflated value first, then the queue in
  arrival order.

  Draining does not clear the overflowed flag. Use `BackpressureManager::clear_degraded` for that.

### Struct Supervisor

```rust
pub struct Supervisor {
    pub reconcile_interval: Duration,
    pub dead_after: Duration,
    pub degraded_after: Duration,
    pub connections: Vec<ConnectionState>,
}
```
Connection supervisor without I/O: it compares desired and actual state and says what to do.

Call `reconcile` on a timer (every `reconcile_interval`).
It returns one `ReconcileAction` per connection and changes nothing on the wire. The decision
is made by the connection’s silence: see `HealthClass`.

A new supervisor holds one connection with id `1`.

- `reconcile_interval: Duration`
  How often the owner should call `reconcile`. Default 1 second. The
  supervisor does not read it itself.

- `dead_after: Duration`
  Silence after which a connection is `HealthClass::Dead`. Default 60 seconds.

- `degraded_after: Duration`
  Silence after which a connection is `HealthClass::Degraded`. Default 15 seconds.

- `connections: Vec<ConnectionState>`
  The supervised connections.

- `pub fn new(now: Instant) -> Self`
  Creates a supervisor with one connection (id `1`) last heard from at `now`, and the default
  timings listed on the fields.

- `pub fn connection_mut(&mut self, id: u64) -> Option<&mut ConnectionState>`
  The connection with this id, if there is one.

- `pub fn reconcile(&mut self, now: Instant) -> Vec<(u64, ReconcileAction)>`
  Decides what each connection needs at `now`. Pure: the connections are not changed.

  The silence of the connection picks the action: healthy gives
  `ReconcileAction::ApplyDesired`, degraded gives `ReconcileAction::ResubscribeMissing`, dead
  gives `ReconcileAction::ScheduleReconnect`. One entry per connection, in connection order.

- `pub fn update_desired(&mut self, conn_id: u64, desired: SubscriptionSet)`
  Sets the desired subscriptions of a connection. An unknown `conn_id` is ignored.

- `pub fn resubscribe_targets(&self, conn_id: u64) -> Vec<DesiredSubscription>`
  The subscriptions of a connection that must be (re)sent, see
  `ConnectionState::needs_resubscribe`. Empty for an unknown `conn_id`.

### Function load_candles

```rust
pub async fn load_candles<S: MarketDataSource + ?Sized>(
    src: &S,
    id: &InstrumentId,
    tf: Timeframe,
    range: TimeRange,
    opts: HistoryOpts,
) -> Result<Vec<Candle>, HistoryError>
```
Reads the whole range into memory: `CandlePages` until the end.

Suits a few thousand bars. For a year of minute bars of many instruments write page by page
instead, so memory does not grow.

**Errors**

As `CandlePages::next_page`. Bars read before the error are lost with it: use
`CandlePages` when you need them.

### Function not_confirmed

```rust
pub fn not_confirmed(limit: Duration) -> Error
```
The error of a subscription that was not confirmed within `limit`: [`Error::Transport`].

### Function now

```rust
pub fn now() -> Instant
```
The current instant of the runtime clock.

Equal to `std::time::Instant::now()` in a program. In a test with a paused tokio clock it is the
virtual instant, so a rate limit that refills after one second refills when the test calls
`tokio::time::advance(Duration::from_secs(1))`, without a real pause.

### Function reconcile_ledger

```rust
pub fn reconcile_ledger(
    ledger: &[OrderSnapshot],
    broker_active: &[OrderSnapshot],
) -> ReconcileReport
```
Compares the client’s ledger of orders with the broker’s active orders and sorts them into three
groups.

- `confirmed`: in both. The broker’s snapshot is returned.

- `closed_offline`: in the ledger, not active at the broker. They filled, were cancelled or
were rejected while the client was away. Their cids are returned. The function does not say
which of these happened. Ask the broker for the order state.

- `foreign`: active at the broker, not in the ledger. They were placed by someone else (another
program, the broker’s app). They must not be touched. Why: cancelling an order the client
never created could damage someone else’s strategy.

Broker orders whose cid is the nil UUID (no client id) are ignored. They cannot be matched with
a ledger entry and are not reported as foreign. The order of the returned lists is not defined.

The function is pure: no I/O and no state.

### Function replay

```rust
pub async fn replay<I>(
    envelopes: I,
    tx: &Sender<Envelope>,
    speed: ReplaySpeed,
) -> Result<ReplayReport, LogError>where
    I: IntoIterator<Item = Result<Envelope, LogError>>,
```
Sends envelopes into a channel, in order, like a connector would.

The difference from a live connector is the one that matters for research: `replay` waits for
room in the channel ([`mpsc::Sender::send`]), so a slow consumer slows the replay down and no
event is lost. A live connector cannot wait and applies the loss policy of the subscription
instead. Code that handles `Overflow` therefore needs its own test: a replay never produces one.

The envelopes keep their `source` and `sub`, so the consumer routes them as it did live.

**Errors**

The first `LogError` from `envelopes`. Envelopes sent before it stay sent.

### Function run_detached

```rust
pub async fn run_detached<T, F>(work: F) -> Option<T>where
    F: Future<Output = T> + Send + 'static,
    T: Send + 'static,
```
Runs `work` as a task of its own and waits for its result. If the caller’s future is dropped
(a `select!` branch lost, a timeout around the call, a cancelled task), `work` is not stopped: it
runs to its end.

A trading command needs this. After the cid is marked and the order is registered as in flight, the
request, the answer and the update of the order must all happen. A command that stopped halfway would
leave the order in flight for ever, or the bot with a placed order it never heard about.

Returns `None` only when the task was torn down with the runtime (it panicked or the runtime is
shutting down). A caller maps that to an unknown outcome.

### Function wait_ack

```rust
pub async fn wait_ack<C>(
    core: &Mutex<C>,
    notify: Arc<Notify>,
    limit: Duration,
    state: impl Fn(&C) -> AckState,
) -> Result<(), Error>
```
Waits until the broker has confirmed a subscription.

`state` reads the state of the subscription from `core` and is read again after every wake of `notify`
(the core calls `notify_waiters` whenever a subscription changes state). The wait ends with:

- `Ok(())` when the state is `AckState::Confirmed`;

- [`Error::Rejected`] with the code `SUBSCRIPTION_REFUSED` when it is `AckState::Refused`: the broker
said no, repeating the call unchanged will not help;

- [`Error::Transport`] when `limit` passes with the state still `AckState::Pending`: the stream did not
open in time (the network, or a slow broker). The call may be repeated.

The limit is counted on the tokio clock, so a test moves it with `tokio::time::advance`.

The caller owns the cleanup: on `Err` it takes the subscription back (nothing stays half open).

**Errors**

As listed above.

## Крейт brokapi_bcs


### Trait BookLevelSource

```rust
pub trait BookLevelSource {
    // Required methods
    fn price(&self) -> Decimal;
    fn quantity(&self) -> Decimal;
}
```
A price level of an order book in either wire form (REST or WebSocket), so one function can map both.

- `fn price(&self) -> Decimal`
  Price of the level.

- `fn quantity(&self) -> Decimal`
  Quantity at the level, as the broker sends it.

### Struct BcsCidCodec

```rust
pub struct BcsCidCodec;
```
BCS accepts full UUID strings as `clientOrderId` (lossless, 128-bit).

### Struct BcsRaw

```rust
pub struct BcsRaw { /* private fields */ }
```
A handle for calling any BCS API method with a connector’s token and rate limiter.

Get it from `raw()` on `MarketDataConnector` or
`UserDataConnector`. It is cheap to clone, and all clones share the
token and the limiter. See the module docs for the rules of a call.

- `pub async fn call<T, F, Fut>(&self, method: &str, f: F) -> Result<T, Error>where F: FnOnce(BcsHttpClient) -> Fut, Fut: Future<Output = Result<T, BcsApiError>>,`
  Calls a read-only API method: takes the quota for `method`, then runs `f`.

  `method` is the name of the quota route, not a URL. Take a name from `limits.toml`
  (for example `GetQuotes`). An unknown name spends the default route `"*"`. `f` receives the
  connector’s [`BcsHttpClient`] and returns the future of one API call. Do not use this for
  anything that changes the account, use `call_command`.

  Errors

  - [`Error::RateLimited`] when the quota is spent. Nothing was sent. Also when the broker
  answers HTTP 429.

  - [`Error::Rejected`] with code `validation` when the broker answers HTTP 400 with a
  validation error.

  - [`Error::Transport`] for everything else: no connection, a timeout, an unreadable answer, HTTP
  404 (`not found`), a failed token refresh, any other HTTP status. A read can be repeated.

  - [`Error::Config`] when the limits file routes the name to a bucket that does not exist.

  The full table is in `crate::error::map_err`.

  Examples

  A quota that is spent refuses the call before anything is sent:

- `pub async fn call_command<T, F, Fut>( &self, method: &str, f: F, ) -> Result<T, Error>where F: FnOnce(BcsHttpClient) -> Fut, Fut: Future<Output = Result<T, BcsApiError>>,`
  Calls an API method that changes the account: takes the quota for `method`, then runs `f`.

  Like `call`, but anything that may have reached the broker is
  [`Error::Unknown`]. The library never repeats the call: a retry of a lost place request could
  create a second order. Find out what happened (read the order, run `reconcile`) before you
  decide to send it again. The order is not tracked by the connector (see the module docs).

  Errors

  - [`Error::Unknown`] when there was no answer, a break or a timeout, HTTP 500 or above, or an
  answer that cannot be read. The command may have been done.

  - [`Error::Rejected`] when the broker answered and said no: code `validation` for a validation
  error, code `http_<status>` for another status from 400 to 499 except 429. Nothing was done.

  - [`Error::RateLimited`] when the quota is spent (nothing was sent) or the broker answers HTTP 429.

  - [`Error::Transport`] for a failed token refresh and for HTTP 404 (`not found`).

  - [`Error::Config`] when the limits file routes the name to a bucket that does not exist.

  The full table is in `crate::error::map_command_err`.

  Examples

  An order sent through the raw layer, refused by the broker (the mock refuses a limit order at
  `REJECT_PRICE`). The refusal is a definite [`Error::Rejected`], not an unknown outcome:

- `pub fn connect_ws( &self, channel: BcsWsChannel, event_tx: AsyncSender<IncomingMessage>, ) -> Result<WsSession, Error>`
  Opens a WebSocket channel (`Limits`, `MarginalIndicators`, …) with the connector’s token.

  The quota name is `Connect<Channel>` (`ConnectLimits`). The session reconnects and
  resubscribes on its own. Messages arrive on `event_tx`, parsed by the protocol layer. The
  returned [`WsSession`] connects in the background: it does not wait for the connection.
  Read `event_tx`’s receiver, or the session drops messages when the channel is full (see
  `WsSession::dropped_events`).

  Errors

  - [`Error::RateLimited`] when the quota `Connect<Channel>` is spent. No socket is opened.

  - [`Error::Config`] when the limits file routes the name to a bucket that does not exist.

  Examples

### Struct Cumulative

```rust
pub struct Cumulative {
    pub qty: Decimal,
    pub value: Decimal,
}
```
What the broker had executed in one order after a message: the quantity and the value (price times
quantity) of everything filled so far, summed over all fills of the order.

The `orders/events/ws` messages carry running totals (`executedQuantity`, `executionValue`,
`averagePrice`), not the numbers of one fill. The docs sample: `lastQuantity` 50, `executedQuantity`
100, `averagePrice` 244.5, `executionValue` 24450. The price of one fill is the growth of the value
divided by the growth of the quantity between two messages of the same order, see `map_ws_execution`.

- `qty: Decimal`
  Quantity executed so far (`executedQuantity`), in pieces.

- `value: Decimal`
  Value executed so far, in the currency of the instrument: `executionValue`, else
  `averagePrice * executedQuantity`.

### Constant DEFAULT_LIMITS_TOML

```rust
pub const DEFAULT_LIMITS_TOML: &str = "# BCS Trade API rate limits for brokapi-bcs.\n#\n# Source: the table \"\u{41e}\u{433}\u{440}\u{430}\u{43d}\u{438}\u{447}\u{435}\u{43d}\u{438}\u{44f}\" of the BCS documentation (trade-api-bcs-docs, rest/restrictions): for every\n# service 10 requests per second per client account, except non-trade operations (operation history),\n# 3 per second. The orders service also has a cap of 200000 requests per day. WebSocket: 2 connections for\n# portfolio, limits and marginal indicators, 4 for orders, 20 for market data (100 instruments per connection).\n# An answer 429 means the limit is exceeded.\n#\n# What is NOT verified against a live account: whether the broker counts a second as a fixed window or as a\n# sliding one, whether \"per account\" really means per token, and whether the daily 200000 is a calendar day\n# (Moscow time) or 24 hours. Every service gets its own bucket of 10 per second with a burst of 10 (one\n# second\'s worth, so a short run of calls such as place, status, cancel, list is not held back). Sustained use\n# is held to 10 a second. The daily bucket counts only what this process sends (several processes on one account each have their own count: give the limiter to all\n# connectors of one process, see the Rate limits page of the book).\n\n[bcs.buckets.portfolio]\n# Service \"\u{41f}\u{43e}\u{440}\u{442}\u{444}\u{435}\u{43b}\u{44c}\": 10 RPS\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bcs.buckets.limits]\n# Service \"\u{41b}\u{438}\u{43c}\u{438}\u{442}\u{44b}\": 10 RPS\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bcs.buckets.reference]\n# Service \"\u{421}\u{43f}\u{440}\u{430}\u{432}\u{43e}\u{447}\u{43d}\u{438}\u{43a}\" (instruments, trading status, schedule): 10 RPS\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bcs.buckets.orders]\n# Service \"\u{417}\u{430}\u{44f}\u{432}\u{43a}\u{438}\" (place, change, cancel, the list of orders): 10 RPS\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bcs.buckets.place]\n# Not a limit of the broker: placements (and replacements, stop orders) go through this bucket too, so that\n# they can never use all of the 10 a second of the orders service. At least 2 a second are left for cancels.\nrate = 8\nper = \"1s\"\nburst = 4\n\n[bcs.buckets.orders_daily]\n# The same service, 200000 requests a day. A bucket of 200000 that refills evenly over 24 hours.\nrate = 200000\nper = \"24h\"\nburst = 200000\n\n[bcs.buckets.order_status]\n# \"\u{41f}\u{43e}\u{43b}\u{443}\u{447}\u{435}\u{43d}\u{438}\u{435} \u{441}\u{442}\u{430}\u{442}\u{443}\u{441}\u{430} \u{437}\u{430}\u{44f}\u{432}\u{43a}\u{438}\": 10 RPS, a service of its own in the table\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bcs.buckets.trades]\n# Service \"\u{421}\u{434}\u{435}\u{43b}\u{43a}\u{438}\": 10 RPS\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bcs.buckets.discounts]\n# \"\u{414}\u{438}\u{441}\u{43a}\u{43e}\u{43d}\u{442}\u{44b} \u{43f}\u{43e} \u{438}\u{43d}\u{441}\u{442}\u{440}\u{443}\u{43c}\u{435}\u{43d}\u{442}\u{430}\u{43c}\": 10 RPS\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bcs.buckets.market_data]\n# Service \"\u{420}\u{44b}\u{43d}\u{43e}\u{447}\u{43d}\u{44b}\u{435} \u{434}\u{430}\u{43d}\u{43d}\u{44b}\u{435}\": 10 RPS\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bcs.buckets.non_trade]\n# \"\u{41d}\u{435}\u{442}\u{43e}\u{440}\u{433}\u{43e}\u{432}\u{44b}\u{435} \u{43e}\u{43f}\u{435}\u{440}\u{430}\u{446}\u{438}\u{438}\" (operation history): 3 RPS\nrate = 3\nper = \"1s\"\nburst = 3\n\n[bcs.route]\n# \"*\" = any method with no route below (raw() calls). It goes to the budget of the reference service, so\n# a call made through raw() is never free.\n\"*\" = [\"reference\"]\nPlaceOrder = [\"orders\", \"orders_daily\", \"place\"]\nCancelOrder = [\"orders\", \"orders_daily\"]\nReplaceOrder = [\"orders\", \"orders_daily\", \"place\"]\nPlaceStopOrder = [\"orders\", \"orders_daily\", \"place\"]\nCancelStopOrder = [\"orders\", \"orders_daily\"]\nSearchOrders = [\"orders\", \"orders_daily\"]\nGetOrder = [\"order_status\"]\nGetOrderStatus = [\"order_status\"]\nSearchTrades = [\"trades\"]\nGetPortfolio = [\"portfolio\"]\n# A reconciliation spends the budget of its own call here, the GET per order and the list spend theirs.\nReconcile = [\"order_status\"]\nGetOrderBook = [\"market_data\"]\nSubscribeOrderBook = [\"market_data\"]\nSubscribeQuotes = [\"market_data\"]\nGetQuotes = [\"market_data\"]\nGetCandles = [\"market_data\"]\nGetLastTrades = [\"market_data\"]\nGetOperationHistory = [\"non_trade\"]\nGetLimits = [\"limits\"]\nGetInstrumentsDiscounts = [\"discounts\"]\nGetDailySchedule = [\"reference\"]\nGetTradingStatus = [\"reference\"]\nGetInstrumentsByTickers = [\"reference\"]\nGetInstrumentsByIsins = [\"reference\"]\nGetInstrumentsByType = [\"reference\"]\n\n[bcs.conns]\n# The orders channel allows 4 connections, the other channels allow 2 (portfolio, limits) and 20 (market data).\nmax_conns = 4\nmax_subs_per_conn = 50\nsupports_multiplex = false\n";
```
The text of the bundled limits file `limits.toml`, compiled into the crate.

Parse it with `limits_from_toml_str`, or take the result directly from `default_limits`.

### Constant LIST_STATUS_ACTIVE

```rust
pub const LIST_STATUS_ACTIVE: i32 = 3;
```
Order status of the list: 3 = active.

### Struct MappedMarketEvent

```rust
pub struct MappedMarketEvent {
    pub sub: Option<SubscriptionId>,
    pub event: Event,
}
```
An event mapped from a market data message, with the subscription it belongs to if known.

- `sub: Option<SubscriptionId>`
  The subscription the event belongs to, when the caller of the mapper knew it.

- `event: Event`
  The mapped event.

### Struct MarketDataConfig

```rust
pub struct MarketDataConfig {
    pub bcs: BcsConfig,
    pub source: Arc<str>,
    pub reconnect_min: Duration,
    pub reconnect_max: Duration,
    pub subscribe_ack_timeout: Duration,
}
```
Settings of `MarketDataConnector`.

- `bcs: BcsConfig`
  Client settings of the BCS API: refresh token, timeouts, endpoints, write switch.

- `source: Arc<str>`
  Envelope source label, e.g. `"bcs-md"`.

- `reconnect_min: Duration`
  Pause before the first reconnect of the WebSocket, doubling up to `reconnect_max`
  (default 0.5 s, as at T-Bank and Finam).

- `reconnect_max: Duration`
  Upper bound of the pause (default 30 s). The pause returns to `reconnect_min` once a connection
  has lived 10 seconds.

- `subscribe_ack_timeout: Duration`
  How long a `subscribe_*` call waits for the broker to confirm the subscription (default 3 s). The call
  returns when the broker has confirmed: what happens after that is delivered. A refusal is returned as
  `Rejected` (`subscription_refused`). If nothing comes in time, the call fails with `Transport` and the
  subscription is taken back, so the call can be repeated.

- `pub fn new(bcs: BcsConfig, source: impl Into<Arc<str>>) -> Self`
  Creates a config with the default reconnect pauses (0.5 s up to 30 s) and a 3 s wait for the
  confirmation of a subscription.

  `source` is the label put in every `Envelope` this connector emits,
  so a bot with several connectors can tell them apart.

### Struct MarketDataConnector

```rust
pub struct MarketDataConnector { /* private fields */ }
```
BCS market data connector: order books, trades, quotes, candles and the instrument directory.

One connector per broker is enough: it is not tied to an account. It implements
[`OrderBookSource`], [`MarketDataSource`] and [`InstrumentSource`]. Commands return their
answer directly. Subscriptions deliver [`Envelope`]s into the channel given to
`start`.

- `pub async fn start( cfg: MarketDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Starts the connector: gets the first access token and opens the market data WebSocket.

  The refresh token in `cfg` is exchanged for an access token before anything else, so a wrong
  token fails here and not on the first request. This step does not spend rate limit quota.
  The WebSocket session is started in the background. It reconnects on its own and restores the
  subscriptions after a reconnect. A reconnect voids the order books: the stream tells the bot
  with `Event::Resync` before the new snapshot.

  `limiter` is shared by value of `Arc`: give the same limiter to every connector of one BCS
  account, because the limits belong to the token. `out` receives the envelopes of all
  subscriptions. Its capacity is the bot’s choice. The bot must read it, because a slow reader
  triggers the backpressure policy of each subscription.

  Errors

  - [`Error::Config`] when the HTTP or WebSocket client cannot be built from `cfg`.

  - [`Error::Transport`] when the token refresh fails: no connection, a timeout, or the auth
  server refuses the refresh token. A refused or expired refresh token is a permanent failure,
  it will not heal on retry (exact auth server answers: not verified against the live API).

  - [`Error::RateLimited`] when the auth server answers HTTP 429.

  Examples

  A broker that cannot be reached is a [`Error::Transport`], and nothing is left running:

- `pub fn raw(&self) -> BcsRaw`
  Returns a handle for any other method of the BCS API, with this connector’s token, endpoint and
  rate limiter.

  The handle is cheap to clone and stays usable while the connector lives. Every call through it
  spends quota by the operation name you give. See `BcsRaw` for the rules of a call.

  Examples

  A method the shared traits do not wrap, here the quotes snapshot:

- `pub fn caps(&self) -> MarketDataCaps`
  Describes what this connector can do, without any request.

  The answer is fixed for BCS: order books up to 20 levels, always as snapshots
  (`incremental_order_book` is `false`), and streams for order books, market trades, quotes and
  candles. A deeper `depth` in a request is cut to the maximum, it is not an error.

  Examples

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Stops the connector and reports what was cleaned up. Consumes the connector.

  - [`ShutdownMode::Immediate`]: no unsubscribe messages are sent, the WebSocket just goes away.

  - [`ShutdownMode::Graceful`]: every subscription is unsubscribed on the WebSocket first. The
  counts are in the report (`unsubscribed`, `unsubscribe_failed`). Then the events that still
  wait in the subscription buffers get time to reach the bot’s channel, until `deadline`.

  After the call the event pump has stopped, and no more envelopes arrive. Market data holds no
  orders, so `inflight_abandoned` is always empty here.

  Examples

  A graceful shutdown with one live subscription:

### Struct MdCore

```rust
pub struct MdCore { /* private fields */ }
```
Sans-io market data core of the BCS market data WebSocket.

It turns inbound frames into [`Event`]s (order books, quotes, trades, candles), tracks which
subscriptions the broker has confirmed, and after a reconnect tells the consumer that the order
books are void (`Event::Resync` with [`ResyncReason::SubscriptionReset`]). It does no I/O.

- `pub fn new() -> Self`
  Creates a core with no subscriptions.

- `pub fn notifier(&self) -> Arc<Notify>`
  The signal that is given whenever a subscription is confirmed or refused. A connector waits on it
  (with [`brokapi_runtime::wait_ack`]) to hold `subscribe_*` until the broker has answered.

- `pub fn failure(&self, id: SubscriptionId) -> Option<&str>`
  Why subscription `id` failed, in the words of the broker, if it did.

- `pub fn desired(&self) -> &SubscriptionSet`
  Subscriptions the connector asked for (input of the last `apply`).

- `pub fn actual_state(&self, id: SubscriptionId) -> Option<SubState>`
  The state of subscription `id`, or `None` if the core does not know it (never applied, or
  already unsubscribed).

- `pub fn feed_raw(&mut self, text: &str, now: Instant, out: &mut EventSink)`
  Feeds one raw text frame of the market data WebSocket.

  A frame with an `errors` array is a subscribe error: the pending subscription it is about becomes
  `SubState::Failed` (all pending ones if the frame does not say which, see `fail_pending`) and no
  event is pushed. Any other frame is parsed and handled like
  `on_frame` does. A frame that cannot be parsed is skipped, there is
  no error to return.

### Enum MdSubKind

```rust
pub enum MdSubKind {
    OrderBook,
    MarketTrades,
    Quotes,
    Candles,
}
```
What a market data subscription delivers.

- `OrderBook`
  Order book snapshots of one instrument.

- `MarketTrades`
  Trades of the market (the tape) of one instrument.

- `Quotes`
  Best bid and ask of one instrument.

- `Candles`
  The latest candle of one instrument and timeframe.

### Struct MdSubscription

```rust
pub struct MdSubscription {
    pub id: SubscriptionId,
    pub instrument: InstrumentId,
    pub kind: MdSubKind,
    pub opts: SubscribeOpts,
    pub ws: MarketDataSubscribe,
}
```
One live market data subscription, as the connector remembers it.

- `id: SubscriptionId`
  The id the bot got from `subscribe_*`.

- `instrument: InstrumentId`
  The instrument the data is about.

- `kind: MdSubKind`
  What the subscription delivers.

- `opts: SubscribeOpts`
  The options given at subscribe time (backpressure policy, depth).

- `ws: MarketDataSubscribe`
  The WebSocket message that created it. It is replayed after a reconnect, and its mirror
  image is sent to unsubscribe.

### Constant PLAIN_LIST_TYPES

```rust
pub const PLAIN_LIST_TYPES: [i32; 4];
```
Order types of the order LIST (`POST /orders/search`) that are plain orders: 1 market, 2 limit,
3 iceberg, 10 limit for 30 days. Types 4..7 and 11 are conditional (stop) orders.

### Constant STOP_LIST_TYPES

```rust
pub const STOP_LIST_TYPES: [i32; 5];
```
Order types of the list that are conditional orders: 4 stop-limit, 5 and 11 take-profit,
6 stop-loss, 7 take-profit and stop-loss.

### Struct SubIdGen

```rust
pub struct SubIdGen(/* private fields */);
```
Hands out subscription ids of one connector, in increasing order, from a thread-safe counter.

The market data connector counts from 1, the user data connector from 1001, so the ids of the
two connectors of one bot do not collide.

- `pub fn new(start: u64) -> Self`
  Creates a generator whose first id is `start + 1`.

- `pub fn next(&self) -> SubscriptionId`
  Returns the next id. Safe to call from several tasks.

### Enum SubState

```rust
pub enum SubState {
    Pending,
    Active,
    Failed,
}
```
The state of one subscription as the core sees it.

- `Pending`
  The subscribe message is queued or sent, and the broker has not confirmed it yet. Also the
  state after a reconnect, until the broker confirms again. Data that arrives for a pending
  subscription marks it active, because data can overtake the confirmation.

- `Active`
  The broker confirmed the subscription, or data for it has arrived.

- `Failed`
  The broker answered with an error while the subscription was pending. The core does not
  retry. The connector reports it to the caller.

### Struct UdCore

```rust
pub struct UdCore { /* private fields */ }
```
Sans-io user data core: order statuses, fills and portfolio updates from the WebSocket channels.

It turns frames into `Event`s (orders, executions, portfolio), delivers each
fill once even if the broker repeats it (de-duplicated by `executionId`, the last 20 000 are
remembered), and remembers which broker order id belongs to which client order id. It does no I/O.

- `pub fn new() -> Self`
  Creates a core with no subscriptions and no known orders. The answer timer for a registered
  place command is 30 seconds (see `register_place`).

- `pub fn learn_order_id(&mut self, order_id: &str, cid: ClientOrderId)`
  Remembers that the broker’s order `order_id` is our `cid`.

- `pub fn cid_of_order_id(&self, order_id: &str) -> Option<ClientOrderId>`
  Our cid of the broker’s order `order_id`, if this process has seen it.

- `pub fn orders(&self) -> &OrderRegistry`
  The orders this core tracks (state machine per client order id).

- `pub fn orders_mut(&mut self) -> &mut OrderRegistry`
  The same registry, for changes. Meant for tests and replays.

- `pub fn register_place(&mut self, cid: ClientOrderId, now: Instant)`
  Registers a place command that was just sent, and starts the 30 second answer timer for it.

  `UserDataConnector` does not call it (it tracks sent orders in its
  own runtime). It is here to drive the core by hand.

- `pub fn on_incoming( &mut self, msg: IncomingMessage, now: Instant, out: &mut EventSink, )`
  Handles one message already parsed by the protocol layer.

  This is what the pump calls. Order messages push `Event::Order`, and a message with a fill
  (`lastQuantity` above zero) also pushes `Event::Execution` once per `executionId`, but only
  while somebody is subscribed to executions. Portfolio messages push `Event::Portfolio`. Other
  messages are skipped.

- `pub fn feed_ws( &mut self, channel: BcsWsChannel, text: &str, now: Instant, out: &mut EventSink, )`
  Parses one raw text frame of a WebSocket `channel` and handles it like
  `on_incoming`. A frame that cannot be parsed is skipped, there is no
  error to return.

### Enum UdFrame

```rust
pub enum UdFrame {
    Ws {
        channel: BcsWsChannel,
        text: String,
    },
    Rest(UdRestFrame),
}
```
A frame for the user data core: a WebSocket push or a REST answer.

- `Ws`
  A text frame from one of the WebSocket channels.

- `channel: BcsWsChannel`
  The channel the frame came from. It decides how the text is parsed.

- `text: String`
  The text of the frame, as it came off the socket.

- `Rest(UdRestFrame)`
  The answer of a REST call.

### Enum UdRestFrame

```rust
pub enum UdRestFrame {
    Place(OrderOperationResponse),
    Cancel(OrderOperationResponse),
    Replace(OrderOperationResponse),
    OrderState(OrderStatusResponse),
    ActiveOrders(OrderSearchResponse),
}
```
The answer of a REST call, given to the user data core by hand.

`UserDataConnector` reads the answers of its own commands directly and
does not send them through the core. These frames exist to drive the core in tests and replays.

- `Place(OrderOperationResponse)`
  The answer to a place request: the order is acknowledged, an `Event::Order` is pushed.

- `Cancel(OrderOperationResponse)`
  The answer to a cancel request: the order is marked cancelled in the core, no event is pushed.

- `Replace(OrderOperationResponse)`
  The answer to a replace request: the new order is acknowledged, no event is pushed.

- `OrderState(OrderStatusResponse)`
  The answer to an order status read: an `Event::Order` with the reported state is pushed.

- `ActiveOrders(OrderSearchResponse)`
  The answer to an active orders search. The core ignores it.

### Enum UdSubKind

```rust
pub enum UdSubKind {
    OrderState,
    Portfolio,
    Executions,
}
```
What a user data subscription delivers.

- `OrderState`
  Status changes of orders.

- `Portfolio`
  Portfolio updates.

- `Executions`
  Fills of orders.

### Struct UdSubscription

```rust
pub struct UdSubscription {
    pub id: SubscriptionId,
    pub kind: UdSubKind,
    pub opts: SubscribeOpts,
}
```
One live user data subscription, as the connector remembers it.

- `id: SubscriptionId`
  The id the bot got from `subscribe_*`.

- `kind: UdSubKind`
  What the subscription delivers.

- `opts: SubscribeOpts`
  The options given at subscribe time. Order state and executions use a lossless buffer.

### Struct UserDataConfig

```rust
pub struct UserDataConfig {
    pub bcs: BcsConfig,
    pub source: Arc<str>,
    pub order_state_buffer: usize,
    pub reconnect_min: Duration,
    pub reconnect_max: Duration,
    pub subscribe_ack_timeout: Duration,
    pub price_grid: PriceGrid,
}
```
Settings of `UserDataConnector`.

For a connector that sends orders the `bcs` config must be built with `with_write(true)`.

- `bcs: BcsConfig`
  Client settings of the BCS API: refresh token, timeouts, endpoints, write switch.

- `source: Arc<str>`
  Envelope source label, e.g. `"bcs-ud"`.

- `order_state_buffer: usize`
  Capacity of the lossless buffer of each order state and execution subscription (default
  1024 events). When it is full, the subscription reports an overflow and the connector
  becomes degraded: commands are refused until the next successful `reconcile`.

- `reconnect_min: Duration`
  Pause before the first reconnect of the WebSocket, doubling up to `reconnect_max`
  (default 0.5 s, as at T-Bank and Finam).

- `reconnect_max: Duration`
  Upper bound of the pause (default 30 s). The pause returns to `reconnect_min` once a connection
  has lived 10 seconds.

- `subscribe_ack_timeout: Duration`
  How long a `subscribe_*` call waits for the WebSocket of its channel to be open (default 3 s). The
  call returns when the socket is open: an order placed right after it is not missed. If the socket does
  not open in time, the call fails with `Transport` and the subscription is taken back, so the call can
  be repeated.

- `price_grid: PriceGrid`
  Price steps the connector checks order prices against (default: empty, nothing is checked).
  Fill it with [`PriceGrid::learn`] from the trading parameters of the instruments you trade. A
  price off the step is then refused with `Rejected` (`price_off_step`) before anything is sent.

- `pub fn new(bcs: BcsConfig, source: impl Into<Arc<str>>) -> Self`
  Creates a config with the default buffer (1024 events), the default reconnect pauses
  (0.5 s up to 30 s) and a 3 s wait for the socket in `subscribe_*`.

  `source` is the label put in every `Envelope` this connector emits.

### Struct UserDataConnector

```rust
pub struct UserDataConnector { /* private fields */ }
```
BCS user data connector: orders, stop orders, portfolio and the history of fills for one account.

It implements [`OrderExecutor`], [`StopOrderExecutor`], [`ExecutionSource`] and
[`PortfolioSource`]. The account is the one the refresh token in the config belongs to. One
connector per account. Start it with `start`. Commands return their answer
directly, and subscriptions deliver [`Envelope`]s into the channel given to `start`.

**Rules that protect the account**

- Write access. The config must be built with `with_write(true)` (the environment switch
`BCS_IS_WRITE=true` does the same for `BcsConfig::from_env`). Without it the connector asks for
a read-only token. Whether the broker then refuses the orders with a definite error is not
verified against the live API. Never rely on it as a safety lock, use a read-only config
only for reading.

- Reconcile gate. Trading commands (`place`, `replace`, `cancel`, `place_stop`, `cancel_stop`)
are refused with [`Error::NotReconciled`] until `reconcile` has
succeeded once. This forces the bot to compare its own ledger of orders with the broker before
it sends a new order, so a restart cannot double an order that was placed before the crash.
Reads (`order_state`, `active_orders`, `portfolio`, `executions`) are not gated.

- Degraded. If a lossless subscription overflows (the bot reads too slowly), the gate closes
and commands return [`Error::Degraded`] until the next successful `reconcile`.

- No retry of `place`. A cid that has been sent is spent. If `place` returns
[`Error::Unknown`] (no answer, a break, HTTP 5xx), the order may exist. Ask `order_state` or
run `reconcile`. Calling `place` again with the same cid returns [`Error::Rejected`] with code
`duplicate_place`, because a retry could create a second order.

- The gate is checked twice. Once at the start, and again when the command’s turn in the queue of its
instrument comes: a gate that closed while it waited stops it, and nothing is sent.

- A cut of the order events socket is told. `Event::Reconnected` on the
order state and executions subscriptions, the gate closes until `reconcile`.

- Quantity is in pieces, a whole number, never rounded: `0.4` or `2.5` is `Rejected` with code `invalid_qty`.

- Rate limit. Quota is taken from the shared limiter before the request. A refusal is
[`Error::RateLimited`] and nothing is sent, so the same cid can be used again.

- `pub async fn start( cfg: UserDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Starts the connector: gets the first access token and opens the order events and portfolio
  WebSockets.

  The trading gate starts closed: call `reconcile` before the first
  command, or `place` returns [`Error::NotReconciled`]. The token refresh does not spend rate
  limit quota. The two WebSocket sessions run in the background and reconnect on their own.
  Order status changes and fills arrive on one channel, `orders/events/ws`.

  `limiter` should be the one shared by every connector of this BCS account, because the limits
  belong to the token. `out` receives the envelopes of all subscriptions and must be read.

  Errors

  - [`Error::Config`] when the HTTP or WebSocket client cannot be built from `cfg`.

  - [`Error::Transport`] when the token refresh fails: no connection, a timeout, or the auth
  server refuses the refresh token. Exact auth server answers: not verified against the live
  API.

  - [`Error::RateLimited`] when the auth server answers HTTP 429.

  Examples

  A broker that cannot be reached is a [`Error::Transport`]:

- `pub fn raw(&self) -> BcsRaw`
  Returns a handle for any other method of the BCS API, with this connector’s token, endpoint
  and rate limiter.

  Orders sent through the handle bypass this connector’s order state: there are no events for
  them, they are not in the ledger and `reconcile` does not know them. Use [`OrderExecutor`]
  for orders, and the handle for reads and for methods the traits do not wrap. Use
  `call_command` for anything that changes the account. See `BcsRaw`
  for the rules of a call.

  Examples

  A raw read, spending the quota of the name `GetQuotes`:

- `pub fn price_grid(&self) -> &PriceGrid`
  The price steps this connector checks order prices against. Fill it from
  `TradingParams` (`grid.learn(&params)`) to have a price off the
  step refused before anything is sent. See [`PriceGrid`].

- `pub fn caps(&self) -> UserDataCaps`
  Describes what this connector can do, without any request.

  The answer is fixed for BCS: a place request is deduplicated by label only
  ([`PlaceIdempotency::LabelOnly`], so never repeat a place), replace and stop orders are
  supported, fills are streamed, there is no command channel over WebSocket and no sandbox.

  Examples

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Stops the connector and reports what was left unfinished. Consumes the connector.

  The command gate closes first, so nothing new is sent. Orders that were placed but have no
  answer yet are listed in `inflight_abandoned` and marked unknown locally. The broker is not
  asked to cancel anything: orders that exist on the exchange stay alive, and a later
  `reconcile` finds them. With [`ShutdownMode::Graceful`] the events that wait in the
  subscription buffers first get time to reach the bot’s channel, until `deadline`. The
  WebSocket sessions are not unsubscribed message by message, so `unsubscribed` is `0`.

  Examples

### Function bcs_instrument_key

```rust
pub fn bcs_instrument_key(id: &InstrumentId) -> Option<InstrumentKey>
```
Returns the BCS instrument key of an [`InstrumentId`], or `None` when its native id is not
[`NativeId::TickerClass`] (the id came from another broker).

### Function build

```rust
pub fn build(req: &PlaceStopOrderRequest) -> Result<CreateOrderRequest, Error>
```
Wire body of a stop order. Everything that can be checked locally is checked before anything is
sent or any quota is spent.

### Function check_book_seq

```rust
pub fn check_book_seq(
    core: &mut MdCore,
    sub: SubscriptionId,
    expected: u64,
    instrument: InstrumentId,
    out: &mut EventSink,
)
```
Detects a sequence gap and emits a resync: a hook for order book integrity.

### Function class_code

```rust
pub fn class_code(id: &InstrumentId) -> Result<String, Error>
```
Returns the class code (trading board, for example `TQBR`) of an instrument id.

**Errors**

[`brokapi_core::Error::Config`] when the native id is not [`NativeId::TickerClass`], as in
`require_ticker_class`.

### Function cumulative_of

```rust
pub fn cumulative_of(data: &OrderExecutionData) -> Option<Cumulative>
```
The running totals of an order message, or `None` when the message does not carry enough to know
them (no `executedQuantity`, or neither `executionValue` nor `averagePrice`).

### Function default_limits

```rust
pub fn default_limits() -> BrokerLimits
```
The bundled BCS limits, parsed.

BCS publishes no numbers of its own, so these are cautious starting values, not the broker’s
real limits (not verified against the live API). A method with no route of its own spends the
default route `"*"`, so no call is free. Cancel has its own bucket, separate from placing, so a
burst of new orders cannot block a cancel.

**Panics**

Never in practice: the bundled file is checked by a test of this crate.

### Function drain_backlog

```rust
pub async fn drain_backlog(emitter: &Emitter, deadline: Duration)
```
Gives the bot’s channel time to take what the buffers still hold (shutdown).

### Function foreign_cid

```rust
pub fn foreign_cid(order_id: &str) -> ClientOrderId
```
Stable cid for an order of this account that this connector did not place (the broker’s order
list carries only the broker’s id, not our `clientOrderId`): the same order id always gives the
same cid, so it can be told apart from ours and found again.

### Function instrument_from_native

```rust
pub fn instrument_from_native(id: &InstrumentId) -> InstrumentKey
```
Returns the broker-neutral key of an [`InstrumentId`] (a clone of `id.key`).

### Function instrument_id

```rust
pub fn instrument_id(key: &InstrumentKey) -> InstrumentId
```
Builds an [`InstrumentId`] from a BCS instrument key. The native id is
[`NativeId::TickerClass`], the form every BCS call wants.

### Function instrument_id_ticker

```rust
pub fn instrument_id_ticker(ticker: &str, class_code: &str) -> InstrumentId
```
Builds an [`InstrumentId`] from a ticker and a class code (board). Same as `instrument_id`.

### Function limits_from_toml_path

```rust
pub fn limits_from_toml_path(path: &str) -> Result<BrokerLimits, ConfigError>
```
Reads the `[bcs]` section of a limits file on disk.

Use it when you tune the limits for your account without recompiling. Same as
`limits_from_toml_str` after reading the file.

**Errors**

[`brokapi_core::ConfigError::Message`] when the file cannot be read (the message names the path),
and for every reason `limits_from_toml_str` lists.

### Function limits_from_toml_str

```rust
pub fn limits_from_toml_str(raw: &str) -> Result<BrokerLimits, ConfigError>
```
Reads the `[bcs]` section of a limits file given as text.

Other sections of the file (other brokers) are ignored. Give every connector of one BCS account
the same `BrokerLimiter` built from the result, because the limits belong to the token.

**Errors**

[`brokapi_core::ConfigError::Message`] when the text is not valid TOML, when there is no `[bcs]`
section, or when the section is not a valid set of limits (see
[`BrokerLimits::from_toml_section`]).

### Function map_cid_err

```rust
pub fn map_cid_err(err: CidError) -> Error
```
Maps a failure to encode a client order id to [`Error::Config`], with the text of the original
error.

### Function map_command_err

```rust
pub fn map_command_err(err: BcsApiError) -> Error
```
A command that failed: only answers that prove the broker did not act become definite errors.
No answer, a break, HTTP 5xx or an unreadable answer may have reached the broker: `Error::Unknown`,
never retried by the library.

Protocol errorPublic error
`HttpError` without a status (no answer, a break, a timeout) or with status 500 and above[`Error::Unknown`]
`ParseError` (the answer cannot be read), `WsError`[`Error::Unknown`]
`HttpError` with status 400 to 499, except 429[`Error::Rejected`] with code `http_<status>` and the broker’s message
everything else (429, `AuthError`, `NotFound`, `ValidationError`, …)as in `map_err`

Note that `ValidationError` (the usual way BCS refuses an order) is [`Error::Rejected`] with code
`validation`, and that `NotFound` is [`Error::Rejected`] with code `not_found`, as in `map_err`.

### Function map_config_err

```rust
pub fn map_config_err(err: BcsApiError) -> Error
```
Maps a failure to build a client from the configuration to [`Error::Config`], with the text of
the original error.

### Function map_daily_schedule

```rust
pub fn map_daily_schedule(
    exchange: &str,
    day: NaiveDate,
    resp: &DailyScheduleResponse,
    now: Instant,
) -> Schedule
```
Maps the daily schedule of an instrument to a [`Schedule`] for `exchange` (the label the caller
used, `"SBER@TQBR"`).

Only intervals with the status `OPEN` are kept, and a day that is not a working day gives no
intervals at all. The times of day are put on `day` and read as Moscow time, which is an assumption
(see the module docs).

### Function map_err

```rust
pub fn map_err(err: BcsApiError) -> Error
```
Maps a failed read (or a failed step before a command was sent) to a public error.

Protocol errorPublic error
`RateLimitExceeded`, or `HttpError` with status 429[`Error::RateLimited`] with `retry_after` of 1 second (the broker does not say how long: the value is a fixed guess)
`AuthError` (the token is refused)[`Error::Config`] with the broker’s message: fix the token, a repeat will not help
`SubscriptionError`[`Error::Transport`] with the broker’s message
`HttpError` (any other status or no status), `WsError`, `ParseError`[`Error::Transport`] with the message
`NotFound` (HTTP 404, `type = "NOT_FOUND"`)[`Error::Rejected`] with code `not_found` (an unknown instrument or order, as at the other brokers)
`ValidationError` (HTTP 400, `type = "VALIDATION_ERROR"`)[`Error::Rejected`] with code `validation` and the field errors as the message

All [`Error::Transport`] results are retryable for a read. Do not use this function for a
command: it would turn a lost answer into a plain transport error. Use `map_command_err`.

### Function map_instrument_info

```rust
pub fn map_instrument_info(
    info: &InstrumentInfo,
    class_code: &str,
) -> Instrument
```
Maps the directory record of an instrument, for the board `class_code`.

- Name: `displayName`, else `shortName`, else the ticker.

- Kind: from the BCS type text (contains `STOCK`, `BOND`, `FUTURE`, `OPTION`, `ETF`, `FUND`,
`CURRENCY`), anything else is `Other`.

- Currency: the trading currency, `RUB` when the record has none (an assumption).

- Exchange: the exchange of the board with this class code, `MOEX` when the board is not listed
(an assumption).

### Function map_last_trade

```rust
pub fn map_last_trade(
    data: &LastTradesData,
    now: Instant,
) -> Option<MarketTrade>
```
Maps a market trade message to a [`MarketTrade`].

The side is `BUY` / `1` or `SELL` / `2`. A trade whose side is none of these is dropped (`None`)
rather than guessed, like the REST tape (`map_rest_last_trade`): a wrong side would corrupt
anything built from the stream.

### Function map_order_ack

```rust
pub fn map_order_ack(
    cid: ClientOrderId,
    broker_id: BrokerOrderId,
    now: Instant,
) -> OrderAck
```
Builds an [`OrderAck`] for a client order id and the broker’s order id. The ack carries the local
receive time only, the broker gives no time with it.

### Function map_order_cmd_err

```rust
pub fn map_order_cmd_err(err: BcsApiError) -> Error
```
`map_command_err` for commands on an existing order. “Not found” is a definite refusal
(`Rejected`, code `not_found`) in `map_command_err` already; this is the same mapping, kept for the
code that calls it.

Used by `cancel`, `replace` and `cancel_stop`.

### Function map_order_read_err

```rust
pub fn map_order_read_err(err: BcsApiError) -> Error
```
`map_err` for a read of one order. Since “not found” is `Rejected` with code `not_found` in
`map_err` already, this is the same mapping, kept for the code that calls it.

Used by `order_state`. The way out of an `Unknown` is to ask again until the
order is found or the answer stays `not_found` long enough ([`Error::Unknown`]).

### Function map_order_record

```rust
pub fn map_order_record(
    record: &OrderRecord,
    cid: ClientOrderId,
    now: Instant,
) -> Option<OrderSnapshot>
```
A plain order of the list. `cid` is what the caller knows about this order (the list does not carry
our `clientOrderId`).

### Function map_order_status

```rust
pub fn map_order_status(
    resp: &OrderStatusResponse,
    now: Instant,
) -> Option<Event>
```
Maps an order status answer to an `Event::Order`. `None` when the client order id is not a UUID
or the answer has no report.

### Function map_place_response

```rust
pub fn map_place_response(resp: &OrderOperationResponse, now: Instant) -> Event
```
Maps the plain answer of a place request (`status: OK`) to an `Event::Order` that says “acknowledged”.

The answer only says that the request was accepted, so the snapshot is a thin one: no instrument,
quantity or price (empty and zero), and the broker order id is the client order id as text. Use
`map_state_list` on the answer of a synchronous call to get the full report.

### Function map_portfolio

```rust
pub fn map_portfolio(
    positions: &[WsPortfolioPosition],
    now: Instant,
) -> Portfolio
```
Backward-compatible helper used by UD core tests.

### Function map_quotes

```rust
pub fn map_quotes(data: &QuotesData, now: Instant) -> Quote
```
Maps a quotes message to a [`Quote`].

The message has one `volume`, and it is put on both sides (bid and ask). A side the message does
not carry is `None`, and a missing volume is `0`. Whether that volume belongs to the bid, the ask
or the last trade is not verified against the live API.

### Function map_rest_candles

```rust
pub fn map_rest_candles(
    resp: &CandlesResponse,
    tf: Timeframe,
    now: Instant,
) -> Vec<Candle>
```
Maps the candles of `POST /candles-chart` to domain candles of the timeframe `tf` that was asked.

The API gives one time per bar. It is used for both `open_ts` and `close_ts`. Whether that time is
the open or the close of the bar is not verified against the live API, so do not rely on the
difference between the two fields.

### Function map_rest_last_trade

```rust
pub fn map_rest_last_trade(
    trade: &LastTradeRecord,
    instrument: &InstrumentId,
    now: Instant,
) -> Option<MarketTrade>
```
One trade of the tape (`POST /last-trades`) for `instrument`, the one asked for: the records of the
docs carry no instrument. Side is `1` buy / `2` sell (older samples: `BUY` / `SELL`); a record whose
side is neither is dropped rather than guessed.

### Function map_rest_order_book

```rust
pub fn map_rest_order_book(book: &OrderBookResponse, now: Instant) -> Event
```
Maps a REST order book answer to an [`Event::OrderBook`] holding an [`OrderBook::Snapshot`].

BCS gives full books only, so the result is always a snapshot. Levels keep the broker’s order.

### Function map_rest_portfolio

```rust
pub fn map_rest_portfolio(
    positions: &[PortfolioPosition],
    now: Instant,
) -> Portfolio
```
Maps the REST portfolio to a [`Portfolio`].

A position of type `moneyLimit` is a balance (`available` is the quantity, `blocked` is the locked
part, both `0` when missing). A position with a ticker and a board is a holding. A record with
neither is dropped. Average price and current price are `None` when the broker leaves them out.

### Function map_state_list

```rust
pub fn map_state_list(
    known_cid: Option<ClientOrderId>,
    resp: &OrderStateListResponse,
    now: Instant,
) -> Option<OrderSnapshot>
```
The latest report of an answer with a `data` list (`GET /orders`, `/orders*/sync`) as a snapshot.
`known_cid` is the cid the caller asked about (a status answer is about that order); without it the
ids of the answer are used.

### Function map_stop_record

```rust
pub fn map_stop_record(
    record: &OrderRecord,
    cid: Option<ClientOrderId>,
    now: Instant,
) -> Vec<StopOrderSnapshot>
```
A conditional order of the list as one or two stop snapshots (two for type 7, a take-profit and
stop-loss pair: the pair is one order at the broker, shown as its two legs with the same ids).
`None` for plain orders.

### Function map_trade_record

```rust
pub fn map_trade_record(record: &TradeRecord, now: Instant) -> Option<Execution>
```
A trade of the history (`POST /trades/search`). The docs describe a flat record (`tradeNum`,
`orderNum`, `tradeQuantity`, `tradeDateTime`, …) without our `clientOrderId` and without a
commission: `cid` is `None`, `commission` 0. The older `data` form is still read if it comes.

### Function map_trading_params

```rust
pub fn map_trading_params(snapshot: &TradingParamsSnapshot) -> TradingParams
```
Maps trading parameters of one board.

`min_qty` is the lot size, because BCS gives no separate minimum. Prices and steps are passed
through unchanged.

### Function map_trading_status

```rust
pub fn map_trading_status(
    ticker: &str,
    class_code: &str,
    resp: &TradingStatusResponse,
) -> TradingStatus
```
Maps the trading status of a board.

`OPEN` is [`TradingAvailability::Open`], `CLOSE` is `Closed` (any letter case), any other text is
`Unknown`. The `reason` is the session type text from the broker. The status carries no timestamp.

### Function map_ws_execution

```rust
pub fn map_ws_execution(
    msg: &OrderMessage,
    now: Instant,
    before: Option<Cumulative>,
) -> Option<Execution>
```
A fill from the `orders/events/ws` WebSocket. Only messages that carry a fill: `lastQuantity > 0`
and an `executionId`, and not a corrected report (`executionType` 13, which restates an earlier
message). The order’s cid is read as `crate::map::order::message_cid` says.

The price of the fill. `before` is the running total of the same order from the previous message
the connector saw (`cumulative_of`). With it the price is exact:

- the totals after this message minus `before` give the quantity and the value of this fill alone,
and the price is value divided by quantity. A first fill of an order (`before` is `None` and
`executedQuantity` equals `lastQuantity`) is the same with `before` of zero. Two fills at
different prices give two different prices, not their average.

- When the totals do not fit (a message was missed, so the growth of `executedQuantity` is not
`lastQuantity`, or the message has no totals), the price is the best the message says:
`averagePrice` of the order so far, else the price of the order. That is an average over earlier
fills too, so it can be off by the difference between the fills. The price of every trade is in the
history (`ExecutionSource::executions`), which is the way to settle it.

### Function map_ws_market_data

```rust
pub fn map_ws_market_data(
    msg: &MarketDataMessage,
    now: Instant,
    sub: Option<SubscriptionId>,
) -> Option<MappedMarketEvent>
```
Maps a market data WebSocket message to an event.

`None` for messages that carry no data: subscribe confirmations, error answers, undefined
messages, a trade with an unknown side, and a candle message that lacks a ticker, a class code or a price. `sub` is passed
into the result unchanged. The core fills it in.

### Function map_ws_order_book

```rust
pub fn map_ws_order_book(data: &OrderBookData, now: Instant) -> OrderBook
```
Maps a WebSocket order book message to an [`OrderBook::Snapshot`]. Every message of this stream
is a full book, not a delta.

### Function map_ws_order_message

```rust
pub fn map_ws_order_message(msg: &OrderMessage, now: Instant) -> Option<Event>
```
Maps a message of the order events WebSocket to an `Event::Order`.

The client order id is chosen by `message_cid`. `None` when neither id is a UUID or the message has no
report. Fills are mapped separately by `crate::map::execution::map_ws_execution`.

### Function map_ws_portfolio

```rust
pub fn map_ws_portfolio(
    positions: &[WsPortfolioPosition],
    now: Instant,
) -> Event
```
Maps a portfolio WebSocket message to an `Event::Portfolio`. The rules are those of
`map_rest_portfolio`. Every message is a full portfolio, not a change.

### Function message_cid

```rust
pub fn message_cid(msg: &OrderMessage) -> Option<ClientOrderId>
```
The cid of the order an `orders/events/ws` message is about.

The two ids of the message mean: `clientOrderId` is the id of the request that caused the message,
`originalClientOrderId` is the order that was changed (docs: “идентификатор измененной заявки”). For a
cancel the request id is a fresh id of the cancel, for a replace the new order’s id. So:

- only one of them is a UUID: that one (the docs sample of a fill has `clientOrderId` empty);

- both are, and the message is a cancel, a replace or a cancel in progress (`orderStatus` 4, 5, 6):
`originalClientOrderId`, the order the news is about, not the id of the request;

- both are, any other status: `clientOrderId`, the id of the order that was placed or changed.

Not verified against the live API: which id the broker puts in which field for each kind of message.
`None` when neither is a UUID.

### Function nanos_to_datetime

```rust
pub fn nanos_to_datetime(nanos: UnixNanos) -> DateTime<Utc>
```
Converts a time in nanoseconds since the Unix epoch to a UTC date and time.

Sub-second precision is kept. A value that is out of the range of dates falls back to the current
time, so this function never fails (and never panics).

### Function parse_broker_ts

```rust
pub fn parse_broker_ts(raw: Option<&str>) -> Option<UnixNanos>
```
Parses a time string of the BCS API into nanoseconds since the Unix epoch.

Reads RFC 3339 (`2026-10-19T10:00:00.123+03:00`) and the form `2026-10-19T10:00:00.123Z`. `None`
when the input is `None` or in neither form. A time with no zone is not accepted, because its zone
would be a guess.

### Function parse_subscribe_error

```rust
pub fn parse_subscribe_error(text: &str) -> Option<String>
```
Detect BCS market-data subscribe error payloads (`errors` array).

Returns the message of the first error, or a default text when it has none. `None` when the text is
not JSON or has no non-empty `errors` array.

### Function rejection_reason

```rust
pub fn rejection_reason(snap: &OrderSnapshot) -> Option<&str>
```
`true` when the report says the broker refused the order (`orderStatus` 8), with its reason.

### Function require_ticker_class

```rust
pub fn require_ticker_class(id: &InstrumentId) -> Result<InstrumentKey, Error>
```
Returns the BCS instrument key (ticker and class code) of an instrument id.

BCS names an instrument by ticker plus trading board (class code), for example `SBER` on `TQBR`.

**Errors**

[`brokapi_core::Error::Config`] when the native id of `id` is not [`NativeId::TickerClass`]. An id
that came from another broker cannot be used here.

### Function run_md_pump

```rust
pub async fn run_md_pump(
    core: Arc<Mutex<MdCore>>,
    emitter: Arc<Emitter>,
    rx: AsyncReceiver<IncomingMessage>,
    cancel: CancellationToken,
)
```
Runs the market data event pump until `cancel` fires or the frame channel closes.

Every frame from `rx` goes through `core` and the resulting events to `emitter`. Events that a
slow bot left in the subscription buffers are offered again every 20 ms.

### Function run_ud_pump

```rust
pub async fn run_ud_pump(
    core: Arc<Mutex<UdCore>>,
    emitter: Arc<Emitter>,
    rx: AsyncReceiver<IncomingMessage>,
    cancel: CancellationToken,
)
```
Runs the user data event pump until `cancel` fires or the frame channel closes.

Same loop as `run_md_pump`, with the user data core (order status, fills, portfolio).

### Function ticker

```rust
pub fn ticker(id: &InstrumentId) -> Result<String, Error>
```
Returns the ticker (for example `SBER`) of an instrument id.

**Errors**

[`brokapi_core::Error::Config`] when the native id is not [`NativeId::TickerClass`], as in
`require_ticker_class`.

### Function timeframe_label

```rust
pub fn timeframe_label(tf: Timeframe) -> Option<&'static str>
```
The label of a timeframe in BCS requests: `M1 M5 M15 M30 H1 H4 D W MN` (docs, checked 2026-10-19).
`M10` is not offered by BCS: `None`, the caller answers `Unsupported`.

### Function timestamps

```rust
pub fn timestamps(now: Instant, broker_ts: Option<UnixNanos>) -> Timestamps
```
Builds the [`Timestamps`] of an event: the local receive time now (wall clock), the monotonic
`now` of the receive, and the broker’s own time when it is known.

The broker time is optional because not every BCS message carries one.

### Function unix_nanos

```rust
pub fn unix_nanos(dt: DateTime<Utc>) -> UnixNanos
```
Converts a UTC date and time to nanoseconds since the Unix epoch. A date outside the range that
fits in 64 bits (years 1677 to 2262) gives `0`.

### Function whole_pieces

```rust
pub fn whole_pieces(qty: Qty) -> Result<i64, Error>
```
The order quantity as the whole number of pieces the BCS order API takes.

BCS counts an order in pieces (`orderQuantity`, “Количество в заявке (шт.)”, an integer of at
least 1), and so does `Qty` everywhere in this library: the number is passed
on as it is. A quantity that is not a whole number of pieces is refused, never rounded: a rounded
order would buy something the caller did not ask for.

**Errors**

[`brokapi_core::Error::Rejected`] with code `invalid_qty` for a fraction, zero, a negative number or
a number that does not fit in an `i64`.

## Крейт brokapi_tbank


### Enum CommandOutcome

```rust
pub enum CommandOutcome<T> {
    Done(T),
    Refused(Status, Error),
    Unknown,
}
```
Result of a trading command: a definite answer, or an outcome nobody knows.

The split exists so that a caller cannot treat a timeout as a refusal. Only `Refused` proves
that the broker did not act.

- `Done(T)`
  The broker answered and did the command. Holds the reply.

- `Refused(Status, Error)`
  The broker refused; `status` is its answer.

- `Unknown`
  Timeout, break, server fault: the command may or may not have been executed.

### Constant DEFAULT_LIMITS_TOML

```rust
pub const DEFAULT_LIMITS_TOML: &str = "# T-Bank (T-Invest API) rate limits.\n#\n# Source: developer.tbank.ru/invest/intro/intro/limits,\n# error codes: developer.tbank.ru/invest/errors (80001 open streams, 80002 request limit, 80006 error limit).\n#\n# SCOPE (several accounts of one user): the limits are per USER, not per account and not per\n# token. The docs say: \"\u{41b}\u{438}\u{43c}\u{438}\u{442}\u{43d}\u{430}\u{44f} \u{43f}\u{43e}\u{43b}\u{438}\u{442}\u{438}\u{43a}\u{430} \u{440}\u{430}\u{441}\u{43f}\u{440}\u{43e}\u{441}\u{442}\u{440}\u{430}\u{43d}\u{44f}\u{435}\u{442}\u{441}\u{44f} \u{43d}\u{430} \u{432}\u{441}\u{435} \u{430}\u{43a}\u{43a}\u{430}\u{443}\u{43d}\u{442}\u{44b} (\u{441}\u{447}\u{435}\u{442}\u{430}) \u{43f}\u{43e}\u{43b}\u{44c}\u{437}\u{43e}\u{432}\u{430}\u{442}\u{435}\u{43b}\u{44f}\", and the\n# total cap is counted per source address: \"\u{43e}\u{431}\u{449}\u{435}\u{435} \u{447}\u{438}\u{441}\u{43b}\u{43e} \u{437}\u{430}\u{43f}\u{440}\u{43e}\u{441}\u{43e}\u{432} \u{43f}\u{43e} \u{432}\u{441}\u{435}\u{43c} \u{441}\u{447}\u{435}\u{442}\u{430}\u{43c} \u{438} \u{442}\u{43e}\u{43a}\u{435}\u{43d}\u{430}\u{43c} \u{432} \u{441}\u{435}\u{43a}\u{443}\u{43d}\u{434}\u{443} \u{43d}\u{435}\n# \u{434}\u{43e}\u{43b}\u{436}\u{43d}\u{43e} \u{43f}\u{440}\u{435}\u{432}\u{44b}\u{448}\u{430}\u{442}\u{44c} 50\". Hence ONE BrokerLimiter for everything one user runs from one IP: every\n# UserDataConnector (any account_id) and every MarketDataConnector that uses the user\'s tokens share the\n# same Arc<BrokerLimiter>. (Accounts are isolated for portfolio/margin, not for request limits.)\n#\n# Limits are dynamic (\"\u{441}\u{438}\u{441}\u{442}\u{435}\u{43c}\u{430} \u{434}\u{438}\u{43d}\u{430}\u{43c}\u{438}\u{447}\u{435}\u{441}\u{43a}\u{43e}\u{433}\u{43e} \u{444}\u{43e}\u{440}\u{43c}\u{438}\u{440}\u{43e}\u{432}\u{430}\u{43d}\u{438}\u{44f} \u{43b}\u{438}\u{43c}\u{438}\u{442}\u{43e}\u{432}\"): the real per-user values are\n# returned by UsersService/GetUserTariff and in x-ratelimit-limit / -remaining / -reset response headers.\n# The values below are the documented defaults. Interpretation (not verified on the live API): a method\n# that has its own row in the docs table (PostOrder, CancelOrder, ReplaceOrder, GetOrders) is limited by\n# that row, the other methods of the service by the service row.\n#\n# Per-minute limits are token buckets refilled evenly over 60 s. `burst` is kept below the minute quota\n# so a script does not spend the whole minute in the first second.\n\n[tbank.buckets.global]\n# \"\u{440}\u{435}\u{43a}\u{43e}\u{43c}\u{435}\u{43d}\u{434}\u{443}\u{435}\u{43c} ... \u{43d}\u{435} \u{43f}\u{440}\u{435}\u{432}\u{44b}\u{448}\u{430}\u{43b}\u{43e} 50\" requests per second over all accounts and tokens\nrate = 50\nper = \"1s\"\nburst = 50\n\n[tbank.buckets.instruments]\n# Instruments service: 200/min\nrate = 200\nper = \"60s\"\nburst = 20\n\n[tbank.buckets.operations]\n# Operations service (GetPortfolio, GetPositions, GetOperationsByCursor): 200/min\nrate = 200\nper = \"60s\"\nburst = 20\n\n[tbank.buckets.accounts]\n# Accounts service (GetAccounts): 100/min\nrate = 100\nper = \"60s\"\nburst = 10\n\n[tbank.buckets.market_data]\n# Quotes service unary (GetOrderBook, GetLastTrades, GetCandles, GetTradingStatus): 600/min\nrate = 600\nper = \"60s\"\nburst = 50\n\n[tbank.buckets.market_data_subscribe]\n# \"\u{41c}\u{430}\u{43a}\u{441}\u{438}\u{43c}\u{430}\u{43b}\u{44c}\u{43d}\u{43e}\u{435} \u{43a}\u{43e}\u{43b}\u{438}\u{447}\u{435}\u{441}\u{442}\u{432}\u{43e} \u{437}\u{430}\u{43f}\u{440}\u{43e}\u{441}\u{43e}\u{432} \u{43f}\u{43e}\u{434}\u{43f}\u{438}\u{441}\u{43a}\u{438} \u{432} \u{43c}\u{438}\u{43d}\u{443}\u{442}\u{443} - 100\" (per stream)\nrate = 100\nper = \"60s\"\nburst = 20\n\n[tbank.buckets.orders_service]\n# Orders service total (GetOrderState and the rest): 100/min\nrate = 100\nper = \"60s\"\nburst = 20\n\n[tbank.buckets.order_post]\n# PostOrder: 15 per second (900/min)\nrate = 15\nper = \"1s\"\nburst = 15\n\n[tbank.buckets.order_cancel]\n# CancelOrder: 300/min. Separate bucket, so cancels are never starved by placements\nrate = 300\nper = \"60s\"\nburst = 30\n\n[tbank.buckets.order_replace]\n# ReplaceOrder: 300/min\nrate = 300\nper = \"60s\"\nburst = 30\n\n[tbank.buckets.orders_get]\n# GetOrders: 200/min\nrate = 200\nper = \"60s\"\nburst = 20\n\n[tbank.buckets.instruments_list]\n# Whole-kind listings (Shares, Bonds, Etfs, Futures, Options, Currencies): 15/min. One bucket for all\n# six: the docs give the limit for the group of methods, not per method (not verified on the live API).\nrate = 15\nper = \"60s\"\nburst = 3\n\n[tbank.buckets.stop_orders]\n# Stop orders service (PostStopOrder, CancelStopOrder): 50/min\nrate = 50\nper = \"60s\"\nburst = 10\n\n[tbank.buckets.stop_orders_get]\n# GetStopOrders has its own row in the docs table: 60/min\nrate = 60\nper = \"60s\"\nburst = 10\n\n[tbank.buckets.order_post_async]\n# PostOrderAsync: 600/min\nrate = 600\nper = \"60s\"\nburst = 30\n\n[tbank.buckets.operations_reports]\n# Reports in the operations service (GetBrokerReport, GetDividendsForeignIssuer): 5/min\nrate = 5\nper = \"60s\"\nburst = 1\n\n[tbank.buckets.sandbox]\n# Sandbox service, every method: 200/min\nrate = 200\nper = \"60s\"\nburst = 20\n\n[tbank.buckets.signals]\n# Signals service (GetStrategies, GetSignals): 100/min\nrate = 100\nper = \"60s\"\nburst = 10\n\n[tbank.buckets.default]\n# Anything without a route of its own (a method added to the API later, a stream opened through\n# raw()). 100/min is the number the docs table gives most services; the shared \"global\" bucket still\n# applies on top.\nrate = 100\nper = \"60s\"\nburst = 10\n\n[tbank.route]\n# \"*\" = methods with no route below. Used by raw(), so no call goes out without spending quota.\n\"*\" = [\"global\", \"default\"]\nPostOrder = [\"global\", \"order_post\"]\nCancelOrder = [\"global\", \"order_cancel\"]\nReplaceOrder = [\"global\", \"order_replace\"]\nGetOrders = [\"global\", \"orders_get\"]\nGetOrderState = [\"global\", \"orders_service\"]\nGetOrderBook = [\"global\", \"market_data\"]\nGetLastTrades = [\"global\", \"market_data\"]\nGetCandles = [\"global\", \"market_data\"]\nGetTradingStatus = [\"global\", \"market_data\"]\nSubscribeMarketData = [\"global\", \"market_data_subscribe\"]\nFindInstrument = [\"global\", \"instruments\"]\nGetInstrumentBy = [\"global\", \"instruments\"]\nTradingSchedules = [\"global\", \"instruments\"]\nShares = [\"global\", \"instruments_list\"]\nBonds = [\"global\", \"instruments_list\"]\nEtfs = [\"global\", \"instruments_list\"]\nFutures = [\"global\", \"instruments_list\"]\nOptions = [\"global\", \"instruments_list\"]\nCurrencies = [\"global\", \"instruments_list\"]\nGetPortfolio = [\"global\", \"operations\"]\nGetPositions = [\"global\", \"operations\"]\nGetOperationsByCursor = [\"global\", \"operations\"]\nGetAccounts = [\"global\", \"accounts\"]\n\n# Everything below: the rest of the API, reachable through raw() and the stop order trait.\n# Instruments service (200/min), the listed kinds are 15/min, see instruments_list.\nGetAssets = [\"global\", \"instruments_list\"]\nBondBy = [\"global\", \"instruments\"]\nGetBondCoupons = [\"global\", \"instruments\"]\nGetBondEvents = [\"global\", \"instruments\"]\nCurrencyBy = [\"global\", \"instruments\"]\nEtfBy = [\"global\", \"instruments\"]\nFutureBy = [\"global\", \"instruments\"]\nOptionBy = [\"global\", \"instruments\"]\nOptionsBy = [\"global\", \"instruments\"]\nShareBy = [\"global\", \"instruments\"]\nIndicatives = [\"global\", \"instruments\"]\nGetAccruedInterests = [\"global\", \"instruments\"]\nGetFuturesMargin = [\"global\", \"instruments\"]\nGetDividends = [\"global\", \"instruments\"]\nGetAssetBy = [\"global\", \"instruments\"]\nGetFavorites = [\"global\", \"instruments\"]\nEditFavorites = [\"global\", \"instruments\"]\nCreateFavoriteGroup = [\"global\", \"instruments\"]\nDeleteFavoriteGroup = [\"global\", \"instruments\"]\nGetFavoriteGroups = [\"global\", \"instruments\"]\nGetCountries = [\"global\", \"instruments\"]\nGetBrands = [\"global\", \"instruments\"]\nGetBrandBy = [\"global\", \"instruments\"]\nGetAssetFundamentals = [\"global\", \"instruments\"]\nGetAssetReports = [\"global\", \"instruments\"]\nGetConsensusForecasts = [\"global\", \"instruments\"]\nGetForecastBy = [\"global\", \"instruments\"]\nGetRiskRates = [\"global\", \"instruments\"]\nGetInsiderDeals = [\"global\", \"instruments\"]\nStructuredNoteBy = [\"global\", \"instruments\"]\nStructuredNotes = [\"global\", \"instruments\"]\n# Quotes service unary (600/min)\nGetLastPrices = [\"global\", \"market_data\"]\nGetTradingStatuses = [\"global\", \"market_data\"]\nGetClosePrices = [\"global\", \"market_data\"]\nGetTechAnalysis = [\"global\", \"market_data\"]\nGetMarketValues = [\"global\", \"market_data\"]\n# Operations service (200/min); reports 5/min\nGetOperations = [\"global\", \"operations\"]\nGetWithdrawLimits = [\"global\", \"operations\"]\nGetBrokerReport = [\"global\", \"operations_reports\"]\nGetDividendsForeignIssuer = [\"global\", \"operations_reports\"]\n# Orders service (100/min); PostOrderAsync has its own row\nPostOrderAsync = [\"global\", \"order_post_async\"]\nGetMaxLots = [\"global\", \"orders_service\"]\nGetOrderPrice = [\"global\", \"orders_service\"]\n# Stop orders service (50/min), GetStopOrders 60/min\nPostStopOrder = [\"global\", \"stop_orders\"]\nCancelStopOrder = [\"global\", \"stop_orders\"]\nGetStopOrders = [\"global\", \"stop_orders_get\"]\n# Users service (accounts, 100/min)\nGetMarginAttributes = [\"global\", \"accounts\"]\nGetUserTariff = [\"global\", \"accounts\"]\nGetInfo = [\"global\", \"accounts\"]\nGetBankAccounts = [\"global\", \"accounts\"]\nCurrencyTransfer = [\"global\", \"accounts\"]\n# Signals service (100/min)\nGetStrategies = [\"global\", \"signals\"]\nGetSignals = [\"global\", \"signals\"]\n# Sandbox service (200/min, one row for every method)\nOpenSandboxAccount = [\"global\", \"sandbox\"]\nGetSandboxAccounts = [\"global\", \"sandbox\"]\nCloseSandboxAccount = [\"global\", \"sandbox\"]\nPostSandboxOrder = [\"global\", \"sandbox\"]\nPostSandboxOrderAsync = [\"global\", \"sandbox\"]\nReplaceSandboxOrder = [\"global\", \"sandbox\"]\nGetSandboxOrders = [\"global\", \"sandbox\"]\nCancelSandboxOrder = [\"global\", \"sandbox\"]\nGetSandboxOrderState = [\"global\", \"sandbox\"]\nGetSandboxOrderPrice = [\"global\", \"sandbox\"]\nGetSandboxPositions = [\"global\", \"sandbox\"]\nGetSandboxOperations = [\"global\", \"sandbox\"]\nGetSandboxOperationsByCursor = [\"global\", \"sandbox\"]\nGetSandboxPortfolio = [\"global\", \"sandbox\"]\nSandboxPayIn = [\"global\", \"sandbox\"]\nGetSandboxWithdrawLimits = [\"global\", \"sandbox\"]\nGetSandboxMaxLots = [\"global\", \"sandbox\"]\nPostSandboxStopOrder = [\"global\", \"sandbox\"]\nGetSandboxStopOrders = [\"global\", \"sandbox\"]\nCancelSandboxStopOrder = [\"global\", \"sandbox\"]\n\n[tbank.conns]\n# Stream connections per user: market data 32; orders 16 per stream type; operations 11 per stream type\n# (max_conns below is the tightest of them).\n# 300 simultaneous subscriptions (candles + order books + trades together) inside one market-data stream.\n# The unary calls share one HTTP/2 channel, so there are no dedicated command connections.\nmax_conns = 11\nmax_subs_per_conn = 300\nsupports_multiplex = true\n";
```
The bundled default limits, as documented by T-Bank (the text of `limits.toml`).

Parse it with `limits_from_toml_str`, or call `default_limits`.

### Struct Emitter

```rust
pub struct Emitter { /* private fields */ }
```
Moves events from a connector’s reader task into the consumer’s channel, without ever blocking.

This is the egress half of a connector. Each subscription has a buffer with its
[`Backpressure`] policy. `emit` puts events into the buffers and moves as many as
the channel accepts into it with `try_send`. When the consumer is slow the channel fills and the
buffers take the strain:

PolicyA slow consumer causes
[`Backpressure::Conflate`]only the latest value is kept, older ones are replaced
[`Backpressure::DropNewest`]new events are thrown away once the buffer is full
[`Backpressure::Lossless`]the buffer overflows: an [`Event::Overflow`] notice is queued once, the event is lost, the emitter becomes degraded

Degraded means events were lost that must not be lost. The connector copies the flag
(`is_degraded`) into its command gate, so trading commands fail with
[`brokapi_core::Error::Degraded`] until a successful reconcile calls
`clear_degraded`. Why: after a hole in the order or fill stream the
client’s picture of its orders is wrong, and trading on it could be harmful.

Things to know:

- Events with no subscription (`sub` is `None`) skip the buffers. If the channel is full or
closed they are dropped and counted in `dropped_unrouted`.

- Resync notices ([`Event::Resync`], made by the protocol cores after a sequence gap or a
reconnect) travel like any other event of their subscription. The emitter does not create
them and does not treat them specially.

- Each envelope is stamped with the time it is built, which is when it leaves the buffer, not
when the event was parsed. For a slow consumer the stamp is later than the arrival.

- The [`Event::Overflow`] notice has its own queue, which is sent before the buffers. So it can
reach the consumer before older events of the same subscription that are still waiting. Treat
it as “events of this subscription are missing, re-read the state”, not as an ordered marker.

- Events for a subscription that was not `register`ed are discarded.

- All methods take `&self` and are safe to call from several tasks. Share the emitter as an
`Arc`.

- `pub fn new(source: Arc<str>, out: Sender<Envelope>) -> Arc<Emitter>`
  Creates an emitter that writes into `out`.

  `source` is the connector label from its config (for example `"bcs-ud"`). It is put into
  every [`Envelope`] so the consumer can tell connectors apart.

- `pub fn source(&self) -> &Arc<str>`
  The `source` label given to `new`.

- `pub fn register( &self, sub: SubscriptionId, instrument: Option<InstrumentId>, policy: Backpressure, )`
  Starts buffering a subscription with the given policy.

  Call it before the first event of `sub` can arrive. `instrument` is the subscription’s
  instrument, or `None` for account-level streams such as order state or portfolio.

- `pub fn unregister(&self, sub: SubscriptionId)`
  Stops buffering a subscription. Events still waiting for it are discarded.

  An [`Event::Overflow`] notice already queued for the subscription is still sent.

- `pub fn is_degraded(&self) -> bool`
  Whether the connector lost events and no reconcile has cleared it yet: a `Lossless` subscription
  overflowed, or a stream of orders or fills was cut and reopened ([`Event::Reconnected`]).

- `pub fn mark_degraded(&self)`
  Enters the degraded state by hand: the owner knows that events were lost for a reason the emitter
  cannot see (a message that could not be processed). The trading gate closes until the next
  successful reconcile, like after an overflow. Send an [`Event::Overflow`] for the subscription
  to tell the consumer.

- `pub fn clear_degraded(&self)`
  Leaves the degraded state. Call it from a successful reconcile.

  Overflowed `Lossless` buffers accept events again and a new overflow sends a new
  [`Event::Overflow`]. Events lost during the overflow are not recovered, the reconcile is
  what makes up for them. Events already waiting in the buffers are still delivered.

- `pub fn has_backlog(&self) -> bool`
  Whether some events or notices are waiting because the channel was full.

  While it is `true`, call `flush` again later. `emit` also
  retries on every call.

- `pub fn dropped_unrouted(&self) -> u64`
  How many events with no subscription were dropped because the channel was full or closed.

- `pub fn emit(&self, events: Vec<CoreEvent>)`
  Buffers events and moves what the channel accepts. Never blocks.

  Events with a subscription go through that subscription’s buffer and policy. A `Lossless`
  overflow queues one [`Event::Overflow`] (with `lost_from` set to the current wall clock)
  per subscription and marks the emitter degraded. Events with no subscription are sent
  straight to the channel or dropped, see the type docs. Nothing is returned: the outcome is
  visible through `is_degraded`, `has_backlog` and
  `dropped_unrouted`.

- `pub fn flush(&self)`
  Retries delivery of buffered events and notices. Never blocks.

  Call it periodically while `has_backlog` is `true`, for example when
  the consumer has read from the channel.

### Enum LState

```rust
pub enum LState {
    Pending,
    Active,
    Failed,
}
```
Where a logical subscription is in its life, see `MdCore::state`.

- `Pending`
  Asked for, not confirmed yet.

- `Active`
  Confirmed by the broker. Data flows.

- `Failed`
  Refused by the broker. A transient refusal is retried after `RETRY_AFTER`.

### Struct LastError

```rust
pub struct LastError(/* private fields */);
```
The last reason a stream closed, for error messages of calls that wait on a stream.

- `pub fn set(&self, msg: impl Into<String>)`
  Remembers the reason the stream closed, replacing an earlier one.

- `pub fn clear(&self)`
  Forgets the reason, after the stream reopened.

- `pub fn get(&self) -> Option<String>`
  The last reason, if the stream is down now.

  Examples

### Struct LotBook

```rust
pub struct LotBook { /* private fields */ }
```
The lot sizes of the instruments this connector has met. Cheap to share behind an `Arc`.

An instrument is remembered under every name a message can call it: the uid, the figi and the
`TICKER_CLASSCODE` form.

- `pub fn new() -> Self`
  An empty book.

- `pub fn put( &self, uid: &str, figi: &str, ticker: &str, class_code: &str, lot: i64, )`
  Remembers the lot of an instrument under its uid, figi and ticker with class code. A lot below 1
  is ignored.

- `pub fn put_instrument(&self, asked: &InstrumentId, i: &Instrument)`
  Remembers the lot from a directory record, under the name the caller asked with too.

- `pub fn put_id(&self, id: &InstrumentId, lot: i64)`
  Remembers the lot under the names of an [`InstrumentId`].

- `pub fn get_parts( &self, uid: &str, figi: &str, ticker: &str, class_code: &str, ) -> Option<i64>`
  The lot of an instrument by the names a message carries, if it is known.

- `pub fn get(&self, id: &InstrumentId) -> Option<i64>`
  The lot of an [`InstrumentId`], if it is known.

### Constant MAX_DEPTH

```rust
pub const MAX_DEPTH: u32 = 50;
```
The deepest order book T-Bank serves.

### Struct MarketDataConfig

```rust
pub struct MarketDataConfig {
    pub tbank: TbankConfig,
    pub source: Arc<str>,
    pub ping_interval: Duration,
}
```
Market data connector config. Not tied to an account: one per broker (token) is enough.

Build it with `MarketDataConfig::new` and change the public fields if needed.

- `tbank: TbankConfig`
  Endpoint, token and timeouts of the API. `tbank.account_id` is ignored by market data.

- `source: Arc<str>`
  Envelope source label, e.g. `"tbank-md"`.

- `ping_interval: Duration`
  Server ping period on the stream. T-Bank accepts 5 s to 180 s: a value outside that range is
  brought into it, both for the request and for the silence limit. A stream silent for three
  periods is treated as dead and reconnected. Default 30 s.

- `pub fn new(tbank: TbankConfig, source: impl Into<Arc<str>>) -> Self`
  A config with the default ping period (30 s).

  `source` labels every `Envelope` this connector emits, so that you
  can tell connectors apart when they write into one channel.

  Examples

### Struct MarketDataConnector

```rust
pub struct MarketDataConnector { /* private fields */ }
```
T-Bank market data connector: order books, trades, quotes, candles and the instrument directory.

One connector per token is enough, it is not tied to an account. It implements
[`OrderBookSource`], [`MarketDataSource`] and [`InstrumentSource`]. Requests return their answer
directly. Subscriptions deliver [`Envelope`]s into the channel given to `start`.

How T-Bank differs from the other brokers:

- All market data streams run over ONE bidirectional gRPC stream, which the connector keeps open
and reopens with a growing delay after a break. After a reopen the subscriptions are sent again
and an order book subscription gets an `Event::Resync`.

- Every order book message is a full snapshot, there are no increments.

- Quotes are the best level of the order book. T-Bank has no separate quote stream.

- The broker counts the levels of the order book, trades and candle volumes in LOTS. The connector multiplies by the lot
size, so the events count in units of the instrument, as at every broker (the first read or subscription on an
instrument looks the lot size up, one `GetInstrumentBy`). Prices are per one instrument.

- Depths the broker accepts are 1, 10, 20, 30, 40 and 50. Other depths are rounded up on the wire
and the events are cut to the depth you asked for.

- `pub async fn start( cfg: MarketDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Builds the channel and starts the stream task.

  The task opens the market data stream in the background at once and reopens it after a break
  (delay from 500 ms doubling to 30 s). `start` does not wait for it and does not fail when the
  broker cannot be reached: the first request or subscription reports that. A subscription
  waits for the broker to confirm it, for at most `request_timeout`.

  `limiter` should be the one limiter of the whole user, shared with every other connector
  (see `crate::limits`). Events go to `out`.

  Errors

  [`Error::Config`] when the token or app name is not a valid header value, or the endpoint is
  not a valid URL. Nothing was started in that case.

  Examples

- `pub fn raw(&self) -> TbankRaw`
  Any other method of the T-Bank API with this connector’s token, endpoint and rate limiter.
  See `TbankRaw`.

  The raw layer is not blocked by the connector’s shutdown gate and does not know the
  connector’s subscriptions. Its calls spend quota from the same limiter as the connector’s own.

  Examples

- `pub fn caps(&self) -> MarketDataCaps`
  What this connector can do.

  The order book is at most 50 levels deep ([`MarketDataCaps::max_order_book_depth`]). Every stream
  message is a full snapshot (`incremental_order_book` is `false`). Quotes are the best level of
  the book, since T-Bank has no separate quote stream. Candles stream as they form.

  Examples

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Stops the connector and its stream task.

  With [`ShutdownMode::Graceful`] the connector first unsubscribes every wire subscription and
  waits for the broker to confirm, until the deadline. Subscriptions not confirmed by then count
  as `unsubscribe_failed`. It also gives the event backlog the same deadline to drain into your
  channel. With [`ShutdownMode::Immediate`] it just stops, and the report counts nothing.

  Market data has no commands, so `inflight_abandoned` is always empty. The method consumes the
  connector, so no further call is possible. Closing the stream is the only cleanup the broker
  needs.

  Examples

### Struct MdCore

```rust
pub struct MdCore { /* private fields */ }
```
Sans-io core of the market data stream.

Logical subscriptions (what the client asked for) are mapped to wire subscriptions (what T-Bank
is told). The core queues the requests (`poll_request`), turns responses
into events, and reports confirmations (`take_updates`). It implements
[`BrokerCore`] with `MdFrame` as the frame type. See the module docs for an
example.

- `pub fn new(ping_interval: Duration) -> Self`
  `ping_interval` is requested from the server (5000..180000 ms are valid).

- `pub fn poll_request(&mut self) -> Option<MarketDataRequest>`
  Next request to write to the stream.

- `pub fn take_updates(&mut self) -> Vec<SubUpdate>`
  Takes the confirmations and refusals that happened since the last call.

- `pub fn desired(&self) -> &SubscriptionSet`
  The set of subscriptions the consumer wants now, as last given to `apply`.

- `pub fn state(&self, sub: SubscriptionId) -> Option<LState>`
  The state of a logical subscription, or `None` if the core does not know the id.

- `pub fn wire_count(&self) -> usize`
  Wire subscriptions currently held at the broker (or being set up).

- `pub fn unsubscribe_progress(&self) -> (usize, usize, usize)`
  `(confirmed, refused, still waiting)` unsubscribe confirmations (graceful shutdown).

- `pub fn give_up_unsubscribing(&mut self)`
  Counts what is still unconfirmed as failed (shutdown deadline passed).

### Enum MdFrame

```rust
pub enum MdFrame {
    Connected,
    Response(MarketDataResponse),
}
```
One inbound item for `MdCore`: what the runtime saw on the market data stream.

- `Connected`
  A (new) stream is open: every subscription is sent again.

- `Response(MarketDataResponse)`
  A message the server sent on the stream: data, a subscription answer or a ping.

### Struct OrderParts

```rust
pub struct OrderParts {
    pub exchange_id: String,
    pub request_id: String,
    pub status: OrderExecutionReportStatus,
    pub lots_requested: i64,
    pub lots_executed: i64,
    pub lot_size: i64,
    pub unit_price: Option<Price>,
    pub direction: OrderDirection,
    pub order_type: OrderType,
    pub instrument: InstrumentId,
    pub reason: String,
    pub broker_ts: Option<Timestamp>,
}
```
What every order message (unary state, placement response, stream update) has in common.

- `exchange_id: String`
  The exchange order id (`order_id`). Empty when the message has none.

- `request_id: String`
  The `order_request_id`: the idempotency key we sent, as text. Empty for an order placed
  elsewhere.

- `status: OrderExecutionReportStatus`
  The order status as the broker reports it.

- `lots_requested: i64`
  Lots requested.

- `lots_executed: i64`
  Lots executed so far.

- `lot_size: i64`
  Units of the instrument in one lot. 1 until the caller sets it (`OrderParts::with_lot`): the
  message of a unary call does not carry it, the order stream message does (`lot_size`).

- `unit_price: Option<Price>`
  Price of one instrument, if the message carries a non-zero one.

- `direction: OrderDirection`
  Buy or sell, as the broker reports it.

- `order_type: OrderType`
  Limit, market or best price, as the broker reports it.

- `instrument: InstrumentId`
  The instrument, built from the uid, figi or ticker the message carries.

- `reason: String`
  The broker’s text for a refusal, if any (the placement response `message`, or the cause of a
  stream status).

- `broker_ts: Option<Timestamp>`
  The broker’s time of the message, if it gave one.

- `pub fn with_lot(self, lot_size: i64) -> Self`
  The same parts with the lot size of the instrument set. A lot below 1 is read as 1.

- `pub fn qty_requested(&self) -> Qty`
  Quantity requested, in units of the instrument.

- `pub fn qty_executed(&self) -> Qty`
  Quantity executed so far, in units of the instrument.

- `pub fn qty_left(&self) -> Qty`
  Quantity not executed yet, in units of the instrument (never below zero).

- `pub fn cid(&self) -> ClientOrderId`
  Our cid, or a stable stand-in for an order placed elsewhere.

- `pub fn is_ours(&self) -> bool`
  `true` when `request_id` is a cid of ours, so the order was placed by this library.

### Constant RETRY_AFTER

```rust
pub const RETRY_AFTER: Duration;
```
Pause before a subscription refused with a transient status (internal error, too many requests)
is sent again.

### Constant STOP_ORDERS_RESPONSE_LIMIT

```rust
pub const STOP_ORDERS_RESPONSE_LIMIT: &str = "stop_orders_response_limit";
```
`Error::Rejected` code for the broker’s answer 30261: the list of stop orders is too long for one reply.

### Enum StreamKind

```rust
pub enum StreamKind {
    OrderState,
    Trades,
    Portfolio,
    Positions,
}
```
The user-data streams the runtime keeps open.

- `OrderState`
  `OrdersStreamService/OrderStateStream`: state changes of own orders.

- `Trades`
  `OrdersStreamService/TradesStream`: own trades.

- `Portfolio`
  `OperationsStreamService/PortfolioStream`: the portfolio with prices.

- `Positions`
  `OperationsStreamService/PositionsStream`: cash and securities balances.

### Struct SubIdGen

```rust
pub struct SubIdGen(/* private fields */);
```
Hands out subscription ids, each one larger than the last.

A connector keeps one generator. The user data connector starts at 1000 and the market data
connector at 0, so ids of the two kinds do not collide in a log.

- `pub fn new(start: u64) -> Self`
  A generator whose first id is `start + 1`.

- `pub fn next(&self) -> SubscriptionId`
  The next id. Safe to call from several tasks at once.

### Struct SubUpdate

```rust
pub struct SubUpdate {
    pub sub: SubscriptionId,
    pub result: Result<(), String>,
}
```
A logical subscription changed state: confirmed or refused.

The connector takes these from `MdCore::take_updates` and wakes the caller that waits in
`subscribe_*`.

- `sub: SubscriptionId`
  The subscription that changed.

- `result: Result<(), String>`
  `Ok` when the broker confirmed it. `Err` holds the broker’s subscription status name
  (for example `SUBSCRIPTION_STATUS_INSTRUMENT_NOT_FOUND`).

### Struct TbankCidCodec

```rust
pub struct TbankCidCodec;
```
The client order id codec of T-Bank: the cid is sent as a hyphenated UUID.

A hyphenated UUID is exactly 36 characters, which is the limit of `PostOrderRequest.order_id`, so
the cid fits without loss and comes back unchanged in `order_request_id`. The connectors use this
codec for you. Use it yourself when you read broker messages outside the connector (for example
through `TbankRaw`) and want to know whether an order is yours.

### Struct TbankRaw

```rust
pub struct TbankRaw { /* private fields */ }
```
Direct access to any T-Bank API method, made by `MarketDataConnector::raw` or
`UserDataConnector::raw`. See the module docs. Cheap to clone.

Reads go through `call`. Anything that changes the account goes through
`call_command`, which never lets a lost answer look like a refusal.

- `pub fn account_id(&self) -> Option<&str>`
  Account of the connector this came from (user data connector only).

  `None` for a market data connector, which has no account. The raw layer does not add the
  account to your requests: put it into the request yourself.

  Examples

- `pub fn timeout(&self) -> Duration`
  The deadline applied to every call: the `request_timeout` of the connector’s config (30 s by
  default).

  Examples

- `pub async fn call<T, F, Fut>(&self, method: &str, f: F) -> Result<T, Error>where F: FnOnce(TbankClient) -> Fut, Fut: Future<Output = Result<Response<T>, Status>>,`
  Reads: takes quota for `method`, runs the call, maps errors.

  For a server-streaming method the result type is `tonic::Streaming<_>`: the quota covers
  opening the stream, the stream is then yours to read.

  Errors

  - [`Error::RateLimited`] when the limiter has no quota for `method`. The request is not sent.
  Wait `retry_after` and call again.

  - [`Error::Transport`] on a timeout or a network status. A read may be repeated.

  - [`Error::Rejected`] when the broker refuses the request (`InvalidArgument`,
  `NotFound`, which is the code `not_found`, `PermissionDenied`, …).

  - [`Error::Config`] when the token is not accepted (`Unauthenticated`).

  Examples

- `pub async fn call_command<T, F, Fut>( &self, method: &str, f: F, ) -> Result<T, Error>where F: FnOnce(TbankClient) -> Fut, Fut: Future<Output = Result<Response<T>, Status>>,`
  Commands: like `call`, but a timeout, a break or a server fault is
  `Error::Unknown`, because the broker may have executed the command. Never retried.

  Errors

  - [`Error::RateLimited`] when the limiter has no quota. Nothing was sent.

  - [`Error::Rejected`] when the broker refuses (`InvalidArgument`, `NotFound`,
  `PermissionDenied`, `FailedPrecondition`, …). The broker did not act.

  - [`Error::Config`] when the token is not accepted. The broker did not act.

  - [`Error::Unknown`] on a timeout, a broken connection or a server fault (`Unavailable`,
  `Internal`, …). The command may or may not have been done. Do not repeat it. Ask the
  broker for the state of the order or account first.

  Examples

### Struct UdCore

```rust
pub struct UdCore { /* private fields */ }
```
Sans-io core of the user data of ONE account: order state machine, own trades, portfolio.

It links our cid to the exchange order id and the instrument, drops every message that belongs to
another account, and turns the rest into events. The connector calls the command methods
(`begin_place`, `place_response`, …) around its
requests, and feeds stream messages through [`BrokerCore::on_frame`] as `UdFrame`s.

- `pub fn new(account_id: impl Into<String>) -> Self`
  A core for the account `account_id`. Messages with another account id are dropped.

- `pub fn lots(&self) -> &Arc<LotBook>`
  The lot sizes this core knows. The connector fills the book before it feeds a message of an
  instrument it has not met (the messages count in lots, the events in units).

- `pub fn account_id(&self) -> &str`
  The account this core serves.

- `pub fn orders_mut(&mut self) -> &mut OrderRuntime`
  The order runtime, for the shutdown sequence.

- `pub fn order_state(&self, cid: ClientOrderId) -> Option<&OrderState>`
  The state this core holds for `cid`, or `None` if it never saw that cid.

- `pub fn take_forgotten(&mut self) -> Vec<ClientOrderId> ⓘ`
  Takes the cids the core forgot since the last call (orders finished long ago, see the memory window
  of [`OrderRuntime`]). The connector drops what else it keeps for them.

- `pub fn take_updates(&mut self) -> Vec<SubUpdate>`
  Takes the subscription confirmations and refusals since the last call.

- `pub fn take_reconnected(&mut self) -> Vec<StreamKind>`
  Streams that were reopened after a break (not the first open). The runtime refreshes order
  state from `GetOrders` for `OrderState`.

- `pub fn add_sub(&mut self, id: SubscriptionId, kind: UdSubKind)`
  Registers a subscription of `kind` under `id`. A later one of the same kind replaces it.

- `pub fn remove_sub(&mut self, kind: UdSubKind)`
  Forgets the subscription of `kind`, if any.

- `pub fn sub(&self, kind: UdSubKind) -> Option<SubscriptionId>`
  The id of the active subscription of `kind`, if any.

- `pub fn learn(&mut self, id: &InstrumentId)`
  Teach the core an instrument (uid/figi -> ticker@class), e.g. the one an order is placed on.

- `pub fn resolve( &self, uid: &str, figi: &str, ticker: &str, class_code: &str, ) -> InstrumentId`
  Instrument id from message fields; the ticker is filled from earlier messages when missing.

- `pub fn begin_place( &mut self, req: &PlaceOrderRequest, now: Instant, ) -> Result<(), Error>`
  Registers a placement before the request is sent. At most one placement per cid, ever
  (no automatic retry of `place`).

- `pub fn place_response( &mut self, cid: ClientOrderId, resp: &PostOrderResponse, now: Instant, out: &mut EventSink, )`
  The placement response arrived.

- `pub fn place_rejected( &mut self, cid: ClientOrderId, reason: String, now: Instant, out: &mut EventSink, )`
  The broker refused the placement: nothing exists at the broker.

- `pub fn place_unknown( &mut self, cid: ClientOrderId, now: Instant, out: &mut EventSink, )`
  Timeout or break during `place`: the outcome is unknown, never `Rejected`.

- `pub fn cancel_ok( &mut self, cid: ClientOrderId, now: Instant, out: &mut EventSink, )`
  The broker accepted a cancel.

- `pub fn begin_replace( &mut self, new_cid: ClientOrderId, original: ClientOrderId, now: Instant, ) -> Result<(), Error>`
  Registers a replace before the request is sent: the new cid is placed at most once, and it
  inherits the instrument and side of the original when we know them.

- `pub fn replace_response( &mut self, new_cid: ClientOrderId, resp: &PostOrderResponse, now: Instant, out: &mut EventSink, )`
  The broker accepted a replace: the new cid is a new live order.

- `pub fn replace_failed( &mut self, new_cid: ClientOrderId, unknown: bool, reason: String, now: Instant, out: &mut EventSink, )`
  The replace failed with a definite refusal or an unknown outcome.

- `pub fn sweep_timeouts(&mut self, now: Instant, out: &mut EventSink)`
  Moves the placements that have had no answer for the command timeout to `Unknown`, one event each.
  The connector calls it on a timer. It is the safety net: a command normally ends by itself, within
  the request timeout.

- `pub fn set_command_timeout(&mut self, timeout: Duration)`
  Sets how long a placement may wait for its answer before `sweep_timeouts`
  gives it up. Keep it above the request timeout of the connector.

- `pub fn abandon_inflight( &mut self, now: Instant, out: &mut EventSink, ) -> Vec<ClientOrderId> ⓘ`
  In-flight placements become `Unknown` at shutdown, one event each.

- `pub fn exchange_id(&self, cid: ClientOrderId) -> Option<&str>`
  Exchange order id of a cid we know (placed here or seen on the stream).

- `pub fn instrument_of(&self, cid: ClientOrderId) -> Option<InstrumentId>`
  Instrument of an order we know, for ordering commands per instrument.

- `pub fn order_basis(&self, cid: ClientOrderId) -> Option<(Qty, Option<Price>)>`
  Lots and price of an order we know, for building a replace without a round trip.

- `pub fn note_order(&mut self, parts: &OrderParts)`
  Learns ids from a `GetOrderState` / `GetOrders` answer.

### Enum UdFrame

```rust
pub enum UdFrame {
    Connected(StreamKind),
    OrderState(OrderStateStreamResponse),
    Trades(TradesStreamResponse),
    Portfolio(PortfolioStreamResponse),
    Positions(PositionsStreamResponse),
    ActiveOrders(GetOrdersResponse),
    StreamFailed(StreamKind, String),
}
```
One inbound item for `UdCore`: a message from one of the user data streams.

- `Connected(StreamKind)`
  A stream was (re)opened.

- `OrderState(OrderStateStreamResponse)`
  A message of `OrderStateStream`.

- `Trades(TradesStreamResponse)`
  A message of `TradesStream`.

- `Portfolio(PortfolioStreamResponse)`
  A message of `PortfolioStream`.

- `Positions(PositionsStreamResponse)`
  A message of `PositionsStream`.

- `ActiveOrders(GetOrdersResponse)`
  `GetOrders` result used to refresh order state after a stream reconnect.

- `StreamFailed(StreamKind, String)`
  The broker refused to open the stream for good (a rejected token, no permission, the limit of open
  streams): the subscription fed by it fails with this reason, and the stream is not reopened.

### Enum UdSubKind

```rust
pub enum UdSubKind {
    OrderState,
    Executions,
    Portfolio,
}
```
User-data subscriptions. `brokapi-sansio`’s `SubscriptionKind` has no executions variant yet, so
this core takes them through `UdCore::add_sub`.

- `OrderState`
  State changes of own orders. Fed by `StreamKind::OrderState`.

- `Executions`
  Own trades. Fed by `StreamKind::Trades`.

- `Portfolio`
  Portfolio, positions and cash. Fed by `StreamKind::Portfolio` and `StreamKind::Positions`.

### Struct UserDataConfig

```rust
pub struct UserDataConfig {
    pub tbank: TbankConfig,
    pub source: Arc<str>,
    pub order_state_buffer: usize,
    pub ping_interval: Duration,
    pub price_grid: PriceGrid,
}
```
User data connector config. One connector = one account: `tbank.account_id`.
With `None` the connector uses the token’s only open account and refuses to start when there are several.

Several accounts of one token need several connectors, each with its own config that differs in
`tbank.account_id` and in `source`. Share one rate limiter between them.

- `tbank: TbankConfig`
  Endpoint, token, account and timeouts of the API. Set `account_id` with
  [`TbankConfig::with_account`] when the token sees more than one account.

- `source: Arc<str>`
  Envelope source label, e.g. `"tbank-ud-main"`. Give every account its own.

- `order_state_buffer: usize`
  Lossless buffer capacity for the order state and executions subscriptions.

  Own order states and trades are never dropped. If the consumer is so slow that the buffer
  fills up, an overflow event is sent and the trading gate closes until the next successful
  reconcile. Default 1024.

- `ping_interval: Duration`
  Server ping period on the user data streams. A value outside 5 s to 120 s is brought into
  that range, both for the request and for the silence limit. A stream silent for three periods
  is treated as dead and reconnected. Default 30 s.

- `price_grid: PriceGrid`
  Price steps the connector checks order prices against (default: empty, nothing is checked).
  Fill it with [`PriceGrid::learn`] from the trading parameters of the instruments you trade. A
  price off the step is then refused with `Rejected` (`price_off_step`) before anything is sent.

- `pub fn new(tbank: TbankConfig, source: impl Into<Arc<str>>) -> Self`
  A config with the default buffer (1024 events) and ping period (30 s).

  `source` labels every `Envelope` of this connector. Give each account
  its own label.

  Examples

### Struct UserDataConnector

```rust
pub struct UserDataConnector { /* private fields */ }
```
T-Bank user data connector: orders, own trades, portfolio and stop orders of ONE account.

It implements [`OrderExecutor`], [`ExecutionSource`], [`PortfolioSource`] and
[`StopOrderExecutor`]. Commands return their answer directly. Order states, own trades and
portfolio changes arrive as [`Envelope`]s in the channel given to `start`, after
you subscribe.

**Rules that protect your money**

- The gate. Trading commands return [`Error::NotReconciled`] until [`OrderExecutor::reconcile`]
has succeeded once. Call it right after `start` with the cids you have on record (an empty
slice if none).

- A place is never repeated. If a `place` gets no answer (timeout, broken connection, server
fault) the result is [`Error::Unknown`] and the library does NOT send it again, because a retry
could create a second order. A `cid` is used for one `place` at most: sending it again is
refused locally as `duplicate_place`. Look at the order with
`order_state`, and use a NEW `cid` for a new attempt.

- One account. Messages for another account are dropped. For several accounts of one token
start one connector per account and share ONE rate limiter between them.

- Units, not lots. Quantities are in units of the instrument (shares, bonds), as at every broker. T-Bank counts
in lots: the connector converts both ways with the lot size of the instrument (read once, from the directory). An
order that is not a whole number of lots is refused with `invalid_qty` before it is sent. Prices are per one instrument.

**Several accounts**

One token can see several accounts. Start one connector per account. Give each its own `source`
and share ONE limiter, because T-Bank counts its limits per user.

- `pub fn in_flight_orders(&self) -> usize`
  How many commands are in flight: sent, and the answer has not been taken yet. Zero when all of them
  are settled. A `place` runs to its end even if the caller drops its future, so this number goes back
  to zero by itself. For health checks and tests.

  Examples

- `pub async fn start( cfg: UserDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Resolves the account (`cfg.tbank.account_id`, or the token’s only open account) and starts.
  Commands are refused with `NotReconciled` until [`OrderExecutor::reconcile`] succeeds.

  With an `account_id` in the config nothing is sent at start. Without one the connector calls
  `GetAccounts` once, and takes the only open account. Streams are opened later, when you
  subscribe. `limiter` should be the one limiter of the whole user (see `crate::limits`).

  Errors

  - [`Error::Config`] when the token or endpoint is invalid, when no `account_id` is set and the
  token has no open account, when it has several (set `tbank.account_id`), or when the
  broker does not accept the token.

  - [`Error::Transport`] when the `GetAccounts` call fails on the network (only without an
  `account_id`), and [`Error::RateLimited`] when it is refused for rate.

  Examples

- `pub fn account_id(&self) -> &str`
  The brokerage account this connector works on: the configured one, or the token’s only open
  account.

  Examples

- `pub fn raw(&self) -> TbankRaw`
  Any other method of the T-Bank API with this connector’s token, endpoint and rate limiter.
  `account_id()` of the result is this connector’s account. See `TbankRaw`.

  The raw layer does not know the connector’s order registry: orders placed through it are not
  tracked (no events, no `reconcile`). Use the trait methods for orders. Use `call_command`
  for anything that changes the account, so a lost answer is [`Error::Unknown`].

  Examples

- `pub fn price_grid(&self) -> &PriceGrid`
  The price steps this connector checks order prices against. Fill it from
  `TradingParams` (`grid.learn(&params)`) to have a price off the
  step refused before anything is sent. See [`PriceGrid`].

- `pub fn caps(&self) -> UserDataCaps`
  What this connector can do.

  `place_idempotency` is `LabelOnly` on purpose: `order_id` is documented as an idempotency key
  (“для целей идемпотентности”; error 30057 mentions a duplicate with a found report), but
  nobody has measured the broker’s behavior on a repeated key, and the library requires `LabelOnly`
  until it is confirmed on the sandbox (a live run). The library never repeats a
  `place` itself regardless.

  Replace and stop orders are supported, and own trades are streamed. There is no websocket RPC
  and no ordered command channel: commands are plain gRPC calls, serialized per instrument by
  the connector. `sandbox` is the `sandbox` flag of the config.

  Examples

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Stops the connector. Orders already at the exchange are NOT touched.

  The command gate closes first. Placements still in flight are abandoned locally: each becomes
  `Unknown` with one event, and its `cid` is listed in `inflight_abandoned`. Look at those orders
  with a new connector (`reconcile`, `order_state`) before you place anything again.

  With [`ShutdownMode::Graceful`] the open server streams are closed (the broker has no
  unsubscribe message, ending the call is the way) and `unsubscribed` counts these streams (a
  portfolio subscription uses two). The event backlog gets until the deadline to drain into your
  channel. With [`ShutdownMode::Immediate`] nothing is waited for and `unsubscribed` is 0.
  `unsubscribe_failed` is always 0 here. The method consumes the connector.

  Examples

### Constant VALID_DEPTHS

```rust
pub const VALID_DEPTHS: [u32; 6];
```
Order book depths T-Bank accepts (`SUBSCRIPTION_STATUS_DEPTH_IS_INVALID`: 1, 10, 20, 30, 40, 50).

### Function availability

```rust
pub fn availability(s: SecurityTradingStatus) -> TradingAvailability
```
Broker trading status to [`TradingAvailability`].

Normal trading and open session are `Open`. A break is `Break`. Not available for trading and a
closed session are `Closed`. Opening and closing periods and auctions are `Unknown`, because
orders are possible there only with restrictions that this mapping does not model.

### Function backoff

```rust
pub fn backoff(attempt: u32) -> Duration
```
Delay before reconnect attempt `n` (0-based): 500 ms doubling to 30 s. The same range at BCS and
Finam: none of the three brokers documents a reason for a different one.

### Function balances

```rust
pub fn balances(
    money: &[MoneyValue],
    blocked: &[MoneyValue],
    now: Instant,
) -> Vec<Balance>
```
Cash lives in `GetPositions.money` / `blocked`; currency instruments in `GetPortfolio` repeat it
and are left out of `positions`.

### Function balances_from_positions

```rust
pub fn balances_from_positions(
    p: &PositionsResponse,
    now: Instant,
) -> Vec<Balance>
```
Cash of a `GetPositions` answer: money and blocked money by currency.

### Function balances_from_stream

```rust
pub fn balances_from_stream(
    money: &[PositionsMoney],
    now: Instant,
) -> Vec<Balance>
```
Cash of a `PositionsStream` message: available and blocked money by currency.

### Function book_snapshot

```rust
pub fn book_snapshot(
    instrument: &InstrumentId,
    bids: &[Order],
    asks: &[Order],
    depth: usize,
    broker_ts: Option<&Timestamp>,
    now: Instant,
) -> OrderBook
```
An order book snapshot from the two sides of a broker message, cut to `depth` levels.

Quantities are in lots, as the broker sends them (the connector multiplies by the lot size). The time is the broker’s `orderbook_ts` when given.

### Function broker_code

```rust
pub fn broker_code(status: &Status) -> Option<u32>
```
The five-digit T-Invest error code of a status (`80007`, `30261`), if the broker sent one.

T-Bank puts the number in the status message and the text in the `message` header. The number is
read from the start of either one, so both layouts work.

### Function build_post

```rust
pub fn build_post(
    account: &str,
    req: &PlaceStopOrderRequest,
    lots: i64,
) -> Result<PostStopOrderRequest, Error>
```
Builds the `PostStopOrder` request for `account` from a domain request. `lots` is the quantity in
lots (the request is in units, the caller converts with the lot size of the instrument).

The `cid` goes to `order_id`, the idempotency key. The request is checked with `validate`
first, so nothing invalid is built.

**Errors**

[`Error::Rejected`] with code `invalid_price`, `missing_limit_price` or
`unsupported_expiry`, as `validate` lists.

### Function candle_interval

```rust
pub fn candle_interval(tf: Timeframe) -> CandleInterval
```
Timeframe to the interval name used when requesting historic candles.

### Function command

```rust
pub async fn command<T>(
    timeout: Duration,
    fut: impl Future<Output = Result<Response<T>, Status>>,
) -> CommandOutcome<T>
```
Runs a trading command (place, cancel, replace) under a deadline.

Waits for `fut` at most `timeout`. Statuses that prove the broker did not act become
`CommandOutcome::Refused` (see `map_command_status`). A timeout, a broken connection or a
server fault is `CommandOutcome::Unknown`: the broker may have acted. The caller must never
repeat the command after `Unknown`, because a repeated place could create a second order.

### Function config

```rust
pub fn config(msg: impl Into<String>) -> Error
```
Builds a [`Error::Config`] with a text message.

### Function default_limits

```rust
pub fn default_limits() -> BrokerLimits
```
The bundled default limits of T-Bank, ready for `BrokerLimiter::new`.

Every unary method of the API has a route (placing and cancelling orders have buckets of their
own). A method name without a route spends the `"*"` default route, so no call is free.

**Panics**

Never in practice: the bundled file is checked by this crate’s tests.

### Function direction

```rust
pub fn direction(s: Side) -> OrderDirection
```
[`Side`] to broker direction.

### Function event_instrument

```rust
pub fn event_instrument(ev: &Event) -> Option<&InstrumentId>
```
The instrument a market data event is about, if it has one.

### Function fatal_open

```rust
pub fn fatal_open(status: &Status) -> Option<String>
```
Whether the refusal to open a stream is final: a repeat will not help, so the supervisor stops and
reports it. `Some(reason)` for a rejected token (`Unauthenticated`), no permission
(`PermissionDenied`) and the limit of open streams (80001, `streams_limit`). Everything else (the
network, `Unavailable`, a rate limit, the lifetime limit 80005) is worth another try.

The status is read with `map_status`, as every other answer of the broker.

### Function from_bond

```rust
pub fn from_bond(i: &Bond) -> Instrument
```
A bond record of the `Bonds` listing to [`Instrument`] of kind `Bond`.

### Function from_currency

```rust
pub fn from_currency(i: &Currency) -> Instrument
```
A currency record of the `Currencies` listing to [`Instrument`] of kind `Currency`.

### Function from_etf

```rust
pub fn from_etf(i: &Etf) -> Instrument
```
An ETF record of the `Etfs` listing to [`Instrument`] of kind `Etf`.

### Function from_future

```rust
pub fn from_future(i: &Future) -> Instrument
```
A futures record of the `Futures` listing to [`Instrument`] of kind `Future`. It has no ISIN.

### Function from_operation

```rust
pub fn from_operation(op: &OperationItem, now: Instant) -> Vec<Execution>
```
History from `GetOperationsByCursor`: BUY/SELL operations, one `Execution` per trade.
`broker_order_id` is the operation id (the history has no order id), `cid` is `None`, the
operation commission is spread over its trades in proportion to quantity. Quantities are in
pieces here, unlike the lots of the live stream.

### Function from_option

```rust
pub fn from_option(i: &Option) -> Instrument
```
An option record of the `Options` listing to [`Instrument`] of kind `Option`. It has no ISIN and no figi.

### Function from_order_trades

```rust
pub fn from_order_trades(
    t: &OrderTrades,
    instrument: InstrumentId,
    cid: Option<ClientOrderId>,
    now: Instant,
) -> Vec<Execution>
```
One `Execution` per trade in an `OrderTrades` message. The stream carries no commission (zero) and
no `order_request_id`: `cid` comes from the order ids the connector has seen.

### Function from_share

```rust
pub fn from_share(i: &Share) -> Instrument
```
A share record of the `Shares` listing to [`Instrument`] of kind `Stock`.

### Function historic_candle

```rust
pub fn historic_candle(
    instrument: &InstrumentId,
    tf: Timeframe,
    c: &HistoricCandle,
    now: Instant,
) -> Candle
```
A candle of `GetCandles` to [`Candle`].

Volume is in lots, as the broker sends it (the connector multiplies by the lot size). `close_ts` is derived from the open time and the timeframe.

### Function id_from_parts

```rust
pub fn id_from_parts(
    uid: &str,
    figi: &str,
    ticker: &str,
    class_code: &str,
) -> InstrumentId
```
Canonical id from the fields a broker message carries. Prefers the uid (stable across tickers).

The native id is the uid, else the figi, else ticker and class code. When the message has no
ticker, the key holds the uid or figi in place of the symbol, until a message with a ticker
arrives (the user data core learns the mapping).

### Function instrument_from_full

```rust
pub fn instrument_from_full(i: &Instrument) -> Instrument
```
Full record of `GetInstrumentBy` to [`Instrument`].

`exchange` holds the class code (the board), for example `TQBR`. `isin` is `None` when the broker
sends none.

### Function instrument_from_short

```rust
pub fn instrument_from_short(i: &InstrumentShort) -> Instrument
```
`FindInstrument` returns short records without currency; `currency` stays empty (use `get` for it).

### Function instrument_ref

```rust
pub fn instrument_ref(id: &InstrumentId) -> String
```
Value for the `instrument_id` request fields: `figi`, `instrument_uid` or `ticker_classcode`.

A `Uid` or `Figi` native id is passed as is. A `TickerClass` (or `SymbolMic`) becomes
`TICKER_CLASSCODE`, for example `SBER_TQBR`. No network call is made and the id is not checked
against the directory.

### Function instrument_request

```rust
pub fn instrument_request(id: &InstrumentId) -> InstrumentRequest
```
The directory request for an [`InstrumentId`]: by uid, figi, or ticker with class code.

### Function is_not_found

```rust
pub fn is_not_found(status: &Status) -> bool
```
`true` for the `NotFound` status: the broker does not know the order or the instrument.

### Function kind

```rust
pub fn kind(k: InstrumentType) -> InstrumentKind
```
Instrument kind of a broker message to the domain [`InstrumentKind`].

Bond, share, currency, ETF, futures and option map one to one. Everything else (indicatives,
structured notes, …) is [`InstrumentKind::Other`].

### Function levels

```rust
pub fn levels(orders: &[Order], bids: bool, depth: usize) -> Vec<BookLevel>
```
Levels best-first: bids by price descending, asks ascending, cut to `depth`.

### Function limits_from_toml_path

```rust
pub fn limits_from_toml_path(path: &str) -> Result<BrokerLimits, ConfigError>
```
Reads the `[tbank]` section of a limits file on disk.

**Errors**

[`ConfigError::Message`] when the file cannot be read (the message names the path), and for
every reason `limits_from_toml_str` lists.

### Function limits_from_toml_str

```rust
pub fn limits_from_toml_str(raw: &str) -> Result<BrokerLimits, ConfigError>
```
Reads the `[tbank]` section of a limits file given as text.

Use it to replace the bundled figures, for example after `GetUserTariff` showed that your tariff
has other limits.

**Errors**

[`ConfigError::Message`] when the text is not valid TOML, the `[tbank]` section is missing, or
it is invalid.

### Function lots_to_qty

```rust
pub fn lots_to_qty(lots: i64, lot: i64) -> Qty
```
Lots to units.

### Function map_command_status

```rust
pub fn map_command_status(status: &Status) -> Error
```
Status of a command (place / cancel / replace). Only statuses that prove the broker did not act
become `Rejected`; anything that can hide a processed request is `Unknown`.

The statuses that prove the broker did not act (`ResourceExhausted`, `InvalidArgument`,
`NotFound`, `PermissionDenied`, `Unauthenticated`, `FailedPrecondition`, `Unimplemented`,
`AlreadyExists`, `OutOfRange`) are mapped like in `map_status`. Every other status
(`DeadlineExceeded`, `Unavailable`, `Internal`, `Aborted`, `Cancelled`, `Unknown`, …) is
[`Error::Unknown`], because the request may have been processed before the answer was lost.
Do not repeat the command after it: ask the broker for the order first.

### Function map_status

```rust
pub fn map_status(status: &Status) -> Error
```
Status of a read-only call (snapshots, reference data).

- `ResourceExhausted` gives [`Error::RateLimited`], with `retry_after` taken from the
`x-ratelimit-reset` header (seconds), else 1 s (60 s for the codes 80003 and 80006, which the
broker counts per minute). Except the codes that are not a limit of time: 80001, 80004, 80005
and 80007 give [`Error::Rejected`] with the code `streams_limit`, `no_subscriptions`,
`stream_lifetime` or `stop_orders_limit`, because a repeat cannot help.

- `InvalidArgument` with the code 30261 (too many stop orders in the answer) gives
[`Error::Rejected`] with the code `stop_orders_response_limit`.

- `NotFound` gives [`Error::Rejected`] with the code `not_found` ([`brokapi_core::NOT_FOUND`], the
same at every broker).

- `InvalidArgument`, `PermissionDenied`, `FailedPrecondition`, `Unimplemented`,
`AlreadyExists`, `OutOfRange` gives [`Error::Rejected`], with the status code name as `code` (for
example `"InvalidArgument"`) and the status message as `message`.

- `Unauthenticated` gives [`Error::Config`]: the token is wrong or expired, fix it.

- everything else (`Unavailable`, `DeadlineExceeded`, `Internal`, …) gives [`Error::Transport`].

### Function matches

```rust
pub fn matches(
    id: &InstrumentId,
    uid: &str,
    figi: &str,
    ticker: &str,
    class_code: &str,
) -> bool
```
`true` when a broker message (identified by any of uid/figi/ticker+class) is about `id`.

A message matches when its ticker and class code equal the key of `id`, or when its uid (for a
`Uid` id) or figi (for a `Figi` id) equals the native id. An empty uid or figi in the message never matches.

### Function money_of

```rust
pub fn money_of(m: &MoneyValue) -> Money
```
A [`MoneyValue`] as [`Money`], exactly. The currency is ignored: read it from the message.

### Function nanos_to_proto

```rust
pub fn nanos_to_proto(n: UnixNanos) -> Timestamp
```
Converts nanoseconds since the Unix epoch to a protobuf `Timestamp`. Negative times keep a
non-negative `nanos` part, as protobuf requires.

### Function order_type

```rust
pub fn order_type(t: OrderType) -> OrderType
```
`ORDER_TYPE_BESTPRICE` has no counterpart in the domain model and reads as market.

### Function parse_request_id

```rust
pub fn parse_request_id(raw: &str) -> Option<ClientOrderId>
```
`order_request_id` of a broker message -> our cid, if it is one.

Returns `None` for an empty string or anything that is not a UUID: the order was placed
elsewhere (phone, another program) and carries no cid of ours.

### Function parts_from_post

```rust
pub fn parts_from_post(r: &PostOrderResponse) -> OrderParts
```
`PostOrder` / `ReplaceOrder` response.

### Function parts_from_state

```rust
pub fn parts_from_state(o: &OrderState) -> OrderParts
```
`GetOrderState` / `GetOrders`.

### Function parts_from_stream

```rust
pub fn parts_from_stream(o: &OrderState) -> OrderParts
```
`OrderStateStream` update. `order_price` is taken as the price of one instrument (the docs do not
say; for our own orders the price we sent wins, see `UdCore`).

### Function pieces_to_lots

```rust
pub fn pieces_to_lots(qty: Qty, lot: i64) -> Result<i64, Error>
```
Units to lots: the quantity must be a whole number of lots, at least one.

**Errors**

[`Error::Rejected`] with code `invalid_qty` for zero, a negative number, a fraction, or a number that
is not a multiple of `lot`. Nothing is rounded.

### Function portfolio

```rust
pub fn portfolio(
    p: &PortfolioResponse,
    cash: Vec<Balance>,
    now: Instant,
) -> Portfolio
```
A [`Portfolio`] from a `GetPortfolio` answer and the cash you already have.

Positions come from the answer, with currency holdings left out.

### Function positions_from_portfolio

```rust
pub fn positions_from_portfolio(
    p: &PortfolioResponse,
    now: Instant,
) -> Vec<Position>
```
`quantity` is in pieces (the portfolio is not in lots).

### Function positions_from_securities

```rust
pub fn positions_from_securities(
    securities: &[PositionsSecurities],
    instruments: Vec<InstrumentId>,
    now: Instant,
) -> Vec<Position>
```
Securities of a positions message (balance + blocked, pieces). No prices in this message.

### Function price_of_money

```rust
pub fn price_of_money(m: &MoneyValue) -> Price
```
A [`MoneyValue`] as a [`Price`], exactly. The currency is ignored.

### Function price_of_quotation

```rust
pub fn price_of_quotation(q: &Quotation) -> Price
```
A [`Quotation`] as a [`Price`], exactly (units and nano, no `f64`).

### Function price_to_quotation

```rust
pub fn price_to_quotation(price: Price) -> Result<Quotation, Error>
```
A [`Price`] as the [`Quotation`] a request carries, exactly.

**Errors**

[`Error::Rejected`] with code `invalid_price` when the price has more than 9 decimal places (T-Bank
counts in billionths) or does not fit. The price is never rounded in silence.

### Function proto_to_nanos

```rust
pub fn proto_to_nanos(ts: &Timestamp) -> UnixNanos
```
Converts a protobuf `Timestamp` to nanoseconds since the Unix epoch. Overflow saturates.

### Function qty_of_quotation

```rust
pub fn qty_of_quotation(q: &Quotation) -> Qty
```
A [`Quotation`] as a [`Qty`], exactly.

### Function query

```rust
pub async fn query<T>(
    timeout: Duration,
    fut: impl Future<Output = Result<Response<T>, Status>>,
) -> Result<T, Error>
```
Read-only unary call: transport trouble and timeouts are retryable errors.

Waits for `fut` at most `timeout`, then maps a gRPC failure with `map_status`.

**Errors**

- [`Error::Transport`] when the deadline passes, or for a transport status such as `Unavailable`.

- [`Error::RateLimited`], [`Error::Rejected`] and [`Error::Config`] for the matching statuses,
see `map_status`.

### Function quote

```rust
pub fn quote(
    instrument: &InstrumentId,
    bids: &[Order],
    asks: &[Order],
    broker_ts: Option<&Timestamp>,
    now: Instant,
) -> Quote
```
The best bid and the best ask of a broker order book message.

T-Bank has no quote stream: a quote is the first level of each side. A side with no orders is
`None`.

### Function require_native

```rust
pub fn require_native(id: &InstrumentId) -> Result<(), Error>
```
Refuses an [`InstrumentId`] whose [`NativeId`] is of another broker’s kind (`SymbolMic` is the Finam
form), before anything is sent.

**Errors**

[`Error::Config`]: T-Bank takes a uid, a figi, or a ticker with a class code.

### Function same_as_request

```rust
pub fn same_as_request(
    o: &StopOrder,
    req: &PlaceStopOrderRequest,
    lots: i64,
) -> bool
```
`true` when the broker stop order `o` is what `req` asks for: the same instrument, side, kind,
number of lots (`lots`), stop price and limit price. Nothing else is compared: the broker does not
return the `cid`, so this is how a stop order whose placement got no clear answer is recognised.

### Function scale_book

```rust
pub fn scale_book(book: &mut OrderBook, lot: Decimal)
```
The levels of a book, in units.

### Function scale_candle

```rust
pub fn scale_candle(c: &mut Candle, lot: Decimal)
```
The volume of a candle, in units.

### Function scale_event

```rust
pub fn scale_event(ev: &mut Event, lot: i64)
```
Multiplies the quantities of a market data event by the lot size: the levels of a book, the
best levels of a quote, the quantity of a trade, the volume of a candle. Other events stay as they are.

### Function scale_trade

```rust
pub fn scale_trade(t: &mut MarketTrade, lot: Decimal)
```
The quantity of a trade, in units.

### Function schedule

```rust
pub fn schedule(
    exchange: &str,
    sched: &TradingSchedule,
    now: Instant,
) -> Schedule
```
Main session, premarket, evening session (post-market) and auctions of every trading day.

### Function side

```rust
pub fn side(d: OrderDirection) -> Side
```
Broker direction to [`Side`]. Anything but sell reads as buy.

### Function sleep_or_cancel

```rust
pub async fn sleep_or_cancel(d: Duration, cancel: &CancellationToken) -> bool
```
Sleeps `d` unless cancelled. `false` when cancelled.

### Function snapshot

```rust
pub fn snapshot(
    o: &StopOrder,
    cid: Option<ClientOrderId>,
    now: Instant,
    lot: i64,
) -> StopOrderSnapshot
```
Core snapshot of a broker stop order. `cid` comes from what this connector registered at
`place_stop` (the broker’s `StopOrder` does not carry the idempotency key). `lot` is the lot size of
the instrument: the quantity of the snapshot is in units.

### Function stamps

```rust
pub fn stamps(now: Instant, broker: Option<&Timestamp>) -> Timestamps
```
Reception timestamps with the broker time attached when the message has one.

`now` is the instant the message was received. A missing broker time, or one that is exactly
zero, is left out.

### Function state

```rust
pub fn state(p: &OrderParts, cid: ClientOrderId, now: Instant) -> OrderState
```
The order state of a status report.

Fill, cancel, reject, new and partial fill map to their states. Partial fill carries `filled` and
`left` in units of the instrument. An unspecified status is `Unknown`, never a guess. A rejection without a reason
gets the text `REJECTED`.

### Function stream_candle

```rust
pub fn stream_candle(
    instrument: &InstrumentId,
    tf: Timeframe,
    c: &Candle,
    now: Instant,
) -> Candle
```
A candle of the candle stream to [`Candle`].

Volume is in lots, as the broker sends it (the connector multiplies by the lot size). `close_ts` is derived from the open time and the timeframe. The candle is the
current state of a candle that may still be forming.

### Function subscription_interval

```rust
pub fn subscription_interval(tf: Timeframe) -> SubscriptionInterval
```
Timeframe to the interval name used when subscribing to candles.

### Function supervise

```rust
pub async fn supervise<M, F, Fut, Frame>(
    name: &'static str,
    open: F,
    connected: impl Fn() -> Frame,
    wrap: impl Fn(M) -> Frame,
    failed: impl Fn(String) -> Frame,
    tx: Sender<Frame>,
    stale_after: Duration,
    last_error: Arc<LastError>,
    cancel: CancellationToken,
)where
    F: Fn() -> Fut,
    Fut: Future<Output = Result<Streaming<M>, Status>>,
```
Keeps one server-side stream open. `connected` goes out after every successful open (then the
consumer knows a break happened if it is not the first), `wrap` turns each message into a frame.
A stream silent for `stale_after` is closed and reopened (pings arrive on a healthy stream).

The pause before a reopen grows with each failure (`backoff`) and goes back to the start only after
the stream has delivered a message, not after the open alone: a stream that opens and breaks at once
is not tried again at full speed. When the open is refused for good (`fatal_open`), `failed(reason)`
is sent once and the supervisor ends.

### Function synthetic_cid

```rust
pub fn synthetic_cid(exchange_order_id: &str) -> ClientOrderId
```
Stable stand-in cid for an order that was not placed by us (phone, another robot): the broker gives
no `order_request_id`, but `ReconcileReport::foreign` needs a non-nil cid. Derived from the exchange
order id (FNV-1a), never produced by `Uuid::new_v4`.

The same exchange id always gives the same value, so repeated reports of one foreign order line
up. The value is not a key you can send back to the broker: the broker does not know it.

### Function timeframe_duration_nanos

```rust
pub fn timeframe_duration_nanos(tf: Timeframe) -> i64
```
Length of one candle of timeframe `tf`, in nanoseconds.

A month is taken as 30 days, because the broker’s candle carries only its open time and the close
time has to be derived.

### Function timeframe_from_interval

```rust
pub fn timeframe_from_interval(i: SubscriptionInterval) -> Option<Timeframe>
```
The interval of a candle stream message to timeframe, or `None` for an interval with no
counterpart in the domain model.

### Function trade

```rust
pub fn trade(
    instrument: &InstrumentId,
    t: &Trade,
    now: Instant,
) -> Option<MarketTrade>
```
Trades without a direction cannot be given a `Side` and are skipped.

### Function trading_params

```rust
pub fn trading_params(id: &InstrumentId, i: &Instrument) -> TradingParams
```
`lot_size` is units per lot. `min_qty` is one lot: an order is a whole number of lots (and the library
counts the quantity of an order in units).
`price_step_value` equals the step (price is per one instrument in the settlement currency);
futures need `GetFuturesMargin` for the step in money, not requested here.

### Function trading_status

```rust
pub fn trading_status(
    id: &InstrumentId,
    s: SecurityTradingStatus,
) -> TradingStatus
```
Broker trading status to [`TradingStatus`] of `id`.

`reason` holds the broker’s status name. `ts` is the local clock, since the status has no time of
its own.

### Function transport

```rust
pub fn transport(msg: impl Into<String>) -> Error
```
Builds a [`Error::Transport`] with a text message.

### Function validate

```rust
pub fn validate(req: &PlaceStopOrderRequest) -> Result<(), Error>
```
Checks that need no network. Run before any quota is spent or anything is registered.

### Function wall_nanos

```rust
pub fn wall_nanos() -> UnixNanos
```
Current wall-clock time in nanoseconds since the Unix epoch.

Returns zero if the system clock is before 1970. It is the local clock: nothing guarantees it
matches the broker’s clock.

### Function wire_depth

```rust
pub fn wire_depth(depth: u32) -> u32
```
Smallest valid wire depth that covers `depth` levels.

### Function wire_order_type

```rust
pub fn wire_order_type(t: OrderType) -> OrderType
```
[`OrderType`] to broker order type (limit or market).

## Крейт brokapi_finam


### Enum ApplyOutcome

```rust
pub enum ApplyOutcome {
    Applied,
    NeedSnapshot,
}
```
Result of applying rows to a `BookState`.

- `Applied`
  The rows were applied to the book.

- `NeedSnapshot`
  An increment arrived while no snapshot is held: the book is unusable until resubscribed.

### Struct BookState

```rust
pub struct BookState { /* private fields */ }
```
Local order book rebuilt from Finam’s snapshot + action rows.

- `pub fn reset(&mut self)`
  Forgets the whole book, including the snapshot. The next increment is refused until a new snapshot.

- `pub fn has_snapshot(&self) -> bool`
  `true` once a snapshot was applied since creation or the last `reset`.

- `pub fn apply(&mut self, rows: &[RowView], is_snapshot: bool) -> ApplyOutcome`
  `is_snapshot` replaces the whole book (a snapshot message is assumed to carry all levels).

  Without a snapshot an increment is not applied and the result is `ApplyOutcome::NeedSnapshot`.

- `pub fn levels(&self, depth: usize) -> (Vec<BookLevel>, Vec<BookLevel>)`
  Best-first levels, at most `depth` per side.

### Constant CLIENT_ORDER_ID_MAX

```rust
pub const CLIENT_ORDER_ID_MAX: usize = 20;
```
Documented limit of `Order.client_order_id`.

### Constant COMMENT_MAX

```rust
pub const COMMENT_MAX: usize = 128;
```
Documented limit of `Order.comment`.

### Constant COMMENT_PREFIX

```rust
pub const COMMENT_PREFIX: &str = "bc1:";
```
Marks a comment written by this library: `bc1:` followed by 32 hex characters.

A comment without the prefix (an order placed by hand, or by another program) does not decode.

### Constant DEFAULT_DEPTH

```rust
pub const DEFAULT_DEPTH: u32 = 50;
```
Default depth of emitted books when the subscription did not ask for one.

### Constant DEFAULT_LIMITS_TOML

```rust
pub const DEFAULT_LIMITS_TOML: &str = "# Finam Trade API rate limits for brokapi-finam.\n#\n# Sources:\n#  - \"\u{421}\u{43b}\u{438}\u{448}\u{43a}\u{43e}\u{43c} \u{43c}\u{43d}\u{43e}\u{433}\u{43e} \u{437}\u{430}\u{43f}\u{440}\u{43e}\u{441}\u{43e}\u{432}. \u{414}\u{43e}\u{441}\u{442}\u{443}\u{43f}\u{43d}\u{44b}\u{439} \u{43b}\u{438}\u{43c}\u{438}\u{442} - 200 \u{437}\u{430}\u{43f}\u{440}\u{43e}\u{441}\u{43e}\u{432} \u{432} \u{43c}\u{438}\u{43d}\u{443}\u{442}\u{443}\" - 429 description of every\n#    method in the official swagger (finam-trade-api, docs/swagger/api.swagger.json). This is the\n#    only number Finam documents. No separate limit for orders, no limit for streams is published.\n#  - 429 maps to gRPC RESOURCE_EXHAUSTED.\n#  - UsageMetricsService.GetUsageMetrics (\"\u{43c}\u{435}\u{442}\u{440}\u{438}\u{43a}\u{438} \u{438}\u{441}\u{43f}\u{43e}\u{43b}\u{44c}\u{437}\u{43e}\u{432}\u{430}\u{43d}\u{438}\u{44f} \u{434}\u{43b}\u{44f} \u{43f}\u{43e}\u{43b}\u{44c}\u{437}\u{43e}\u{432}\u{430}\u{442}\u{435}\u{43b}\u{44f}\", GET /v1/usage)\n#    returns the live quotas (name, limit, remaining, reset_time) without any account parameter.\n#\n# LIMIT SCOPE: Finam does not state it in words. The quota call is \"for the user\" and takes no\n# account_id, so the quota is read as PER USER (all tokens/secrets of one Finam user and all of his\n# accounts spend one budget). Spec 3.1 consequence: ONE BrokerLimiter per Finam user, shared by all\n# MarketDataConnector and UserDataConnector instances of that user, however many accounts or\n# secrets they cover. Open question for the live run: compare GetUsageMetrics between two secrets.\n#\n# Values below are deliberately below 200/min (conservative when data is missing).\n# Bucket \"orders\" is capped well under \"global\" so that \"cancel\" (its own bucket) always has room:\n# cancelling must not starve behind placing.\n\n[finam.buckets.global]\nrate = 200\nper = \"60s\"\nburst = 40\n\n[finam.buckets.orders]\nrate = 60\nper = \"60s\"\nburst = 5\n\n[finam.buckets.cancel]\nrate = 100\nper = \"60s\"\nburst = 10\n\n[finam.buckets.market_data]\nrate = 120\nper = \"60s\"\nburst = 20\n\n[finam.buckets.reference]\nrate = 100\nper = \"60s\"\nburst = 30\n\n[finam.buckets.reports]\n# Report generation is heavy on Finam\'s side and not documented separately; kept low on purpose.\nrate = 20\nper = \"60s\"\nburst = 3\n\n[finam.route]\n# \"*\" = any method with no route below (raw() calls to methods the library never named). Finam\n# documents one number for the whole API, so the default is the general \"reference\" budget.\n\"*\" = [\"global\", \"reference\"]\nPlaceOrder = [\"global\", \"orders\"]\nCancelOrder = [\"global\", \"cancel\"]\nGetOrder = [\"global\", \"reference\"]\nGetOrders = [\"global\", \"reference\"]\nGetAccount = [\"global\", \"reference\"]\nTrades = [\"global\", \"reference\"]\nGetAsset = [\"global\", \"reference\"]\nGetAssetParams = [\"global\", \"reference\"]\nAssets = [\"global\", \"reference\"]\nSchedule = [\"global\", \"reference\"]\nOrderBook = [\"global\", \"market_data\"]\nLatestTrades = [\"global\", \"market_data\"]\nBars = [\"global\", \"market_data\"]\n# Opening a stream is a request too (assumed to count against the same minute budget).\nSubscribeOrderBook = [\"global\", \"market_data\"]\nSubscribeQuote = [\"global\", \"market_data\"]\nSubscribeLatestTrades = [\"global\", \"market_data\"]\nSubscribeBars = [\"global\", \"market_data\"]\nSubscribeOrders = [\"global\", \"reference\"]\nSubscribeTrades = [\"global\", \"reference\"]\nSubscribeAccount = [\"global\", \"reference\"]\n# The rest of the API, reachable through raw() and the stop order trait.\nPlaceSLTPOrder = [\"global\", \"orders\"]\nTransactions = [\"global\", \"reference\"]\nExchanges = [\"global\", \"reference\"]\nOptionsChain = [\"global\", \"reference\"]\nClock = [\"global\", \"reference\"]\nGetConstituents = [\"global\", \"reference\"]\nAllAssets = [\"global\", \"reference\"]\nLastQuote = [\"global\", \"market_data\"]\nGetUsageMetrics = [\"global\", \"reference\"]\nTokenDetails = [\"global\", \"reference\"]\nCreateAccountReport = [\"global\", \"reports\"]\nGetAccountReportInfo = [\"global\", \"reports\"]\nSubscribeAccountReportInfo = [\"global\", \"reports\"]\nGetFutureSplits = [\"global\", \"reference\"]\nGetPastSplits = [\"global\", \"reference\"]\nGetFutureBondsEvents = [\"global\", \"reference\"]\nGetPastBondsEvents = [\"global\", \"reference\"]\nGetFutureDividends = [\"global\", \"reference\"]\nGetPastDividends = [\"global\", \"reference\"]\n\n[finam.conns]\n# One HTTP/2 connection per connector, streams are multiplexed over it. The number of concurrent\n# streams per connection is not documented; 100 is the HTTP/2 default most servers advertise.\nmax_conns = 1\nmax_subs_per_conn = 100\nsupports_multiplex = true\n";
```
Text of the bundled limits file. Written from the broker documentation, see the file header for sources.

### Struct FinamApiConfig

```rust
pub struct FinamApiConfig {
    pub endpoint: ApiEndpoint,
    pub secret: String,
    pub source_app_id: String,
    pub tokens: Option<Arc<TokenManager>>,
    pub bearer: bool,
    pub request_timeout: Duration,
}
```
Connection and credentials, shared in shape by both connector roles.

Holds the API secret, so it is not `Display`, and its `Debug` output hides the secret.

- `endpoint: ApiEndpoint`
  Address and timeouts of the channel. Default: the production endpoint.

- `secret: String`
  API secret. Ignored when `tokens` is set.

- `source_app_id: String`
  Optional application id sent with `Auth`.

- `tokens: Option<Arc<TokenManager>>`
  Already running token manager to reuse (several accounts / roles on one secret).

- `bearer: bool`
  Send `authorization: Bearer <jwt>` instead of the bare JWT (official SDKs send it bare).

- `request_timeout: Duration`
  Deadline of a unary call. Default 10 seconds.

  When it passes with no answer, a read fails with a transport error, and a command (place, cancel)
  ends as `Error::Unknown`, because the broker may still have applied it.

- `pub fn new(secret: impl Into<String>) -> Self`
  Config for `secret` with the production endpoint and default settings.

  Nothing is checked or sent here. An empty or wrong secret shows up when a connector starts
  (`Error::Config` for an empty one, `Error::Rejected` for one the broker refuses).

- `pub fn with_tokens(self, tokens: Arc<TokenManager>) -> Self`
  Reuse a token manager (see `FinamApiConfig::shared_tokens`).

  When set, the connector does not log in itself and `secret` is not used.

- `pub fn with_endpoint(self, url: impl Into<String>) -> Self`
  Points the config at another gRPC server, with the default timeouts.

  `url` is `https://host:port` (TLS) or `http://host:port` (plain, for a proxy or the test
  imitation). It replaces the whole [`ApiEndpoint`], so set custom timeouts on
  `FinamApiConfig::endpoint` after this call.

  Examples

- `pub async fn shared_tokens(&self) -> Result<Arc<TokenManager>, Error>`
  Authenticates once and returns the manager to put into several connector configs, so that
  all accounts of one secret share a single JWT.

  The manager renews the token in the background until the last `Arc` is dropped.

  Errors

  - [`Error::Config`]: the secret is empty or the endpoint URL is invalid.

  - [`Error::Config`] also: the broker refused the secret (a wrong token is a configuration problem at every broker).

  - [`Error::RateLimited`]: the broker answered `RESOURCE_EXHAUSTED`.

  - [`Error::Transport`]: the broker could not be reached or the call timed out.

### Struct FinamCidCodec

```rust
pub struct FinamCidCodec;
```
Codec between the library’s client order id (a UUID) and the fields Finam stores on an order.

Implements [`CidCodec`]: `encode` makes the lossless text for the order
`comment`, `decode` reads it back. `FinamCidCodec::client_order_id` makes the
short, one-way tag for the `client_order_id` field.

The connector uses it on its own. You need it to read the cid out of an order you fetched through
`raw()`, or to find your order at the broker by its comment.

See the module docs for the reason for two fields.

- `pub fn client_order_id(&self, cid: ClientOrderId) -> String`
  The 20 character tag for the `client_order_id` field of an order.

  20 lowercase hex chars from 10 fully random bytes of the UUID (v4 version/variant skipped).
  It is one-way: 80 bits of 128 cannot rebuild the cid. It is only a second key for the case
  when the comment is missing in an echo.

- `pub fn validate(&self) -> Result<(), Error>`
  Startup check: the formats must fit the broker limits, otherwise it is a
  configuration error, not a runtime degradation.

  Encodes a sample cid, checks the length of the comment and of the tag, and decodes the
  comment back. `UserDataConnector::start` calls it.

  Errors

  [`brokapi_core::Error::Config`] when a form does not fit the documented limits
  (`COMMENT_MAX`, `CLIENT_ORDER_ID_MAX`) or does not decode back to the same cid.

  Examples

### Struct FinamRaw

```rust
pub struct FinamRaw { /* private fields */ }
```
Handle for calling any Finam method with the connector’s token, endpoint and rate limiter.

Get it from `MarketDataConnector::raw` or `UserDataConnector::raw`. Cheap to clone. It stays
usable while the connector it came from is alive. See the module docs for the rules of a call.

- `pub fn account_id(&self) -> Option<&str>`
  Account of the connector this came from. `None` for a market data connector.

- `pub fn timeout(&self) -> Duration`
  Deadline applied to every call (`FinamApiConfig::request_timeout` of the connector).

- `pub fn limiter(&self) -> &Arc<BrokerLimiter> ⓘ`
  The limiter shared with the connector.

- `pub async fn call<T, F, Fut>(&self, method: &str, f: F) -> Result<T, Error>where F: Fn(FinamApi) -> Fut, Fut: Future<Output = Result<Response<T>, Status>>,`
  Reads: quota for `method`, deadline, one re-auth retry, error mapping.

  `f` may run twice (after a re-auth), hence `Fn`. For a server-streaming method the result is
  `tonic::Streaming<_>`: the quota covers opening the stream. A stream opened here is not
  supervised: when it breaks or the token changes, open it again yourself.

  Do not use it for a call that changes the account, because of the repeat. Use
  `call_command`.

  Errors

  - [`Error::RateLimited`]: the local quota for `method` is spent. Nothing was sent.

  - [`Error::Rejected`]: the broker refused (bad argument, unknown id, no permission, or the
  token was refused again after the renewal).

  - [`Error::Transport`]: connection failure, or no answer within `timeout`.

  A failed token renewal is reported with the same mapping.

  Examples

  See the module docs.

- `pub async fn call_command<T, F, Fut>( &self, method: &str, f: F, ) -> Result<T, Error>where F: Fn(FinamApi) -> Fut, Fut: Future<Output = Result<Response<T>, Status>>,`
  Commands: quota for `method`, deadline, NO retry. Anything that may have reached the broker
  (timeout, break, server fault) is `Error::Unknown`.

  There is no repeat on purpose: a second send could change the account twice. When the answer
  is `Unknown`, read the state (orders, positions) before deciding anything.

  The order index of the connector does not see what you place here: no events, no
  `reconcile`. For plain and stop orders use the shared traits.

  Errors

  - [`Error::RateLimited`]: the local quota for `method` is spent. Nothing was sent.

  - [`Error::Rejected`]: the broker certainly did not process the request (for example
  `INVALID_ARGUMENT` or `NOT_FOUND`, which is the code `not_found`).

  - [`Error::Config`]: `UNAUTHENTICATED`, the token is refused. A command is not repeated after a token
  renewal.

  - [`Error::Unknown`]: timeout, broken connection or server fault. The command may have been applied.

  Examples

### Type Alias FirstOutcome

```rust
pub struct FirstOutcome { /* private fields */ }
```
Result of the first open attempt, reported once to `subscribe_*`.

### Struct M10Builder

```rust
pub struct M10Builder { /* private fields */ }
```
Stream: follows M5 candle updates and keeps the M10 candle that is forming.

Every M5 update (a forming bar sent again, or a closed one) gives the current M10 candle. A
bar of an older interval than the current one is a replay and is ignored.

- `pub fn new() -> Self`
  An empty builder.

- `pub fn on_m5(&mut self, bar: &Candle) -> Option<Candle>`
  Feeds one M5 update. Returns the current M10 candle, or `None` when the bar belongs to an interval
  older than the current one (a replay after a reconnect).

  Call it for every M5 message, including the repeats of a forming bar: each one gives the
  up-to-date M10 candle. Prices and volume of the candle are not final until the interval ends.

### Struct MarketDataConfig

```rust
pub struct MarketDataConfig {
    pub api: FinamApiConfig,
    pub source: Arc<str>,
    pub subscribe_ack_timeout: Duration,
}
```
Market data connector config. Not bound to an account.

Used with `MarketDataConnector::start`.

- `api: FinamApiConfig`
  Credentials and endpoint.

- `source: Arc<str>`
  Envelope source label, e.g. `"finam-md"`.

- `subscribe_ack_timeout: Duration`
  How long `subscribe_*` waits for the stream to open (default 3 seconds).

  The call returns as soon as the broker has opened the stream: what happens after that is delivered. A
  refusal (unknown symbol, no permission) is returned as `Rejected`. A stream that has not opened in time
  leaves the subscription connecting and the call returns `Ok`, unlike the other brokers: Finam is gRPC,
  and a gRPC server may send the headers of a quiet stream only together with its first message, so a
  healthy stream of orders can look “not open” until the first order. Until the stream is open, events of
  the broker are not delivered: check
  `MarketDataConnector::sub_status`. Not verified against the live
  server; if it sends the headers at once, this will become an error like at the other brokers.

- `pub fn new(api: FinamApiConfig, source: impl Into<Arc<str>>) -> Self`
  Config with default settings. `source` labels every envelope the connector emits.

### Struct MarketDataConnector

```rust
pub struct MarketDataConnector { /* private fields */ }
```
Finam market data connector: order books, trades, quotes, candles and the instrument directory.

One connector is enough for the whole broker, it is not tied to an account. It implements
[`OrderBookSource`], [`MarketDataSource`] and [`InstrumentSource`]. Queries return their answer
directly. Subscriptions deliver [`Envelope`]s into the channel given to
`start`.

What is specific to Finam:

- Instruments are `SYMBOL@MIC`. Pass `NativeId::SymbolMic`. Any other native id gives
`Error::Config` without a network call.

- Order books come as full snapshots. Finam sends rows with ADD, UPDATE and REMOVE actions. The
core folds them into a local book, so you never see increments (`incremental_order_book` is
false). If an increment arrives with no snapshot under it, the stream is reopened and you get an
`Event::Resync`.

- Depth. Finam documents no depth limit. The book is cut to the depth you ask for, at most
`caps().max_order_book_depth`.

- Every call spends quota. Opening a stream counts as a request too. A refusal is
`Error::RateLimited`, and nothing is sent.

- Streams reconnect by themselves with a growing pause (0.5 s up to 30 s). After a reconnect
of an order book stream you get `Event::Resync` and then a new snapshot. Trades, quotes and
candle streams resume without a notice (trades already seen are not repeated).
`sub_status` says what a stream is doing.

- M10 candles are built by the connector from M5 candles, because the wire has no M10.

- Schedules are per instrument: pass `SBER@MISX` as the `exchange` argument of `schedule`.

- `pub async fn start( cfg: MarketDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Logs in, opens a lazy channel and starts the event pump. Streams are opened later, by `subscribe_*`.

  `limiter` is the rate limiter of your Finam user: create one per user and share it with every
  connector. Events and subscription results go to `out`. Give it room, because a full channel makes
  a `Lossless` subscription overflow and a `Conflate` one merge events.

  If `cfg.api.tokens` is set, that token manager is used and no login happens here.

  Errors

  - [`Error::Config`]: empty secret, or an invalid endpoint URL.

  - [`Error::Config`] also: the broker refused the secret (a wrong token is a configuration problem at every broker).

  - [`Error::RateLimited`]: the broker answered `RESOURCE_EXHAUSTED` to the login.

  - [`Error::Transport`]: the broker could not be reached.

  Examples

- `pub fn raw(&self) -> FinamRaw`
  Any other method of the Finam API with this connector’s token, endpoint and rate limiter.
  See `FinamRaw`.

  Examples

  See the `raw` module for a complete call.

- `pub fn caps(&self) -> MarketDataCaps`
  What this connector can do. Order books are streamed as full snapshots: Finam sends rows
  with ADD/UPDATE/REMOVE actions, which the core folds into a local book, so the consumer
  never sees increments (and `Conflate` is safe).

  All four streams (order book, market trades, quotes, candles) are on. The maximum depth is
  the cut this connector applies, not a limit Finam documents.

  Examples

- `pub fn sub_status(&self, sub: SubscriptionId) -> Option<SubStatus>`
  State of a subscription’s stream (connecting, active, reconnecting, failed).

  `None` for an id this connector does not know (never issued, or already unsubscribed).
  Use it after `subscribe_*` returned and the stream may still be opening: Finam may send the headers of a
  quiet stream only with its first message, so the call can return before the stream is open.

  Examples

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Graceful: every stream is cancelled (the protocol-level unsubscribe of gRPC) and awaited
  within `deadline`; `Immediate` aborts them.

  Consumes the connector and stops the event pump. The pump makes one last pass and hands over
  what fits into your channel without waiting. Events that do not fit are dropped. There are no commands here, so nothing is abandoned in flight
  (`inflight_abandoned` is always empty).

  Examples

### Struct MdCore

```rust
pub struct MdCore { /* private fields */ }
```
Market data core: turns stream frames into events and keeps the state behind them.

Per subscription it holds the local order book (rebuilt from action rows), the ids of trades
already seen (a reconnect replays recent trades, a repeat is dropped), the M10 candle being built
from M5 bars, and the stream status.

Implements `BrokerCore` from `brokapi-sansio`. See the module docs for an example.

- `pub fn new() -> Self`
  An empty core with no subscriptions.

- `pub fn desired(&self) -> &SubscriptionSet`
  The subscriptions the core was told to serve (set by `apply`).

- `pub fn status(&self, sub: SubscriptionId) -> Option<SubStatus>`
  Stream status of subscription `sub`. `None` for an id the core does not know.

- `pub fn take_restarts(&mut self) -> Vec<SubscriptionId>`
  Streams to reopen because their local book became unusable. Drains the list.

### Enum MdEvent

```rust
pub enum MdEvent {
    OrderBook(SubscribeOrderBookResponse),
    Quotes(SubscribeQuoteResponse),
    Trades(SubscribeLatestTradesResponse),
    Bars(SubscribeBarsResponse),
    Connected,
    Disconnected(String),
    Failed(String),
}
```
What arrived on a market-data stream.

- `OrderBook(SubscribeOrderBookResponse)`
  A message of an order book stream: snapshot or action rows.

- `Quotes(SubscribeQuoteResponse)`
  A message of a quote stream. It can carry an in-band error instead of quotes.

- `Trades(SubscribeLatestTradesResponse)`
  A message of a market trades stream.

- `Bars(SubscribeBarsResponse)`
  A message of a bars stream.

- `Connected`
  The stream was opened (also after a reopen).

- `Disconnected(String)`
  The stream broke or ended and is being reopened. The text says why.

- `Failed(String)`
  The broker refuses the stream (bad symbol, no permission). The text says why.

### Struct MdFrame

```rust
pub struct MdFrame {
    pub sub: SubscriptionId,
    pub event: MdEvent,
}
```
One stream event of one subscription.

- `sub: SubscriptionId`
  The subscription whose stream produced the event.

- `event: MdEvent`
  What happened on that stream.

- `pub fn lifecycle(sub: SubscriptionId, ev: StreamEvent<()>) -> Option<Self>`
  Frame for a stream lifecycle event (connected, disconnected, failed).

  `None` for `StreamEvent::Msg`, because a message has its own typed variant of `MdEvent`.

  Examples

### Type Alias OpenFn

```rust
pub struct OpenFn<M> { /* private fields */ }
```
How to open a stream: a function that returns the future of a new stream. Called again after
every drop, so it must build a fresh request each time. It reads the token through the
`FinamApi` it captured.

### Struct OrderIndex

```rust
pub struct OrderIndex { /* private fields */ }
```
Memory of which Finam order id belongs to which client order id.

Thread safe. The user data connector owns one and its core reads it for every order echo.

See the module docs for an example.

- `pub fn new() -> Self`
  An empty index.

- `pub fn know(&self, cid: ClientOrderId)`
  A cid this process knows about: its tag becomes a lookup key for echoes without comment.

- `pub fn bind(&self, cid: ClientOrderId, order_id: &str)`
  Remembers that Finam order `order_id` belongs to `cid`. An empty `order_id` is ignored.

- `pub fn forget(&self, cid: ClientOrderId)`
  Forgets everything kept for `cid`: the pair with its Finam order id and its tag. For an order that
  finished long ago. A later echo of that order resolves through its comment again, or to a made-up cid.

- `pub fn order_id(&self, cid: ClientOrderId) -> Option<String>`
  Finam order id of `cid`, if this process has seen the pair. `None` does not mean the order
  does not exist: after a restart call `reconcile` first.

- `pub fn lookup(&self, order_id: &str, comment: &str) -> Option<ClientOrderId>`
  Like `OrderIndex::resolve` but never invents a cid: `None` for orders that are not ours.

- `pub fn resolve( &self, order_id: &str, client_order_id: &str, comment: &str, ) -> ClientOrderId`
  Cid of an order echo, in order of trust: comment, known order id, known tag, otherwise a
  stable made-up cid (UUID v5 of the order id). The result is bound to `order_id`.

### Struct PumpIo

```rust
pub struct PumpIo<F> {
    pub rx: Receiver<F>,
    pub out: Sender<Envelope>,
    pub source: Arc<str>,
    pub backpressure: SharedBackpressure,
    pub cancel: CancellationToken,
}
```
The channels and handles one `run_pump` task works with.

`F` is the frame type of the connector (`MdFrame` or `UdFrame`).

- `rx: Receiver<F>`
  Frames from the stream tasks.

- `out: Sender<Envelope>`
  Where the envelopes go: the channel the user gave to `start`.

- `source: Arc<str>`
  Source label written into every envelope.

- `backpressure: SharedBackpressure`
  The per-subscription buffers that hold events until `out` has room.

- `cancel: CancellationToken`
  Stops the pump. A child of the connector’s root token.

### Enum RowAction

```rust
pub enum RowAction {
    Remove,
    Set,
}
```
Row action of a stream update.

- `Remove`
  The level is removed from the book.

- `Set`
  ADD, UPDATE and an unspecified action all mean “set this level to this size”.

### Struct RowView

```rust
pub struct RowView {
    pub price: Price,
    pub is_ask: bool,
    pub size: Qty,
    pub action: RowAction,
    pub ts: Option<UnixNanos>,
}
```
Side-neutral view of a book row (unary and stream rows are different generated types).

- `price: Price`
  Price of the level.

- `is_ask: bool`
  `sell_size` rows are asks, `buy_size` rows are bids.

- `size: Qty`
  Size of the level in pieces. A zero size on a `RowAction::Set` row removes the level.

- `action: RowAction`
  What to do with the level.

- `ts: Option<UnixNanos>`
  Time of the row at the broker, when it sends one.

### Struct Rpc

```rust
pub struct Rpc {
    pub api: FinamApi,
    pub limiter: Arc<BrokerLimiter>,
    pub timeout: Duration,
}
```
Everything a connector needs to make calls: clients, quota, deadline.

Cheap to clone. `FinamRaw` wraps it for application code.

- `api: FinamApi`
  Service clients with the shared token manager.

- `limiter: Arc<BrokerLimiter>`
  Rate limiter shared by all connectors of one Finam user.

- `timeout: Duration`
  Deadline of one unary call.

- `pub fn acquire(&self, method: &str) -> Result<(), Error>`
  Takes the quota for the proto method `method`.

  Errors

  [`brokapi_core::Error::RateLimited`] when a bucket on the route of `method` is empty. Nothing
  is consumed in that case and the request must not be sent.

- `pub async fn read<T, F, Fut>(&self, method: &str, f: F) -> Result<T, Error>where F: Fn(FinamApi) -> Fut, Fut: Future<Output = Result<Response<T>, Status>>,`
  A read-only call: quota, deadline, one re-auth retry, public error mapping.

  Errors

  [`brokapi_core::Error::RateLimited`] (local quota or `RESOURCE_EXHAUSTED`),
  [`brokapi_core::Error::Rejected`] (the broker said no), [`brokapi_core::Error::Transport`]
  (connection failure or timeout). See `crate::error::map_protocol_err`.

- `pub async fn send_command<T, F, Fut>(&self, f: F) -> Result<T, FinamError>where F: Fn(FinamApi) -> Fut, Fut: Future<Output = Result<Response<T>, Status>>,`
  A trading command: quota, deadline, NO retry. The raw protocol error is returned so the
  caller can tell “definitely rejected” from “unknown outcome”.

  Quota is taken by the caller beforehand (`acquire`) so that a refusal happens before the
  command is registered as in flight.

  Errors

  The raw [`FinamError`]. Map it with `crate::error::map_command_err`.

### Type Alias SharedBackpressure

```rust
pub struct SharedBackpressure { /* private fields */ }
```
The per-subscription buffers, shared between the pump and the connector (which registers and
unregisters subscriptions in it).

### Enum StopWire

```rust
pub enum StopWire {
    Order(Order),
    TakeProfit(SltpOrder),
}
```
Which Finam call carries the request.

- `Order(Order)`
  `PlaceOrder` with type STOP or STOP_LIMIT.

- `TakeProfit(SltpOrder)`
  `PlaceSLTPOrder` with the take-profit leg only.

### Enum StreamEvent

```rust
pub enum StreamEvent<M> {
    Connected,
    Msg(M),
    Disconnected(String),
    Failed(String),
}
```
What a stream task reports about its stream (before it is wrapped into a core frame).

- `Connected`
  The stream was opened (also after a reopen).

- `Msg(M)`
  One message of the stream.

- `Disconnected(String)`
  The stream broke or ended. It is being reopened. The text says why.

- `Failed(String)`
  The broker refuses the stream (bad symbol, no permission). It is retried slowly. The text says why.

### Struct StreamHandle

```rust
pub struct StreamHandle { /* private fields */ }
```
Handle of a running stream supervisor: restart it, stop it, or abort it.

Dropping the handle does not stop the stream. Call `stop` or `abort`,
or cancel the token given to `spawn_stream`.

- `pub fn restart(&self)`
  Drop the current stream and open a fresh one right away (book out of sync).

  The pause of the backoff starts over. The new stream is reported as `Connected` again.

- `pub async fn stop(self, limit: Duration) -> bool`
  Cancels the stream (protocol-level unsubscribe) and waits for the task, at most `limit`.
  `true` when the task ended in time.

  Examples

  See the module docs.

- `pub fn abort(self)`
  Cancels the stream and kills the task at once, without waiting for it to finish its step.

### Struct StreamParams

```rust
pub struct StreamParams<M, F> {
    pub sub: SubscriptionId,
    pub method: &'static str,
    pub limiter: Arc<BrokerLimiter>,
    pub tokens: Arc<TokenManager>,
    pub open: OpenFn<M>,
    pub open_timeout: Duration,
    pub wrap: Arc<dyn Fn(StreamEvent<M>) -> F + Send + Sync>,
    pub tx: Sender<F>,
    pub first: Option<FirstOutcome>,
}
```
Everything `spawn_stream` needs. `M` is the message type of the stream, `F` the frame type the
connector wants.

- `sub: SubscriptionId`
  The subscription this stream serves. Only used for logging here.

- `method: &'static str`
  Limiter key of the open call (`SubscribeOrders`, …).

- `limiter: Arc<BrokerLimiter>`
  Rate limiter. A refused open waits for the time the limiter gives and tries again.

- `tokens: Arc<TokenManager>`
  Token manager. The supervisor asks it for a renewal after `UNAUTHENTICATED`.

- `open: OpenFn<M>`
  How to open the stream.

- `open_timeout: Duration`
  How long one open attempt may take before it counts as a failure.

- `wrap: Arc<dyn Fn(StreamEvent<M>) -> F + Send + Sync>`
  Turns a `StreamEvent` into the connector’s frame.

- `tx: Sender<F>`
  Where the frames go. When the receiver is gone, the supervisor ends.

- `first: Option<FirstOutcome>`
  Receives the outcome of the FIRST open attempt, once. Used by `subscribe_*` to return a refusal.

### Struct SubIdGen

```rust
pub struct SubIdGen(/* private fields */);
```
Hands out [`SubscriptionId`]s: 1, 2, 3, … (after `start`), never repeating.

The market data connector starts at 0 and the user data connector at 1000, so that ids from the
two connectors of one process are easy to tell apart in logs. Thread safe.

- `pub fn new(start: u64) -> Self`
  A generator whose first id is `start + 1`.

- `pub fn next(&self) -> SubscriptionId`
  The next unused id.

### Enum SubStatus

```rust
pub enum SubStatus {
    Connecting,
    Active,
    Reconnecting(String),
    Failed(String),
    Erroring(String),
}
```
State of one subscription as seen by the core.

- `Connecting`
  Registered, stream not open yet.

- `Active`
  Stream open.

- `Reconnecting(String)`
  Stream dropped, being reopened with backoff.

- `Failed(String)`
  Server refuses the subscription (bad symbol, no permission…); retried slowly.

- `Erroring(String)`
  Stream is open but carried an in-band error message.

### Struct UdCore

```rust
pub struct UdCore { /* private fields */ }
```
User data core: turns stream frames into order, execution and portfolio events.

It resolves the client order id of every order echo through the `OrderIndex` and advances the
local lifecycle of orders this process placed (so `order_state` and the stream agree).

Implements `BrokerCore` from `brokapi-sansio`.

- `pub fn new(index: Arc<OrderIndex>) -> Self`
  A core with no subscriptions. `index` is shared with the connector.

- `pub fn orders(&mut self) -> &mut OrderRuntime`
  Local lifecycle of orders placed through this connector.

- `pub fn take_forgotten(&mut self) -> Vec<ClientOrderId> ⓘ`
  Takes the cids the core forgot since the last call: orders that finished long ago (see the memory
  window of [`OrderRuntime`]). The connector drops what else it keeps for them.

- `pub fn index(&self) -> &Arc<OrderIndex> ⓘ`
  The order index shared with the connector.

- `pub fn register(&mut self, sub: SubscriptionId, kind: UdSubKind)`
  Adds a subscription of `kind` (status `Connecting`). Executions need this call because the
  shared `SubscriptionKind` has no variant for them.

- `pub fn unregister(&mut self, sub: SubscriptionId)`
  Forgets subscription `sub` and its status. Late frames for it are ignored.

- `pub fn status(&self, sub: SubscriptionId) -> Option<SubStatus>`
  Stream status of subscription `sub`. `None` for an id the core does not know.

- `pub fn ingest_order(&mut self, os: &OrderState, now: Instant) -> OrderSnapshot`
  Maps one order echo and advances the local lifecycle of orders we placed.

### Enum UdEvent

```rust
pub enum UdEvent {
    Orders(SubscribeOrdersResponse),
    Trades(SubscribeTradesResponse),
    Account(GetAccountResponse),
    Connected,
    Disconnected(String),
    Failed(String),
}
```
What arrived on a user data stream.

- `Orders(SubscribeOrdersResponse)`
  A message of the order stream: current states of changed orders.

- `Trades(SubscribeTradesResponse)`
  A message of the own trades stream.

- `Account(GetAccountResponse)`
  A message of the account stream: the whole account state.

- `Connected`
  The stream was opened (also after a reopen).

- `Disconnected(String)`
  The stream broke or ended and is being reopened. The text says why.

- `Failed(String)`
  The broker refuses the stream (no permission). The text says why.

### Struct UdFrame

```rust
pub struct UdFrame {
    pub sub: SubscriptionId,
    pub event: UdEvent,
}
```
One stream event of one subscription.

- `sub: SubscriptionId`
  The subscription whose stream produced the event.

- `event: UdEvent`
  What happened on that stream.

### Enum UdSubKind

```rust
pub enum UdSubKind {
    Orders,
    Executions,
    Portfolio,
}
```
What a user data subscription carries.

- `Orders`
  Order state: `Event::Order`.

- `Executions`
  Own trades: `Event::Execution`.

- `Portfolio`
  Account: `Event::Portfolio`.

### Struct UserDataConfig

```rust
pub struct UserDataConfig {
    pub api: FinamApiConfig,
    pub account_id: String,
    pub source: Arc<str>,
    pub order_state_buffer: usize,
    pub executions_limit: i32,
    pub subscribe_ack_timeout: Duration,
    pub price_grid: PriceGrid,
}
```
User data connector config: one connector = one account.

Used with `UserDataConnector::start`.

- `api: FinamApiConfig`
  Credentials and endpoint.

- `account_id: String`
  Account all orders, executions and portfolio belong to.

- `source: Arc<str>`
  Envelope source label, e.g. `"finam-ud-A12345"`.

- `order_state_buffer: usize`
  Lossless buffer capacity for order-state and execution subscriptions. Default 1024.

  When it fills up, the subscription reports an overflow and the connector turns `Degraded`:
  trading commands are refused until `reconcile` succeeds. Events are never dropped silently.

- `executions_limit: i32`
  `limit` sent with the `Trades` history request. Default 1000.

  `executions(range)` returns at most this many trades.

- `subscribe_ack_timeout: Duration`
  How long `subscribe_*` waits for the stream to open (default 3 seconds). Same meaning as in
  `MarketDataConfig`.

- `price_grid: PriceGrid`
  Price steps the connector checks order prices against (default: empty, nothing is checked).
  Fill it with [`PriceGrid::learn`] from the trading parameters of the instruments you trade. A
  price off the step is then refused with `Rejected` (`price_off_step`) before anything is sent.

- `pub fn new( api: FinamApiConfig, account_id: impl Into<String>, source: impl Into<Arc<str>>, ) -> Self`
  Config for one `account_id` with default settings. `source` labels every envelope.

  The account is checked when the connector starts: it must not be empty and, when the token
  lists its accounts, it must be one of them.

### Struct UserDataConnector

```rust
pub struct UserDataConnector { /* private fields */ }
```
Finam user data connector for ONE account: orders, stop orders, own trades and the portfolio.

It implements [`OrderExecutor`], [`StopOrderExecutor`], [`ExecutionSource`] and
[`PortfolioSource`]. To trade two accounts, start two connectors (they may share one token
manager and must share one rate limiter).

Rules that protect your money:

- Call `reconcile` first. `place`, `cancel`, `place_stop` and `cancel_stop` return
`Error::NotReconciled` until `reconcile` has succeeded once. Pass the cids you know from before the
restart (an empty list at a clean start).

- A place command is sent once. If it gets no answer in time, the result is `Error::Unknown`
and the library does not repeat it, because a repeat could create a second order. Ask
`order_state` or `active_orders` to see what happened. Sending the same cid again is refused
locally with `Error::Rejected` and code `duplicate_place`.

- A refusal is final. `Error::Rejected` means the broker took nothing. Use a NEW cid for the
next try.

- The gate is checked twice. Once at the start, and again when the command’s turn in the queue of its
instrument comes: a gate that closed while it waited (`Degraded`, shutdown) stops it, and nothing is sent.

- A cut of the order or trade stream is told. `Event::Reconnected` on the subscription, the gate closes
until `reconcile`.

- The cid travels in the order `comment` (`bc1:` and 32 hex digits), so it survives a restart.
See `FinamCidCodec`. That the broker echoes the comment unchanged is not verified against the
live API.

- No replace. Finam has no amend call. `replace` returns `Error::Unsupported`. Cancel plus a new
order is not offered, because it is not atomic.

- Quantity is in pieces (units of the instrument, like at every broker), price in the instrument currency. A quantity
that is not a whole number of lots is refused with `invalid_qty` before anything is sent, when the lot is known
(`price_grid().set_lot(..)` or `.learn(&params)`).

- Finam has no sandbox. Test against the mock, then with a tiny order far from the market.

- `pub async fn start( cfg: UserDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Checks the config and the cid codec, logs in, and starts the event pump. Streams are opened later,
  by the `subscribe_*` methods.

  The connector starts NOT reconciled: commands are refused until you call
  `reconcile`. `limiter` is the rate limiter of your Finam user,
  shared by every connector. Events go to `out`.

  When the token lists its accounts and `cfg.account_id` is not one of them, start fails.

  Errors

  - [`Error::Config`]: empty account id, empty secret, invalid endpoint URL, the cid codec does
  not fit the broker limits, or the account is not available to this secret.

  - [`Error::Config`] also: the broker refused the secret (a wrong token is a configuration problem at every broker).

  - [`Error::RateLimited`]: the broker answered `RESOURCE_EXHAUSTED` to the login.

  - [`Error::Transport`]: the broker could not be reached.

  Examples

- `pub fn in_flight_orders(&self) -> usize`
  How many commands are in flight: sent, and the answer has not been taken yet. Zero when all of them
  are settled. A `place` runs to its end even if the caller drops its future, so this number goes back
  to zero by itself. For health checks and tests.

  Examples

- `pub fn account_id(&self) -> &str`
  Account this connector trades and reads.

  Examples

  See `start`.

- `pub fn raw(&self) -> FinamRaw`
  Any other method of the Finam API with this connector’s token, endpoint and rate limiter.
  `account_id()` of the result is this connector’s account. See `FinamRaw`.

  Orders placed through `raw()` are not tracked by this connector: no events, no `reconcile`.
  Use the shared traits for orders.

  Examples

- `pub fn price_grid(&self) -> &PriceGrid`
  The price steps this connector checks order prices against. Fill it from
  `TradingParams` (`grid.learn(&params)`) to have a price off the
  step refused before anything is sent. See [`PriceGrid`].

- `pub fn caps(&self) -> UserDataCaps`
  Capabilities.

  - `place_idempotency` is `LabelOnly` until a live experiment proves otherwise. The `client_order_id` is a label for us, nothing says Finam rejects or merges a
  repeat of it.

  - No replace call exists in the Finam API.

  - `supports_stop_orders` is true: stop-loss and stop-limit go through `PlaceOrder` (STOP,
  STOP_LIMIT), take-profit through `PlaceSLTPOrder`, see [`StopOrderExecutor`]. Not verified
  against the live API.

  - Executions stream (`SubscribeTrades`) exists. Portfolio also streams (`SubscribeAccount`).

  - Finam Trade API has no sandbox.

  Examples

- `pub fn sub_status(&self, sub: SubscriptionId) -> Option<SubStatus>`
  State of a subscription’s stream (connecting, active, reconnecting, failed).

  `None` for an id this connector does not know. Use it after `subscribe_*` returned and the
  stream may still be opening (Finam may send the headers of a quiet stream only with its first message).
  A stream that the broker refuses (no permission) shows as
  `SubStatus::Failed` and is retried slowly.

  Examples

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Stops the connector in the order that protects your orders.

  - New commands are refused (`Error::ShuttingDown`).

  - Every stream is cancelled (the gRPC way of unsubscribing). `Graceful` waits for each up to
  `deadline`, `Immediate` aborts them.

  - Commands still in flight are abandoned and listed in the report (`inflight_abandoned`).
  Their orders may exist at the broker. Read them with `active_orders` after you start again.

  - The event pump stops.

  Orders already on the exchange stay there. Shutdown does NOT cancel them.

  Examples

### Function api_config_from_env

```rust
pub fn api_config_from_env() -> Result<(FinamApiConfig, Option<String>), FinamError>
```
Reads the config from environment variables. Meant for examples and live tests.

Variables:

- `FINAM_SECRET` (required): the API secret.

- `FINAM_API_URL` (optional): another endpoint instead of the production one. It must start with
`https://`: the address of a local imitation is set in code with `FinamApiConfig::with_endpoint`.

- `FINAM_ACCOUNT_ID` (optional): returned as the second value, for user data connectors.

**Errors**

[`FinamError::Config`] when `FINAM_SECRET` is not set, or `FINAM_API_URL` is set and is not an
`https://` address. Nothing is sent to the network here.

### Function apply_trade_lot_size

```rust
pub fn apply_trade_lot_size(params: &mut TradingParams, trade_lot_size: i64)
```
The lot for trading, when `GetAssetParams` gives one: `trade_lot_size` above 0 replaces the lot size of
`GetAsset` (`lot_size` and `min_qty`). 0 or less means “no value” and `params` stays as it is.

### Function availability_at

```rust
pub fn availability_at(
    resp: &ScheduleResponse,
    at: UnixNanos,
) -> (TradingAvailability, Option<String>)
```
Availability from the schedule: inside a non-closed session -> open, otherwise closed.

### Function bar_close

```rust
pub fn bar_close(tf: Timeframe, open: UnixNanos) -> UnixNanos
```
Finam gives the bar open time only; close is open + frame length (UTC for week/month).

### Function book_snapshot

```rust
pub fn book_snapshot(
    instrument: &InstrumentId,
    state: &BookState,
    depth: usize,
    broker_ts: Option<UnixNanos>,
    now: Instant,
) -> OrderBook
```
Full snapshot event from the local book.

### Function build

```rust
pub fn build(
    account_id: &str,
    req: &PlaceStopOrderRequest,
) -> Result<StopWire, Error>
```
Checks that need no network, then the wire request.

### Function build_place_order

```rust
pub fn build_place_order(
    account_id: &str,
    req: &PlaceOrderRequest,
) -> Result<Order, Error>
```
Builds the `PlaceOrder` request. Quantity is in pieces (шт.), as in the Finam API.

### Function cid_from_comment

```rust
pub fn cid_from_comment(comment: &str) -> Option<ClientOrderId>
```
Cid carried by an order echo: the comment (lossless) wins.

### Function code_name

```rust
pub fn code_name(code: Code) -> &'static str
```
Lowercase name of a gRPC status code (`invalid_argument`), used as the `code` of `Error::Rejected`.

### Function dec

```rust
pub fn dec(d: &Option<Decimal>) -> Option<Decimal>
```
`google.type.Decimal` -> [`Dec`], exactly: the text is parsed as it is, no `f64` on the way.
Empty, absent or unparseable -> `None`. The grammar of the message allows a sign, a leading or
trailing point and an exponent.

### Function dec0

```rust
pub fn dec0(d: &Option<Decimal>) -> Decimal
```
Like `dec` but 0 for absent.

### Function default_limits

```rust
pub fn default_limits() -> BrokerLimits
```
The bundled limits, ready for `BrokerLimiter::new`.

**Panics**

Never in a correct build: the bundled file is checked by a test.

### Function echo_keys

```rust
pub fn echo_keys(os: &OrderState) -> (String, String)
```
`(client_order_id, comment)` an order echo carries: from `order`, or for an SL/TP order from
`sltp_order`. This is where the cid is read back.

### Function floor_m10

```rust
pub fn floor_m10(ts: UnixNanos) -> UnixNanos
```
Start of the 10-minute interval that contains `ts`.

### Function foreign_cid

```rust
pub fn foreign_cid(order_id: &str) -> ClientOrderId
```
Stable cid for an order that is not ours.

A UUID v5 of the Finam order id, so the same foreign order always gets the same cid and it cannot
collide with the v4 ids this library makes.

### Function instrument_from_symbol

```rust
pub fn instrument_from_symbol(symbol: &str) -> InstrumentId
```
Instrument id from a Finam symbol (`SBER@MISX`). A symbol without `@` gets an empty MIC.

### Function is_active

```rust
pub fn is_active(status: i32) -> bool
```
Order is still working at the broker (unknown statuses count as working, never hidden).

### Function is_stop

```rust
pub fn is_stop(os: &OrderState) -> bool
```
A Finam order that is a stop order: type STOP / STOP_LIMIT, or an SL/TP order.

### Function kind_from_type

```rust
pub fn kind_from_type(t: &str) -> InstrumentKind
```
Finam’s `type` strings are not enumerated in the docs; matched by substring.

### Function map_account

```rust
pub fn map_account(resp: &GetAccountResponse, now: Instant) -> Portfolio
```
`GetAccount` answer -> portfolio: one balance per currency of `cash` and one position per
holding. Quantity is signed: a short position is negative. `average_price` is `None` when
Finam does not send it (futures positions).

### Function map_asset

```rust
pub fn map_asset(a: &Asset) -> Instrument
```
A directory entry (`Assets`, `AllAssets`) -> core instrument.

The list carries no currency, so `currency` is empty. Use `get` or `trading_params` for it.

### Function map_bar

```rust
pub fn map_bar(
    instrument: &InstrumentId,
    tf: Timeframe,
    b: &Bar,
    now: Instant,
) -> Candle
```
`Bar` -> core candle. Absent prices and volume become 0. `close_ts` is computed from the open time
and the timeframe (month and longer frames follow the calendar), because the bar carries only
the open time.

### Function map_command_err

```rust
pub fn map_command_err(err: &FinamError) -> Error
```
Errors of `place` / `cancel`: anything that may have reached the broker is `Unknown`
, never `Transport` or `Rejected`.

Only a status where the server certainly did not process the request (see
[`FinamError::is_definite_rejection`]) is mapped as in `map_protocol_err`. A dropped connection or
a timeout is [`Error::Unknown`]: resolve it by asking for the order state, never by sending the
command again.

### Function map_get_asset

```rust
pub fn map_get_asset(symbol: &str, a: &GetAssetResponse) -> Instrument
```
A `GetAsset` answer -> core instrument. `symbol` is the `SYMBOL@MIC` that was asked for.

### Function map_order_state

```rust
pub fn map_order_state(
    os: &OrderState,
    cid: ClientOrderId,
    now: Instant,
) -> OrderSnapshot
```
Core snapshot of a Finam order. `cid` is resolved by the caller (see `OrderIndex`).

### Function map_protocol_err

```rust
pub fn map_protocol_err(err: &FinamError) -> Error
```
Errors of queries and subscriptions. Commands use `map_command_err`.

`RESOURCE_EXHAUSTED` becomes [`Error::RateLimited`] with a fixed `retry_after` of one second. Finam
documents the quota (200 requests per minute) but not the wait, so the pause is a guess, not
verified against the live API.

### Function map_quote

```rust
pub fn map_quote(instrument: &InstrumentId, q: &Quote, now: Instant) -> Quote
```
`Quote` -> core quote. Finam sends 0 for a missing side.

### Function map_rest_order_book

```rust
pub fn map_rest_order_book(
    instrument: &InstrumentId,
    resp: &OrderBookResponse,
    depth: u32,
    now: Instant,
) -> OrderBook
```
Unary `OrderBook` response -> snapshot.

### Function map_schedule

```rust
pub fn map_schedule(
    symbol: &str,
    resp: &ScheduleResponse,
    from: UnixNanos,
    to: UnixNanos,
    now: Instant,
) -> Schedule
```
A `Schedule` answer -> core schedule for the range `from..to`.

Sessions of the closed type are dropped, and so are sessions that do not overlap the range.
`symbol` is stored as the schedule’s `exchange` field, because Finam schedules are per instrument.

### Function map_status

```rust
pub fn map_status(
    status: i32,
    cid: ClientOrderId,
    broker_id: &str,
    filled: Qty,
    left: Qty,
    description: &str,
) -> OrderState
```
Finam status -> core state. `filled`/`left` come from executed/remaining quantity.

### Function map_trade

```rust
pub fn map_trade(
    instrument: &InstrumentId,
    t: &Trade,
    now: Instant,
) -> Option<MarketTrade>
```
`Trade` -> market trade. A trade without a side cannot be expressed (core `Side` is
mandatory), so it is dropped by returning `None`.

### Function map_trading_params

```rust
pub fn map_trading_params(symbol: &str, a: &GetAssetResponse) -> TradingParams
```
Price step = `min_step / 10^decimals`. Finam does not give the money value of a step, so
`price_step_value` equals `price_step` (right for stocks, not for futures). `min_qty` is one
lot because orders are in pieces and sized in whole lots.

### Function merge_m5

```rust
pub fn merge_m5(m5: &[Candle]) -> Vec<Candle>
```
History: M5 candles (in any order) -> M10 candles, oldest first.

### Function money

```rust
pub fn money(m: &Money) -> (String, Money)
```
`google.type.Money` -> (currency, amount), exactly. `units` and `nanos` carry the sign of the value.

### Function no_instrument

```rust
pub fn no_instrument() -> InstrumentId
```
Placeholder instrument for subscriptions that are not about one instrument (orders, account).

### Function now_nanos

```rust
pub fn now_nanos() -> UnixNanos
```
Wall clock now, in nanoseconds since the Unix epoch. Gives 0 when the clock is before the epoch.

### Function order_instrument

```rust
pub fn order_instrument(os: &OrderState) -> InstrumentId
```
Instrument of an order echo.

### Function order_type_from_pb

```rust
pub fn order_type_from_pb(t: i32) -> OrderType
```
Stop and multi-leg orders have no core equivalent: shown as the nearest plain type.

### Function parse_limits

```rust
pub fn parse_limits(raw: &str) -> Result<BrokerLimits, Error>
```
Parses the `[finam]` section of a limits file through `BrokerLimits::from_toml_section`.

Use it to load your own limits, for example when the broker raises its quota.

**Errors**

[`Error::Config`] when the text is not valid TOML or has no `[finam]` section. A section of
another broker is not accepted.

### Function price

```rust
pub fn price(d: &Option<Decimal>) -> Option<Price>
```
`dec` as a [`Price`].

### Function price0

```rust
pub fn price0(d: &Option<Decimal>) -> Price
```
`dec0` as a [`Price`].

### Function qty

```rust
pub fn qty(d: &Option<Decimal>) -> Option<Qty>
```
`dec` as a [`Qty`].

### Function qty0

```rust
pub fn qty0(d: &Option<Decimal>) -> Qty
```
`dec0` as a [`Qty`].

### Function rejection_of

```rust
pub fn rejection_of(os: &OrderState) -> Option<Error>
```
Rejection coming back as an order status in the `PlaceOrder` response.

### Function rest_rows

```rust
pub fn rest_rows(rows: &[Row]) -> Vec<RowView>
```
Rows of a unary `OrderBook` answer in the neutral form. Rows with no side or no price are skipped.

### Function run_pump

```rust
pub async fn run_pump<F, H>(
    io: PumpIo<F>,
    handle: H,
    on_overflow: Arc<dyn Fn(SubscriptionId) + Send + Sync>,
)where
    F: Send + 'static,
    H: FnMut(F, Instant) -> Vec<CoreEvent> + Send,
```
Runs until cancelled, the frame channel closes, or the consumer drops the receiver.

`handle` turns one frame into core events (it owns the core, so parsing happens right here in
the task that received the frame). `on_overflow` is called once when a `Lossless` buffer
overflows. The user data connector uses it to mark itself degraded.

Delivery order: control events (`Resync`, `Overflow`) first, then buffered data, one
subscription after another. A `Resync` does not overtake the data of its own subscription that is
already buffered: that data goes out just before it. The pump never waits for a slow consumer inside the core: events wait
in the subscription’s buffer, and what the buffer’s policy says happens when it is full (merge for
`Conflate`, overflow for `Lossless`).

When the pump is cancelled it makes one last pass and hands over what fits into `out` without
waiting. The rest is dropped.

### Function session_kind

```rust
pub fn session_kind(t: &str) -> SessionKind
```
Session type heuristics: the docs do not list the `type` values.

### Function side_from_pb

```rust
pub fn side_from_pb(s: i32) -> Side
```
Finam side -> core side. An unspecified side reads as `Buy`: check the source of a value before
you trust it on an order that is not yours.

### Function snapshot

```rust
pub fn snapshot(
    os: &OrderState,
    cid: Option<ClientOrderId>,
    now: Instant,
) -> StopOrderSnapshot
```
Core snapshot of a Finam stop order. `cid` is resolved by the caller (`OrderIndex::lookup`).

### Function spawn_stream

```rust
pub fn spawn_stream<M, F>(
    params: StreamParams<M, F>,
    cancel: CancellationToken,
) -> StreamHandlewhere
    M: Send + 'static,
    F: Send + 'static,
```
Starts the supervisor task. `cancel` should be a child of the connector’s root token.

Must be called inside a Tokio runtime. See the module docs for a complete example.

### Function state_of

```rust
pub fn state_of(status: i32) -> StopOrderState
```
Lifecycle of a stop order from the Finam status. The names come from the proto; how Finam really
walks through them is not verified against the live API.

### Function stream_rows

```rust
pub fn stream_rows(rows: &[Row]) -> Vec<RowView>
```
Rows of an order book stream message in the neutral form. Rows with no side or no price are skipped.

### Function symbol_of

```rust
pub fn symbol_of(id: &InstrumentId) -> Result<String, Error>
```
`SBER@MISX` for the Finam API. Needs `NativeId::SymbolMic`, no network.

### Function timeframe_to_pb

```rust
pub fn timeframe_to_pb(tf: Timeframe) -> Result<TimeFrame, Error>
```
Core timeframe -> Finam. M10 has no Finam equivalent (see `wire_timeframe`).

### Function timestamps

```rust
pub fn timestamps(broker: Option<&Timestamp>, rx_mono: Instant) -> Timestamps
```
Receive timestamps with the broker time attached when present.

### Function to_decimal

```rust
pub fn to_decimal(v: Decimal) -> Decimal
```
A number -> `google.type.Decimal`, exactly. The text is plain (Rust never writes an exponent for
a decimal), the scale is kept: `244.30` is sent as `"244.30"`.

### Function to_money

```rust
pub fn to_money(currency: &str, amount: Money) -> Option<Money>
```
(currency, amount) -> `google.type.Money`, exactly. The reverse of `money`.

`None` when the amount has more than 9 decimal places (`nanos` counts billionths) or its integer
part does not fit in an `i64`: the amount is never rounded in silence. `units` and `nanos` carry the
sign of the amount.

### Function to_timestamp

```rust
pub fn to_timestamp(n: UnixNanos) -> Timestamp
```
Unix nanoseconds -> protobuf timestamp. Negative values give `seconds < 0` and `nanos >= 0`, the
protobuf way.

### Function transport

```rust
pub fn transport(msg: impl Into<String>) -> Error
```
A [`Error::Transport`] with a text. For failures of this process (a broken stream, an unknown
subscription), not for answers of the broker.

### Function ts_nanos

```rust
pub fn ts_nanos(ts: &Timestamp) -> UnixNanos
```
Protobuf timestamp -> Unix nanoseconds. Saturates instead of overflowing on absurd values.

### Function unary

```rust
pub async fn unary<T, F, Fut>(
    tokens: &Arc<TokenManager>,
    timeout: Duration,
    retry_unauth: bool,
    f: F,
) -> Result<T, FinamError>where
    F: Fn() -> Fut,
    Fut: Future<Output = Result<Response<T>, Status>>,
```
Runs `f` with a deadline. On `UNAUTHENTICATED` and `retry_unauth` renews the JWT and calls
once more. Never set `retry_unauth` for commands (no automatic retry of trading
commands).

A call that does not answer within `timeout` ends as status `DEADLINE_EXCEEDED`. The request may
still be running at the broker, which is why a command with this result is `Unknown`.

**Errors**

[`FinamError::Status`] with the code of the server, or `DEADLINE_EXCEEDED` on a timeout. When the
renewal itself fails, its error is returned instead of the second call.

### Function unsupported

```rust
pub fn unsupported(capability: &'static str) -> Error
```
An [`Error::Unsupported`] naming the capability the broker does not offer (for example `replace_order`).

### Function wire_timeframe

```rust
pub fn wire_timeframe(tf: Timeframe) -> Result<TimeFrame, Error>
```
Timeframe asked from Finam for a core timeframe: the same one, except M10, which is built from M5
candles (`map::m10`).

## Крейт brokapi_alor


### Struct AlorCidCodec

```rust
pub struct AlorCidCodec;
```
The cid is written into the `comment` of the order as a hyphenated UUID (lossless, 128-bit). The broker
returns the comment in the order list, in the order stream and in the trades, which is how a cid comes back.
The same text is the `X-REQID` of the command, so a repeated command carries the same id.

### Struct AlorRaw

```rust
pub struct AlorRaw { /* private fields */ }
```
A handle for calling any ALOR API method with a connector’s token and rate limiter.

Get it from `raw()` on `MarketDataConnector` or
`UserDataConnector`. It is cheap to clone, and all clones share the token and the
limiter. See the module docs for the rules of a call.

- `pub async fn call<T, F, Fut>(&self, method: &str, f: F) -> Result<T, Error>where F: FnOnce(AlorHttpClient) -> Fut, Fut: Future<Output = Result<T, AlorApiError>>,`
  Calls a read-only API method: takes the quota for `method`, then runs `f`.

  `method` is the name of the quota route, not a URL. Take a name from `limits.toml` (for example
  `GetOrders`). An unknown name spends the default route `"*"`. `f` receives the connector’s
  [`AlorHttpClient`] and returns the future of one API call. Do not use this for anything that changes the
  account, use `call_command`.

  Errors

  - [`Error::RateLimited`] when the quota is spent (nothing was sent), or the broker answers HTTP 429.

  - [`Error::Rejected`] with code `not_found` for HTTP 404.

  - [`Error::Transport`] for everything else: no connection, a timeout, an unreadable answer, any other HTTP
  status. A read can be repeated.

  - [`Error::Config`] when the limits file routes the name to a bucket that does not exist, or the broker
  refuses the token.

  The full table is in `crate::error::map_err`.

- `pub async fn call_command<T, F, Fut>( &self, method: &str, f: F, ) -> Result<T, Error>where F: FnOnce(AlorHttpClient) -> Fut, Fut: Future<Output = Result<T, AlorApiError>>,`
  Calls an API method that changes the account: takes the quota for `method`, then runs `f`.

  Like `call`, but anything that may have reached the broker is [`Error::Unknown`]. The
  library never repeats the call: a retry of a lost place request could create a second order. Find out
  what happened (read the order, run `reconcile`) before you decide to send it again. The order is not
  tracked by the connector (see the module docs).

  Errors

  - [`Error::Unknown`] when there was no answer, a break or a timeout, HTTP 500 or above, or an answer that
  cannot be read. The command may have been done.

  - [`Error::Rejected`] when the broker answered and said no: code `http_<status>` for a status from 400 to
  499 except 429, `not_found` for 404. Nothing was done.

  - [`Error::RateLimited`] when the quota is spent (nothing was sent) or the broker answers HTTP 429.

  - [`Error::Config`] when the limits file routes the name to a bucket that does not exist.

  The full table is in `crate::error::map_command_err`.

  Examples

  An order sent through the raw layer, refused by the broker (the mock refuses a limit order at
  `REJECT_PRICE`). The refusal is a definite [`Error::Rejected`], not an unknown outcome:

- `pub fn connect_ws( &self, event_tx: AsyncSender<WsIncoming>, options: WsOptions, ) -> Result<WsSession, Error>`
  Opens one more session of the data channel `/ws` (for the subscriptions the connectors do not make:
  risks, summaries, instruments, …) with the connector’s token.

  The quota name is `ConnectWs`. The session reconnects and resubscribes on its own. Frames arrive on
  `event_tx`, parsed. The returned [`WsSession`] connects in the background: it does not wait for the
  connection. Read the receiver of `event_tx`, or the session drops frames when the channel is full (see
  `WsSession::dropped_events`). The broker allows 10 connections of a user.

  Errors

  - [`Error::RateLimited`] when the quota `ConnectWs` is spent. No socket is opened.

  - [`Error::Config`] when the limits file routes the name to a bucket that does not exist.

- `pub async fn connect_cws( &self, answer_timeout: Duration, ) -> Result<CwsSession, Error>`
  Opens the trading channel `/cws` with the connector’s token and authorizes it. `answer_timeout` is how
  long a command waits for its answer.

  The quota name is `ConnectCws`. The channel gives the order commands that HTTP gives; the connectors use
  HTTP. See [`CwsSession`].

  Errors

  - [`Error::RateLimited`] when the quota `ConnectCws` is spent.

  - [`Error::Transport`] when the connection or the authorization fails.

### Type Alias BoardBook

```rust
pub struct BoardBook { /* private fields */ }
```
The boards of the instruments of a portfolio, by `EXCHANGE:TICKER`. The positions of the broker do not name
the board of an instrument, so the connector looks it up and keeps it here, where the core reads it.

### Constant DEFAULT_LIMITS_TOML

```rust
pub const DEFAULT_LIMITS_TOML: &str = "# ALOR Broker API rate limits for brokapi-alor.\n#\n# Source: the article \"\u{41e}\u{433}\u{440}\u{430}\u{43d}\u{438}\u{447}\u{435}\u{43d}\u{438}\u{44f} \u{438} \u{440}\u{435}\u{43a}\u{43e}\u{43c}\u{435}\u{43d}\u{434}\u{430}\u{446}\u{438}\u{438}\" and the page \"HTTP API\" of the ALOR documentation: up to 100\n# HTTP requests a second per user, up to 10 WebSocket connections, up to 5000 subscriptions on a connection, up to\n# 5000 unprocessed messages in the buffer of a connection. The articles give no other numbers.\n#\n# What is NOT published and not verified against a live account: the social rating (the broker counts \"points\" for\n# actions, blocks a login for 2 hours when the limit is reached, and does not say the limit; a request for an order\n# that does not exist costs 100 points and a request that is not valid JSON costs 5000, a WebSocket connection and a\n# subscription cost 1), and on the futures market the counters of errors that the exchange fines. The limiter below\n# only keeps the plain request rate. Whether \"per user\" means per login (all portfolios of it) is read from the\n# article on the rating (\"counters are bound to the login\"), so one limiter should serve every connector of one\n# login, and the daily counters of the broker are not visible to it.\n#\n# One bucket of 100 a second for every HTTP method (the broker does not split them), a smaller soft bucket for\n# placements so that a flood of new orders can never use all of the budget and leave no room for a cancel.\n\n[alor.buckets.http]\n# All HTTP requests of one user: 100 a second.\nrate = 100\nper = \"1s\"\nburst = 100\n\n[alor.buckets.place]\n# Not a limit of the broker: placements (and replacements, stop orders) go through this bucket too, so that they\n# can never use all of the 100 a second. At least 40 a second are left for cancels and reads.\nrate = 60\nper = \"1s\"\nburst = 30\n\n[alor.route]\n# \"*\" = any method with no route below (raw() calls). It spends the plain budget, so a call is never free.\n\"*\" = [\"http\"]\nPlaceOrder = [\"http\", \"place\"]\nReplaceOrder = [\"http\", \"place\"]\nPlaceStopOrder = [\"http\", \"place\"]\nCancelOrder = [\"http\"]\nCancelStopOrder = [\"http\"]\nGetOrder = [\"http\"]\nGetOrders = [\"http\"]\nGetStopOrders = [\"http\"]\nGetTrades = [\"http\"]\nGetPositions = [\"http\"]\nGetSummary = [\"http\"]\nGetOrderBook = [\"http\"]\nGetLastTrades = [\"http\"]\nGetCandles = [\"http\"]\nGetSecurity = [\"http\"]\nGetSecurities = [\"http\"]\nGetTradingStatus = [\"http\"]\nReconcile = [\"http\"]\n\n[alor.conns]\n# The data channel `/ws` carries every subscription of a connector (market data and the user\'s own streams).\nmax_conns = 10\nmax_subs_per_conn = 5000\nsupports_multiplex = true\n";
```
The text of the bundled limits file `limits.toml`, compiled into the crate.

Parse it with `limits_from_toml_str`, or take the result directly from `default_limits`.

### Constant GTC_HORIZON_SECS

```rust
pub const GTC_HORIZON_SECS: i64 = _; // 31_536_000i64
```
The stop orders of ALOR end at a given time; a stop order that is “good till cancel” gets this long.

### Struct MarketDataConfig

```rust
pub struct MarketDataConfig {
    pub alor: AlorConfig,
    pub source: Arc<str>,
    pub reconnect_min: Duration,
    pub reconnect_max: Duration,
    pub subscribe_ack_timeout: Duration,
}
```
Settings of `MarketDataConnector`.

- `alor: AlorConfig`
  Client settings of the ALOR API: refresh token, contour, timeouts, endpoints.

- `source: Arc<str>`
  Envelope source label, e.g. `"alor-md"`.

- `reconnect_min: Duration`
  Pause before the first reconnect of the WebSocket, doubling up to `reconnect_max` (default 0.5 s, as at
  the other brokers).

- `reconnect_max: Duration`
  Upper bound of the pause (default 30 s). The pause returns to `reconnect_min` once a connection has
  lived 10 seconds.

- `subscribe_ack_timeout: Duration`
  How long a `subscribe_*` call waits for the broker to confirm the subscription (default 3 s). The call
  returns when the broker has confirmed: what happens after that is delivered. A refusal is returned as
  `Rejected`. If nothing comes in time, the call fails with `Transport` and the subscription is taken back, so
  the call can be repeated.

- `pub fn new(alor: AlorConfig, source: impl Into<Arc<str>>) -> Self`
  Creates a config with the default reconnect pauses (0.5 s up to 30 s).

  `source` is the label put in every `Envelope` this connector emits, so a bot
  with several connectors can tell them apart.

### Struct MarketDataConnector

```rust
pub struct MarketDataConnector { /* private fields */ }
```
The market data connector of ALOR: implements [`OrderBookSource`], [`MarketDataSource`] and
[`InstrumentSource`]. It is not tied to an account.

Start it with `MarketDataConnector::start`; commands return their answer directly and subscriptions deliver
[`Envelope`]s into the channel given at start. The data comes over one WebSocket session that reconnects on its
own and restores the subscriptions (an order book is then marked with `Event::Resync`).

An instrument is named by `NativeId::TickerClass { ticker, class_code }`, where the class code is the trading
mode (the board, for example `TQBR`). The exchange follows from it: the board `SPBX` is the exchange `SPBX`,
every other board is `MOEX`.

- `pub async fn start( cfg: MarketDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Starts the connector: gets the first access token (so a refused token is an error here, not at the first
  call) and opens the WebSocket session in the background.

  Errors

  [`Error::Config`] when the client cannot be built or the broker refuses the token; [`Error::Transport`]
  when the authorization server cannot be reached.

- `pub fn raw(&self) -> AlorRaw`
  Direct access to any API method with this connector’s token and rate limiter. See `AlorRaw`.

- `pub fn caps(&self) -> MarketDataCaps`
  What the connector can do: books of up to 50 levels (always whole books), all four streams.

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Stops the connector. Unless the mode is `Immediate`, it cancels the subscriptions at the broker first;
  `Graceful` also lets the bot’s channel take what is still buffered.

### Struct MdCore

```rust
pub struct MdCore { /* private fields */ }
```
The sans-io core of the market data connector.

- `pub fn new() -> Self`
  An empty core.

- `pub fn notifier(&self) -> Arc<Notify> ⓘ`
  Woken whenever a subscription changes state (confirmed, refused, or data came). The connector waits on it
  for the confirmation of a new subscription.

- `pub fn desired(&self) -> &SubscriptionSet`
  The subscriptions the core was told about, as a set.

- `pub fn register( &mut self, guid: &str, id: SubscriptionId, kind: SubscriptionKind, )`
  Registers a subscription: frames with this `guid` belong to `id` and are read as `kind`. The connector
  calls it before the request is sent, so the confirmation cannot overtake it.

- `pub fn forget(&mut self, guid: &str)`
  Forgets a subscription (the data that still arrives for its `guid` is dropped).

- `pub fn actual_state(&self, id: SubscriptionId) -> Option<SubState>`
  The state of a subscription.

- `pub fn failure(&self, id: SubscriptionId) -> Option<&str>`
  The reason the broker gave when it refused a subscription.

### Enum MdSubKind

```rust
pub enum MdSubKind {
    OrderBook,
    MarketTrades,
    Quotes,
    Candles,
}
```
What a market data subscription delivers.

- `OrderBook`
  Order book snapshots of one instrument.

- `MarketTrades`
  Trades of the market (the tape) of one instrument.

- `Quotes`
  Best bid and ask of one instrument.

- `Candles`
  The latest candle of one instrument and timeframe.

### Struct MdSubscription

```rust
pub struct MdSubscription {
    pub id: SubscriptionId,
    pub instrument: InstrumentId,
    pub kind: MdSubKind,
    pub opts: SubscribeOpts,
    pub guid: String,
}
```
One live market data subscription, as the connector remembers it.

- `id: SubscriptionId`
  The id the bot got from `subscribe_*`.

- `instrument: InstrumentId`
  The instrument the data is about.

- `kind: MdSubKind`
  What the subscription delivers.

- `opts: SubscribeOpts`
  The options given at subscribe time (backpressure policy, depth).

- `guid: String`
  The `guid` of the WebSocket subscription: it names the data frames and the `unsubscribe`.

### Struct SubIdGen

```rust
pub struct SubIdGen(/* private fields */);
```
Hands out subscription ids of one connector, in increasing order, from a thread-safe counter.

The market data connector counts from 1, the user data connector from 1001, so the ids of the two
connectors of one bot do not collide.

- `pub fn new(start: u64) -> Self`
  Creates a generator whose first id is `start + 1`.

- `pub fn next(&self) -> SubscriptionId`
  Returns the next id. Safe to call from several tasks.

### Enum SubState

```rust
pub enum SubState {
    Pending,
    Active,
    Failed,
}
```
Where a subscription is in its life.

- `Pending`
  The request was sent, the broker has not confirmed it.

- `Active`
  The broker confirmed it, or data came.

- `Failed`
  The broker refused it.

### Struct UdCore

```rust
pub struct UdCore { /* private fields */ }
```
The sans-io core of the user data connector.

- `pub fn new(exchange: impl Into<String>, boards: BoardBook) -> Self`
  An empty core for the exchange of the portfolio, with the book of boards it reads positions from.

- `pub fn notifier(&self) -> Arc<Notify> ⓘ`
  Woken whenever a subscription changes state. The connector waits on it for the confirmation of a new
  subscription.

- `pub fn actual_state(&self, id: SubscriptionId) -> Option<SubState>`
  The state of a subscription.

- `pub fn failure(&self, id: SubscriptionId) -> Option<&str>`
  The reason the broker gave when it refused a subscription.

- `pub fn register(&mut self, guid: &str, id: SubscriptionId, kind: UdSubKind)`
  Registers a subscription: frames with this `guid` belong to `id` and are read as `kind`.

- `pub fn forget(&mut self, guid: &str)`
  Forgets a subscription.

- `pub fn learn_order_id(&mut self, order_id: &str, cid: ClientOrderId)`
  Remembers that the order with this number is the order of `cid`.

- `pub fn cid_of_order_id(&self, order_id: &str) -> Option<ClientOrderId>`
  The cid of the order with this number, if the core has seen it.

- `pub fn resolve_cid( &mut self, order_id: &str, comment: Option<&str>, ) -> ClientOrderId`
  The cid of an order of the broker: the one in its comment, else the one learned for its number, else a
  stable made-up one (the order is not ours).

### Enum UdSubKind

```rust
pub enum UdSubKind {
    OrderState,
    Portfolio,
    Executions,
}
```
What a user data subscription delivers.

- `OrderState`
  Status changes of orders.

- `Portfolio`
  Portfolio updates.

- `Executions`
  Fills of orders.

### Struct UdSubscription

```rust
pub struct UdSubscription {
    pub id: SubscriptionId,
    pub kind: UdSubKind,
    pub opts: SubscribeOpts,
    pub guid: String,
}
```
One live user data subscription, as the connector remembers it.

- `id: SubscriptionId`
  The id the bot got from `subscribe_*`.

- `kind: UdSubKind`
  What the subscription delivers.

- `opts: SubscribeOpts`
  The options given at subscribe time. Order state and executions use a lossless buffer.

- `guid: String`
  The `guid` of the WebSocket subscription.

### Struct UserDataConfig

```rust
pub struct UserDataConfig {
    pub alor: AlorConfig,
    pub source: Arc<str>,
    pub order_state_buffer: usize,
    pub reconnect_min: Duration,
    pub reconnect_max: Duration,
    pub subscribe_ack_timeout: Duration,
    pub price_grid: PriceGrid,
}
```
Settings of `UserDataConnector`.

The portfolio and the exchange come from the `alor` config: see
`AlorConfig::portfolio`.

- `alor: AlorConfig`
  Client settings of the ALOR API: refresh token, contour, timeouts, endpoints, portfolio, exchange.

- `source: Arc<str>`
  Envelope source label, e.g. `"alor-ud"`.

- `order_state_buffer: usize`
  Capacity of the lossless buffer of each order state and execution subscription (default 1024 events).
  When it is full, the subscription reports an overflow and the connector becomes degraded: commands are
  refused until the next successful `reconcile`.

- `reconnect_min: Duration`
  Pause before the first reconnect of the WebSocket, doubling up to `reconnect_max` (default 0.5 s).

- `reconnect_max: Duration`
  Upper bound of the pause (default 30 s).

- `subscribe_ack_timeout: Duration`
  How long a `subscribe_*` call waits for the broker to confirm the subscription (default 3 s), see
  `MarketDataConfig::subscribe_ack_timeout`.

- `price_grid: PriceGrid`
  Price steps and lot sizes the connector checks orders against (default: empty, only the lot sizes the
  connector reads from the broker). Fill it with [`PriceGrid::learn`] from the trading parameters of the
  instruments you trade. A price off the step is then refused with `Rejected` (`price_off_step`) before
  anything is sent.

- `pub fn new(alor: AlorConfig, source: impl Into<Arc<str>>) -> Self`
  Creates a config with the default buffer (1024 events) and the default reconnect pauses.

  `source` is the label put in every `Envelope` this connector emits.

### Struct UserDataConnector

```rust
pub struct UserDataConnector { /* private fields */ }
```
The user data connector of ALOR for one portfolio: implements [`OrderExecutor`], [`StopOrderExecutor`],
[`ExecutionSource`] and [`PortfolioSource`].

Commands (place, cancel, replace) go by HTTP, one request each, never repeated: a lost answer is
[`Error::Unknown`]. The streams (own orders, fills, positions) come over one WebSocket session that
reconnects on its own. Quantities of the shared interface are pieces; the broker counts in lots, so the
connector reads the lot size of an instrument once and converts both ways.

The portfolio and the exchange come from the config (`ALOR_PORTFOLIO`, `ALOR_EXCHANGE`). An order for an
instrument of another exchange is refused with [`Error::Config`]: start a second connector for it.

The `cid` goes to the broker twice: as `X-REQID` of the command, and as the comment of the order. The comment
is how it comes back in the order list, the stream and the trades.

- `pub async fn start( cfg: UserDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Starts the connector: gets the first access token, finds the portfolio, and opens the WebSocket session in
  the background. Commands wait for the first `reconcile`.

  Errors

  [`Error::Config`] when the client cannot be built, the broker refuses the token, or no single portfolio
  can be told (the config names none and the token names several or none); [`Error::Transport`] when the
  authorization server cannot be reached.

- `pub fn raw(&self) -> AlorRaw`
  Direct access to any API method with this connector’s token and rate limiter. See `AlorRaw`.

- `pub fn portfolio_id(&self) -> &str`
  The portfolio this connector works with.

- `pub fn price_grid(&self) -> &PriceGrid`
  The price steps and lot sizes the connector checks orders against. Fill it with `learn` before you trade.

- `pub fn caps(&self) -> UserDataCaps`
  What the connector can do.

  - `place_idempotency`: `LabelOnly`. The broker’s documentation says a repeated `X-REQID` gets a copy of the
  first answer instead of a second order, but that is not checked on a live account and the window is
  unknown. The library never repeats a place whatever the value.

  - `supports_replace` and `supports_stop_orders`: yes. `streams_executions`: yes.

  - `ws_rpc` and `ordered_command_channel`: no (commands go by HTTP). `sandbox`: no (the test contour is a
  contour of its own, chosen in the config).

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Stops the connector. Unless the mode is `Immediate` it closes the trading gate first; `Graceful` also lets
  the bot’s channel take what is still buffered. Orders at the exchange are not touched.

### Function availability_of

```rust
pub fn availability_of(code: i32) -> TradingAvailability
```
The availability for a trading status code of the broker.

### Function balance

```rust
pub fn balance(r: &PositionRecord, now: Instant) -> Balance
```
The balance of a money position: the amount of the currency named by the symbol. The broker reports the
amount in the quantity; it does not split a part blocked by orders, so `blocked` is zero.

### Function board_key

```rust
pub fn board_key(exchange: &str, ticker: &str) -> String
```
The key of an instrument in a `BoardBook`.

### Function board_of

```rust
pub fn board_of(info: &SecurityInfo) -> String
```
The board an entry is about: the board of the entry, else the main one of the instrument.

### Function build

```rust
pub fn build(
    req: &PlaceStopOrderRequest,
    lot: Decimal,
    portfolio: &str,
    exchange: &str,
    now_secs: i64,
    cid: ClientOrderId,
) -> Result<(StopOrderRequest, bool), Error>
```
The wire request and whether it is a stop-limit order (`true`) or a stop order (`false`).

**Errors**

[`Error::Rejected`] with `missing_price` for a `StopLimit` without `limit_price`; `invalid_qty` for a quantity
that is not a whole number of lots; [`Error::Config`] for an instrument that is not a `TickerClass` one.

### Function candle

```rust
pub fn candle(
    bar: &Bar,
    instrument: &InstrumentId,
    tf: Timeframe,
    now: Instant,
) -> Candle
```
One candle of the timeframe `tf`. The broker gives the open time; the close is the open plus the length of the
timeframe.

### Function candles

```rust
pub fn candles(
    resp: &HistoryResponse,
    instrument: &InstrumentId,
    tf: Timeframe,
    now: Instant,
) -> Vec<Candle>
```
The candles of a history answer, oldest first.

### Function cid_from_comment

```rust
pub fn cid_from_comment(comment: Option<&str>) -> Option<ClientOrderId>
```
The cid of an order that carries none of ours (placed in the app of the broker, or by another program).

### Function cid_of

```rust
pub fn cid_of(r: &StopOrderRecord) -> Option<ClientOrderId>
```
The cid in the comment of a stop order record, if it is ours.

### Function condition

```rust
pub fn condition(kind: StopOrderKind, side: Side) -> &'static str
```
The trigger condition for the side and the kind.

### Function default_limits

```rust
pub fn default_limits() -> BrokerLimits
```
The bundled ALOR limits, parsed.

A method with no route of its own spends the default route `"*"`, so no call is free. Placing goes through a
soft bucket of its own that cancel does not pass.

**Panics**

Never in practice: the bundled file is checked by a test of this crate.

### Function drain_backlog

```rust
pub async fn drain_backlog(emitter: &Emitter, deadline: Duration)
```
Gives the bot’s channel time to take what the buffers still hold (shutdown).

### Function end_time

```rust
pub fn end_time(expiry: StopExpiry, now_secs: i64) -> i64
```
The end of the stop order, Unix seconds, from the expiry. `now_secs` is the time now.

### Function exchange_of_board

```rust
pub fn exchange_of_board(board: &str) -> &'static str
```
The exchange an instrument trades on: `SPBX` for the board `SPBX`, `MOEX` for every other board.

### Function execution

```rust
pub fn execution(
    r: &TradeRecord,
    cid: Option<ClientOrderId>,
    now: Instant,
) -> Option<Execution>
```
A fill. `cid` is the client order id the connector found for the order of the trade (`None` for an order that
is not ours). `None` if the record has no pieces quantity or no side.

### Function foreign_cid

```rust
pub fn foreign_cid(order_id: &str) -> ClientOrderId
```
Stable cid for an order of this account that this connector did not place: the same order number always
gives the same cid, so it can be told apart from ours and found again.

### Function id_of

```rust
pub fn id_of(info: &SecurityInfo) -> InstrumentId
```
The id of the instrument of an entry.

### Function instrument

```rust
pub fn instrument(info: &SecurityInfo) -> Instrument
```
An instrument of the reference data.

### Function instrument_id

```rust
pub fn instrument_id(ticker: &str, board: &str) -> InstrumentId
```
The id of an instrument by ticker and board.

### Function is_money

```rust
pub fn is_money(r: &PositionRecord) -> bool
```
A money position (`isCurrency`) is a balance, not a position.

### Function kind_of

```rust
pub fn kind_of(info: &SecurityInfo) -> InstrumentKind
```
The kind of an instrument, from its type, its market and the CFI code.

### Function limits_from_toml_path

```rust
pub fn limits_from_toml_path(path: &str) -> Result<BrokerLimits, ConfigError>
```
Reads the `[alor]` section of a limits file on disk, to tune the limits without recompiling.

**Errors**

[`brokapi_core::ConfigError::Message`] when the file cannot be read (the message names the path), and for
every reason `limits_from_toml_str` lists.

### Function limits_from_toml_str

```rust
pub fn limits_from_toml_str(raw: &str) -> Result<BrokerLimits, ConfigError>
```
Reads the `[alor]` section of a limits file given as text.

Other sections of the file (other brokers) are ignored.

**Errors**

[`brokapi_core::ConfigError::Message`] when the text is not valid TOML, when there is no `[alor]` section, or
when the section is not a valid set of limits (see [`BrokerLimits::from_toml_section`]).

### Function lots_to_pieces

```rust
pub fn lots_to_pieces(lots: Decimal, lot: Decimal) -> Qty
```
Lots to pieces.

### Function map_command_err

```rust
pub fn map_command_err(err: AlorApiError) -> Error
```
A command that failed: only answers that prove the broker did not act become definite errors. No answer, a
break, HTTP 5xx or an unreadable answer may have reached the broker: `Error::Unknown`, never retried by the
library.

Protocol errorPublic error
`Http` without a status (no answer, a break, a timeout) or with status 500 and above[`Error::Unknown`]
`Parse` (the answer cannot be read), `Ws`[`Error::Unknown`]
`Http` with status 400 to 499, except 429[`Error::Rejected`] with code `http_<status>` and the broker’s message
everything else (429, `Auth`, `NotFound`, …)as in `map_err`

### Function map_config_err

```rust
pub fn map_config_err(err: AlorApiError) -> Error
```
Maps a failure to build a client from the configuration to [`Error::Config`], with the text of the
original error.

### Function map_err

```rust
pub fn map_err(err: AlorApiError) -> Error
```
Maps a failed read (or a failed step before a command was sent) to a public error.

Protocol errorPublic error
`RateLimitExceeded`, or `Http` with status 429[`Error::RateLimited`] with `retry_after` of 1 second (the broker does not say how long: the value is a fixed guess)
`Auth` (the token is refused)[`Error::Config`] with the broker’s message: fix the token, a repeat will not help
`Subscription`, `Ws`, `Parse`[`Error::Transport`] with the message
`Http` (any other status or no status)[`Error::Transport`] with the message
`NotFound` (HTTP 404)[`Error::Rejected`] with code `not_found` (an unknown instrument or order, as at the other brokers)

All [`Error::Transport`] results are retryable for a read. Do not use this function for a command: it would
turn a lost answer into a plain transport error. Use `map_command_err`.

### Function market_trade

```rust
pub fn market_trade(
    r: &MarketTradeRecord,
    instrument: &InstrumentId,
    now: Instant,
) -> Option<MarketTrade>
```
A trade of the market. The quantity is as the broker reports it.

### Function ms_to_nanos

```rust
pub fn ms_to_nanos(ms: i64) -> UnixNanos
```
Unix milliseconds as [`UnixNanos`].

### Function nanos_to_datetime

```rust
pub fn nanos_to_datetime(nanos: UnixNanos) -> DateTime<Utc>
```
A [`UnixNanos`] as a date.

### Function nanos_to_secs

```rust
pub fn nanos_to_secs(nanos: UnixNanos) -> i64
```
Unix seconds of a [`UnixNanos`].

### Function new_guid

```rust
pub fn new_guid() -> String
```
A new `guid` for a WebSocket subscription.

### Function order_ack

```rust
pub fn order_ack(
    cid: ClientOrderId,
    order_number: &str,
    now: Instant,
) -> OrderAck
```
The ack of a command: the cid and the number of the order the broker made.

### Function order_book

```rust
pub fn order_book(
    book: &OrderBook,
    instrument: &InstrumentId,
    now: Instant,
) -> OrderBook
```
An order book, from the REST answer or a stream message: always a whole snapshot (the broker sends the whole
book, to the depth asked, in every message). The volume is as the broker reports it.

### Function order_snapshot

```rust
pub fn order_snapshot(
    r: &OrderRecord,
    cid: ClientOrderId,
    now: Instant,
) -> Option<OrderSnapshot>
```
A snapshot of an order of the portfolio. `None` if the record has no pieces quantity.

### Function parse_time

```rust
pub fn parse_time(raw: Option<&str>) -> Option<UnixNanos>
```
A time of the broker (UTC text such as `2020-06-16T23:59:59.9990000Z`).

### Function pieces_to_lots

```rust
pub fn pieces_to_lots(qty: Qty, lot: Decimal) -> Result<i64, Error>
```
The number of lots in a quantity of pieces: a positive whole number of lots, else `invalid_qty` (the library
never rounds a quantity).

### Function portfolio

```rust
pub fn portfolio(
    records: &[PositionRecord],
    board_of: impl Fn(&str) -> String,
    now: Instant,
) -> Portfolio
```
The portfolio from the positions of the broker; `board_of` gives the board of a ticker.

### Function position

```rust
pub fn position(r: &PositionRecord, board: &str, now: Instant) -> Position
```
A position in an instrument. `board` is the trading mode of the instrument (the broker does not name it in a
position, the connector looks it up). The quantity is in pieces and signed. The average price is `None` when
the broker gives none or zero (futures).

### Function quote

```rust
pub fn quote(q: &QuoteRecord, instrument: &InstrumentId, now: Instant) -> Quote
```
A quotation: the best bid and ask. The broker gives their volumes in lots; with the lot size of the record
they become pieces. A side with no price (an empty book side) is `None`.

### Function run_pump

```rust
pub async fn run_pump<C, F, Fut>(
    core: Arc<Mutex<C>>,
    emitter: Arc<Emitter>,
    rx: AsyncReceiver<WsIncoming>,
    cancel: CancellationToken,
    before: F,
)where
    C: BrokerCore<Frame = WsIncoming>,
    F: Fn(WsIncoming) -> Fut,
    Fut: Future<Output = WsIncoming>,
```
Runs an event pump until `cancel` fires or the frame channel closes.

Every frame from `rx` goes through `core` and the resulting events to `emitter`. `before` runs first for each
frame (the user data connector uses it to look up what a frame needs). Events that a slow bot left in the
subscription buffers are offered again every 20 ms.

### Function secs_to_nanos

```rust
pub fn secs_to_nanos(secs: i64) -> UnixNanos
```
Unix seconds as [`UnixNanos`].

### Function side_of

```rust
pub fn side_of(raw: Option<&str>) -> Option<Side>
```
The side of a trade of the market is the side of the aggressor (`side` of the record). A trade without a
usable side is dropped: the domain type needs one.

### Function snapshot

```rust
pub fn snapshot(
    r: &StopOrderRecord,
    cid: Option<ClientOrderId>,
    now: Instant,
) -> Option<StopOrderSnapshot>
```
A stop order of the portfolio as a snapshot. The cid is the one the connector found (`None` if the order is
not ours). `None` if the record lacks the side, the quantity or the trigger price.

### Function state_of

```rust
pub fn state_of(status: Option<&str>) -> StopOrderState
```
The state of a stop order from its `status`: a `working` one waits for the trigger.

### Function ticker_board

```rust
pub fn ticker_board(id: &InstrumentId) -> Result<(String, String), Error>
```
The ticker and the trading mode (board) of an instrument. ALOR needs them as two fields, so the native id
must be [`NativeId::TickerClass`] (the class code is the board, for example `TQBR`).

### Function timeframe_label

```rust
pub fn timeframe_label(tf: Timeframe) -> String
```
The `tf` of the broker for a timeframe: a number of seconds, or `D`, `W`, `M`.

### Function timestamps

```rust
pub fn timestamps(now: Instant, broker_ts: Option<UnixNanos>) -> Timestamps
```
The timestamps of an event: the time we received it and, if the broker gave one, the time of the broker.

### Function trading_params

```rust
pub fn trading_params(info: &SecurityInfo) -> TradingParams
```
The trading parameters of an instrument. The lot is in pieces; the minimal quantity is one lot. The value of a
price step is the `pricestep` of the broker (the step in roubles), or the step itself when it gives none.

### Function trading_status

```rust
pub fn trading_status(
    info: &SecurityInfo,
    now_ns: Option<UnixNanos>,
) -> TradingStatus
```
The trading status of an instrument, from its `tradingStatus` code.

### Function unix_nanos

```rust
pub fn unix_nanos(dt: DateTime<Utc>) -> UnixNanos
```
A date as [`UnixNanos`].

### Function wait_ack

```rust
pub async fn wait_ack<C>(
    core: &Mutex<C>,
    notify: Arc<Notify>,
    limit: Duration,
    state: impl Fn(&C) -> (SubState, Option<String>),
) -> Result<(), Error>
```
Waits for the broker to confirm a new subscription.

`state` reads the state of the subscription from the core. The wait ends with `Ok` when the subscription is
active (the broker confirmed it, or data came), with [`brokapi_core::Error::Rejected`] (`subscription_refused`)
when the broker refused it, and with [`brokapi_core::Error::Transport`] when `limit` passes with no answer.
The caller then takes the subscription back: it is not left half open. See [`brokapi_runtime::wait_ack`].

**Errors**

[`brokapi_core::Error::Rejected`] with the text of the broker when the subscription was refused,
[`brokapi_core::Error::Transport`] when it was not confirmed in time.

## Крейт brokapi_bybit


### Struct BybitCidCodec

```rust
pub struct BybitCidCodec;
```
The cid is written into the `orderLinkId` of the order as a hyphenated UUID (lossless, 128-bit, 36 characters of
digits, letters and dashes, which the exchange allows). The exchange returns it in the order list, in the order
stream and in the fills, which is how a cid comes back. The exchange refuses a second order with the same id while
the first one is working, which is what makes a repeated place visible.

### Struct BybitRaw

```rust
pub struct BybitRaw { /* private fields */ }
```
A handle to the whole API with the quota of the connector it came from.

- `pub async fn call<T, F, Fut>(&self, method: &str, f: F) -> Result<T, Error>where F: FnOnce(BybitHttpClient) -> Fut, Fut: Future<Output = Result<T, BybitApiError>>,`
  Runs one read. The quota of `method` is taken first.

  Errors

  [`Error::RateLimited`] when the quota is spent (nothing is sent), else the error of the call.

- `pub async fn call_command<T, F, Fut>( &self, method: &str, f: F, ) -> Result<T, Error>where F: FnOnce(BybitHttpClient) -> Fut, Fut: Future<Output = Result<T, BybitApiError>>,`
  Runs one command that changes the account. A lost answer is [`Error::Unknown`], and the call is never repeated.

  Errors

  As `BybitRaw::call`, with the unknown outcome of a command.

- `pub fn connect_ws( &self, stream: WsStream, event_tx: AsyncSender<WsIncoming>, options: WsOptions, ) -> Result<WsSession, Error>`
  Opens one more session of a stream with the same key. The frames arrive in `event_tx`.

  Errors

  [`Error::RateLimited`] when the quota for connections is spent.

### Constant DEFAULT_LIMITS_TOML

```rust
pub const DEFAULT_LIMITS_TOML: &str = "# Bybit API V5 rate limits for brokapi-bybit.\n#\n# Source: the page \"Rate Limit Rules\" of the Bybit documentation (checked 2026-10-23).\n#   * Per user (UID) and endpoint, per second: placing, amending, cancelling, cancelling all and the batch methods of\n#     category linear 10/s; the reads (/v5/order/realtime, /v5/order/history, /v5/execution/list, /v5/position/list,\n#     /v5/account/wallet-balance) 50/s. The demo contour has the default limits and they cannot be raised.\n#   * Per IP address: 600 requests in a 5 second window for api.bybit.com. Beyond that HTTP 403 and a ban of 10 minutes.\n#     The market data reads have no limit of their own, they spend the address limit.\n#   * WebSocket: no more than 500 connections in 5 minutes per IP, no more than 1000 connections per IP for market\n#     data. The number of subscriptions of a futures connection is not limited in the documentation.\n#\n# What is NOT verified against a live account: that the demo contour counts the same way, and the headers\n# X-Bapi-Limit-Status that the exchange sends with every answer are not read (the limiter keeps its own count).\n#\n# One bucket per documented endpoint limit, so a flood of placements can never use the budget of cancels, plus one\n# bucket for the address that every request spends. 100 a second with a burst of 100 keeps the address inside 600 in 5\n# seconds even with another program on the same address.\n\n[bybit.buckets.ip]\nrate = 100\nper = \"1s\"\nburst = 100\n\n[bybit.buckets.create]\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bybit.buckets.amend]\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bybit.buckets.cancel]\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bybit.buckets.cancel_all]\nrate = 10\nper = \"1s\"\nburst = 10\n\n[bybit.buckets.trade_read]\nrate = 50\nper = \"1s\"\nburst = 50\n\n[bybit.buckets.account_read]\nrate = 50\nper = \"1s\"\nburst = 50\n\n[bybit.route]\n# \"*\" = any method with no route below (raw() calls). It spends the address limit, so a call is never free.\n\"*\" = [\"ip\"]\nPlaceOrder = [\"ip\", \"create\"]\nPlaceStopOrder = [\"ip\", \"create\"]\nReplaceOrder = [\"ip\", \"amend\"]\nCancelOrder = [\"ip\", \"cancel\"]\nCancelStopOrder = [\"ip\", \"cancel\"]\nCancelAll = [\"ip\", \"cancel_all\"]\nGetOrder = [\"ip\", \"trade_read\"]\nGetOrders = [\"ip\", \"trade_read\"]\nGetOrderHistory = [\"ip\", \"trade_read\"]\nGetExecutions = [\"ip\", \"trade_read\"]\nGetPositions = [\"ip\", \"trade_read\"]\nReconcile = [\"ip\", \"trade_read\"]\nGetWallet = [\"ip\", \"account_read\"]\nGetOrderBook = [\"ip\"]\nGetTickers = [\"ip\"]\nGetLastTrades = [\"ip\"]\nGetCandles = [\"ip\"]\nGetInstruments = [\"ip\"]\nGetServerTime = [\"ip\"]\n\n[bybit.conns]\n# One connection for the public data and one for the private streams of a login. Bybit allows far more, this keeps\n# a runaway loop from reconnecting in a storm.\nmax_conns = 4\nmax_subs_per_conn = 500\nsupports_multiplex = true\n";
```
The text of the bundled limits file `limits.toml`, compiled into the crate.

Parse it with `limits_from_toml_str`, or take the result directly from `default_limits`.

### Struct MarketDataConfig

```rust
pub struct MarketDataConfig {
    pub bybit: BybitConfig,
    pub source: Arc<str>,
    pub reconnect_min: Duration,
    pub reconnect_max: Duration,
    pub subscribe_ack_timeout: Duration,
}
```
Settings of `MarketDataConnector`.

- `bybit: BybitConfig`
  Client settings of the Bybit API: key pair, contour, receive window, endpoints.

- `source: Arc<str>`
  Envelope source label, e.g. `"bybit-md"`.

- `reconnect_min: Duration`
  Pause before the first reconnect of the WebSocket, doubling up to `reconnect_max` (default 0.5 s, as at
  the other brokers).

- `reconnect_max: Duration`
  Upper bound of the pause (default 30 s). The pause returns to `reconnect_min` once a connection has
  lived 10 seconds.

- `subscribe_ack_timeout: Duration`
  How long a `subscribe_*` call waits for the broker to confirm the subscription (default 3 s). The call
  returns when the broker has confirmed: what happens after that is delivered. A refusal is returned as
  `Rejected`. If nothing comes in time, the call fails with `Transport` and the subscription is taken back, so
  the call can be repeated.

- `pub fn new(bybit: BybitConfig, source: impl Into<Arc<str>>) -> Self`
  Creates a config with the default reconnect pauses (0.5 s up to 30 s).

  `source` is the label put in every `Envelope` this connector emits, so a bot
  with several connectors can tell them apart.

### Struct MarketDataConnector

```rust
pub struct MarketDataConnector { /* private fields */ }
```
The market data connector of Bybit: implements [`OrderBookSource`], [`MarketDataSource`] and [`InstrumentSource`].

It is not tied to an account: the public data needs no key. Reads go by HTTP, streams come over one WebSocket
session that reconnects on its own and restores its subscriptions. The streams are the public ones of the category
(`wss://stream.bybit.com/v5/public/linear`): the demo contour has no public streams of its own, so a connector made
from a demo config reads the same market as one made from a main config.

Instruments are named by the symbol and the category: `BTCUSDT@linear`
(`instrument_id`).

- `pub async fn start( cfg: MarketDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Starts the connector: reads the clock of the exchange and opens the WebSocket session in the background.

  Errors

  [`Error::Config`] when the client cannot be built; [`Error::Transport`] when the exchange cannot be reached.

- `pub fn raw(&self) -> BybitRaw`
  Direct access to any API method with this connector’s key and rate limiter. See `BybitRaw`.

- `pub fn caps(&self) -> MarketDataCaps`
  What the connector can do: books up to 1000 levels (the stream sends the whole book after every change, not
  increments), and all four streams.

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Stops the connector. Unless the mode is `Immediate` it cancels the streams first; `Graceful` also lets the bot’s
  channel take what is still buffered.

### Struct MdCore

```rust
pub struct MdCore { /* private fields */ }
```
The sans-io core of the market data connector.

- `pub fn new() -> Self`
  An empty core.

- `pub fn notifier(&self) -> Arc<Notify>`
  Woken whenever a subscription changes state.

- `pub fn desired(&self) -> &SubscriptionSet`
  The subscriptions the core serves.

- `pub fn register( &mut self, id: SubscriptionId, kind: SubscriptionKind, topic: &str, req_id: Option<&str>, )`
  Registers a subscription: frames of `topic` belong to `id` and are read as `kind`. `req_id` ties the answer of
  the exchange to the topic.

- `pub fn add_request(&mut self, req_id: &str, topic: &str)`
  Remembers a request id for a topic (after a book was asked for again).

- `pub fn forget(&mut self, id: SubscriptionId) -> Vec<String>`
  Forgets a subscription. Returns the topics nobody listens to any more.

- `pub fn has_topic(&self, topic: &str) -> bool`
  Whether any subscription listens to the topic.

- `pub fn book_waits(&self, id: SubscriptionId) -> bool`
  Whether the subscription is an order book that waits for a snapshot.

- `pub fn sub_state(&self, id: SubscriptionId) -> (SubState, Option<String>)`
  The state of a subscription.

- `pub fn take_gaps(&mut self) -> Vec<String>`
  The topics whose order book lost a delta since the last call. The connector asks the exchange for a new
  snapshot of each.

### Enum MdSubKind

```rust
pub enum MdSubKind {
    OrderBook,
    MarketTrades,
    Quotes,
    Candles,
}
```
What a market data subscription is for.

- `OrderBook`
  An order book stream.

- `MarketTrades`
  Public trades.

- `Quotes`
  Best bid and ask.

- `Candles`
  Candles.

### Struct MdSubscription

```rust
pub struct MdSubscription {
    pub id: SubscriptionId,
    pub instrument: InstrumentId,
    pub kind: MdSubKind,
    pub opts: SubscribeOpts,
    pub topic: String,
}
```
A market data subscription as the connector keeps it.

- `id: SubscriptionId`
  The id given to the caller.

- `instrument: InstrumentId`
  The instrument.

- `kind: MdSubKind`
  The purpose.

- `opts: SubscribeOpts`
  The options the caller gave.

- `topic: String`
  The topic of the stream at the exchange.

### Struct Registry

```rust
pub struct Registry<K> { /* private fields */ }
```
The subscriptions of a core, their topics and the state of each topic.

Several subscriptions may listen to one topic (two bots, or a quote stream and a book of depth 1): the exchange is
asked once, the frames go to all of them.

- `pub fn notifier(&self) -> Arc<Notify>`
  Woken whenever a topic changes state. The connector waits on it for the confirmation of a new subscription.

- `pub fn register(&mut self, id: SubscriptionId, kind: K, topics: Vec<String>)`
  Registers a subscription for `topics`. A topic that already has subscriptions keeps its state.

- `pub fn add_request(&mut self, req_id: &str, topic: &str)`
  Remembers that the answer carrying `req_id` is about `topic`.

- `pub fn forget(&mut self, id: SubscriptionId) -> Vec<String>`
  Forgets a subscription. Returns the topics nobody listens to any more.

- `pub fn kind(&self, id: SubscriptionId) -> Option<&K>`
  The kind of a subscription.

- `pub fn topics_of(&self, id: SubscriptionId) -> &[String]`
  The topics of a subscription.

- `pub fn subs_of(&self, topic: &str) -> Vec<SubscriptionId>`
  The subscriptions that listen to a topic.

- `pub fn has_topic(&self, topic: &str) -> bool`
  Whether anyone listens to the topic.

- `pub fn topic_active(&self, topic: &str) -> bool`
  Whether the exchange has confirmed the topic.

- `pub fn state(&self, id: SubscriptionId) -> Option<(SubState, Option<String>)>`
  The state of a subscription: failed when any topic was refused, active when all are, else pending. With the
  reason of the refusal.

- `pub fn on_ack(&mut self, req_id: &str, success: bool, message: &str)`
  An answer of the exchange to a subscription request.

- `pub fn on_data(&mut self, topic: &str)`
  Data came on a topic: it is open, whatever the answer said.

- `pub fn fail_pending(&mut self, reason: &str)`
  The login was refused: every topic still waiting fails with the reason.

- `pub fn reset(&mut self)`
  A connection was lost and made again: every topic is asked for again and waits for its answer.

- `pub fn iter(&self) -> impl Iterator<Item = (SubscriptionId, &K)>`
  Every subscription with its kind.

### Struct ReqIdGen

```rust
pub struct ReqIdGen(/* private fields */);
```
Hands out the `req_id` that ties an answer of the exchange to its request.

- `pub fn next(&self) -> String`
  The next request id, a short text.

### Struct SubIdGen

```rust
pub struct SubIdGen(/* private fields */);
```
Hands out subscription ids, counting up from `start + 1`.

- `pub fn new(start: u64) -> Self`
  A generator whose first id is `start + 1`.

- `pub fn next(&self) -> SubscriptionId`
  The next id.

### Enum SubState

```rust
pub enum SubState {
    Pending,
    Active,
    Failed,
}
```
Where a subscription is with the exchange.

- `Pending`
  Asked for, no answer yet.

- `Active`
  The exchange confirmed it, or data came.

- `Failed`
  The exchange refused it.

### Struct UdCore

```rust
pub struct UdCore { /* private fields */ }
```
The sans-io core of the user data connector.

- `pub fn new(category: impl Into<String>) -> Self`
  An empty core for the category of the account.

- `pub fn notifier(&self) -> Arc<Notify>`
  Woken whenever a subscription changes state.

- `pub fn sub_state(&self, id: SubscriptionId) -> (SubState, Option<String>)`
  The state of a subscription.

- `pub fn register( &mut self, id: SubscriptionId, kind: UdSubKind, topics: &[String], reqs: &[(String, String)], )`
  Registers a subscription for `topics`; `reqs` are (request id, topic) pairs of the requests that are about to
  be sent.

- `pub fn forget(&mut self, id: SubscriptionId) -> Vec<String>`
  Forgets a subscription. Returns the topics nobody listens to any more.

- `pub fn has_topic(&self, topic: &str) -> bool`
  Whether any subscription listens to the topic.

- `pub fn learn_order_id(&mut self, order_id: &str, cid: ClientOrderId)`
  Remembers that the order with this number is the order of `cid`.

- `pub fn cid_of_order_id(&self, order_id: &str) -> Option<ClientOrderId>`
  The cid of the order with this number, if the core has seen it.

- `pub fn resolve_cid(&mut self, order_id: &str, link_id: &str) -> ClientOrderId`
  The cid of an order of the exchange: the one in its `orderLinkId`, else the one learned for its number, else a
  stable made-up one (the order is not ours).

### Enum UdSubKind

```rust
pub enum UdSubKind {
    OrderState,
    Portfolio,
    Executions,
}
```
What a user data subscription is for.

- `OrderState`
  Own orders.

- `Portfolio`
  Positions and balances.

- `Executions`
  Own fills.

### Struct UdSubscription

```rust
pub struct UdSubscription {
    pub id: SubscriptionId,
    pub kind: UdSubKind,
    pub opts: SubscribeOpts,
    pub topics: Vec<String>,
}
```
A user data subscription as the connector keeps it.

- `id: SubscriptionId`
  The id given to the caller.

- `kind: UdSubKind`
  The purpose.

- `opts: SubscribeOpts`
  The options the caller gave.

- `topics: Vec<String>`
  The topics of the streams at the exchange.

### Struct UserDataConfig

```rust
pub struct UserDataConfig {
    pub bybit: BybitConfig,
    pub source: Arc<str>,
    pub order_state_buffer: usize,
    pub reconnect_min: Duration,
    pub reconnect_max: Duration,
    pub subscribe_ack_timeout: Duration,
    pub price_grid: PriceGrid,
}
```
Settings of `UserDataConnector`.

The contour (main or demo) and the category come from the `bybit` config.

- `bybit: BybitConfig`
  Client settings of the Bybit API: key pair, contour, receive window, endpoints.

- `source: Arc<str>`
  Envelope source label, e.g. `"bybit-ud"`.

- `order_state_buffer: usize`
  Capacity of the lossless buffer of each order state and execution subscription (default 1024 events).
  When it is full, the subscription reports an overflow and the connector becomes degraded: commands are
  refused until the next successful `reconcile`.

- `reconnect_min: Duration`
  Pause before the first reconnect of the WebSocket, doubling up to `reconnect_max` (default 0.5 s).

- `reconnect_max: Duration`
  Upper bound of the pause (default 30 s).

- `subscribe_ack_timeout: Duration`
  How long a `subscribe_*` call waits for the broker to confirm the subscription (default 3 s), see
  `MarketDataConfig::subscribe_ack_timeout`.

- `price_grid: PriceGrid`
  Price steps and lot sizes the connector checks orders against (default: empty, only the lot sizes the
  connector reads from the broker). Fill it with [`PriceGrid::learn`] from the trading parameters of the
  instruments you trade. A price off the step is then refused with `Rejected` (`price_off_step`) before
  anything is sent.

- `pub fn new(bybit: BybitConfig, source: impl Into<Arc<str>>) -> Self`
  Creates a config with the default buffer (1024 events) and the default reconnect pauses.

  `source` is the label put in every `Envelope` this connector emits.

### Struct UserDataConnector

```rust
pub struct UserDataConnector { /* private fields */ }
```
The user data connector of Bybit for one account: implements [`OrderExecutor`], [`StopOrderExecutor`],
[`ExecutionSource`] and [`PortfolioSource`].

Commands (place, cancel) go by HTTP, one request each, never repeated: a lost answer is [`Error::Unknown`]. The
streams (own orders, fills, positions, wallet) come over one private WebSocket session that logs in with the key,
reconnects on its own and restores its subscriptions. The account must be in one-way position mode (the default);
the unified trading account is the only kind V5 knows.

Quantities are in units of the base coin (`0.001` BTC). The connector reads the quantity step, the smallest order and
the price step of a symbol once, and refuses an order off the grid before anything is sent.

The `cid` goes to the exchange as the `orderLinkId` of the order, which is how it comes back in the order list, the
stream and the fills. A conditional order (a stop order) is an order with a trigger price: it is placed and cancelled
through the same methods, and listed apart in [`StopOrderExecutor::active_stop_orders`].

- `pub async fn start( cfg: UserDataConfig, limiter: Arc<BrokerLimiter>, out: Sender<Envelope>, ) -> Result<Self, Error>`
  Starts the connector: reads the clock of the exchange, checks the key pair with a read of the wallet, and opens
  the private WebSocket session in the background. Commands wait for the first
  `reconcile`.

  Errors

  [`Error::Config`] when the client cannot be built or the exchange refuses the key pair (a key of the demo account
  used on the main host, a wrong secret, a missing right, an IP not on the list of the key);
  [`Error::Transport`] when the exchange cannot be reached.

- `pub fn raw(&self) -> BybitRaw`
  Direct access to any API method with this connector’s key and rate limiter. See `BybitRaw`.

- `pub fn price_grid(&self) -> &PriceGrid`
  The price steps and quantity steps the connector checks orders against, on top of the rules it reads from the
  exchange. Fill it with `learn` to be stricter.

- `pub fn caps(&self) -> UserDataCaps`
  What the connector can do.

  - `place_idempotency`: `LabelOnly`. The documentation says an `orderLinkId` is unique and a second order with the
  same one is refused (code 110072), but how long the exchange remembers a closed order’s id is not stated and
  not checked on a live account. The library never repeats a place whatever the value.

  - `supports_replace`: no. An amendment of the exchange keeps the client id of the order, and the shared `replace`
  promises a new order under a new cid. The amendment is available through `raw` and
  `BybitHttpClient::amend_order`. `supports_stop_orders` and `streams_executions`: yes.

  - `ws_rpc` and `ordered_command_channel`: no (commands go by HTTP; the exchange has a WebSocket trade channel
  that this connector does not use, and the demo contour has none). `sandbox`: yes on the demo contour (virtual
  money), no on the main one.

- `pub async fn shutdown(self, mode: ShutdownMode) -> ShutdownReport`
  Stops the connector. Unless the mode is `Immediate` it closes the trading gate first; `Graceful` also lets the
  bot’s channel take what is still buffered. Orders at the exchange are not touched.

### Constant WALLET_TOPIC

```rust
pub const WALLET_TOPIC: &str = "wallet";
```
The topic of the wallet.

### Function balance

```rust
pub fn balance(c: &CoinBalance, now: Instant) -> Balance
```
The balance of one coin.

`blocked` is the margin the open orders and positions hold plus what open spot orders lock. `available` is the
equity (the wallet balance plus the unrealised profit) less `blocked`, never below zero: what can still open a
position. The exchange shows an available figure only for the whole account (in USD), so this per coin figure is a
reading of the connector, not the exchange’s own.

### Function balances

```rust
pub fn balances(wallets: &[WalletRecord], now: Instant) -> Vec<Balance>
```
The balances of the coins that hold anything.

### Function broker_ts

```rust
pub fn broker_ts(ms: i64) -> Option<UnixNanos>
```
The time of the exchange as nanoseconds, `None` when the exchange gave none (zero).

### Function build

```rust
pub fn build(
    req: &PlaceStopOrderRequest,
    symbol: &str,
    category: &str,
    cid: ClientOrderId,
) -> Result<CreateOrderRequest, Error>
```
The request for a stop order.

**Errors**

[`Error::Rejected`] with code `missing_price` for a stop-limit without `limit_price`, and with code
`unsupported_expiry` when the expiry is not good-till-cancel: the exchange keeps a conditional order until it fires or
is cancelled, and has no time limit to give it.

### Function candle

```rust
pub fn candle(
    k: &Kline,
    instrument: &InstrumentId,
    tf: Timeframe,
    now: Instant,
) -> Candle
```
A candle from a row of the history. The candle closes one timeframe after it opens.

### Function cid_from_link_id

```rust
pub fn cid_from_link_id(link_id: Option<&str>) -> Option<ClientOrderId>
```
The cid in the `orderLinkId` of an order: a whole UUID, else none (an order placed in the app of the exchange, or by
another program).

### Function default_limits

```rust
pub fn default_limits() -> BrokerLimits
```
The bundled Bybit limits, parsed.

A method with no route of its own spends the default route `"*"`, so no call is free. Placing goes through a
soft bucket of its own that cancel does not pass.

**Panics**

Never in practice: the bundled file is checked by a test of this crate.

### Function drain_backlog

```rust
pub async fn drain_backlog(emitter: &Emitter, deadline: Duration)
```
Waits until the emitter has nothing buffered, or `deadline` has passed.

### Function execution

```rust
pub fn execution(
    r: &ExecutionRecord,
    cid: Option<ClientOrderId>,
    category: &str,
    now: Instant,
) -> Option<Execution>
```
The fill. The commission is the fee as the exchange states it: a rebate is negative. The currency of the fee is the
settle coin of the instrument.

### Function execution_topic

```rust
pub fn execution_topic(category: &str) -> String
```
The topic of the own fills of a category.

### Function foreign_cid

```rust
pub fn foreign_cid(order_id: &str) -> ClientOrderId
```
Stable cid for an order of this account that this connector did not place: the same order number always
gives the same cid, so it can be told apart from ours and found again.

### Function instrument

```rust
pub fn instrument(info: &InstrumentInfo, category: &str) -> Instrument
```
The directory entry.

### Function instrument_id

```rust
pub fn instrument_id(symbol: &str, category: &str) -> InstrumentId
```
The id of an instrument: the symbol and the category.

The key is `BTCUSDT@linear`: the second part names the product category of the exchange, because the same symbol
can trade as spot and as a perpetual. The native id is the pair as the exchange spells it.

### Function interval_label

```rust
pub fn interval_label(tf: Timeframe) -> Option<&'static str>
```
The interval word of the API for a timeframe, `None` for a timeframe the exchange has no candles for (10 minutes).

### Function is_fill

```rust
pub fn is_fill(r: &ExecutionRecord) -> bool
```
Whether the record is a fill of an order. The funding payments the exchange lists among the executions
(`execType` `Funding`) and the settlement of a delivery are not fills of an order of the bot.

### Function is_final

```rust
pub fn is_final(r: &OrderRecord) -> bool
```
Whether the order is closed for good.

### Function is_untriggered

```rust
pub fn is_untriggered(r: &OrderRecord) -> bool
```
Whether the order is a conditional order that has not fired.

### Function kind_of

```rust
pub fn kind_of(contract_type: &str) -> InstrumentKind
```
The kind of an instrument by the contract type of the exchange: a perpetual is a [`InstrumentKind::Perpetual`],
a dated contract a [`InstrumentKind::Future`].

### Function levels

```rust
pub fn levels(levels: &[(Decimal, Decimal)]) -> Vec<BookLevel>
```
The levels of one side, as the library spells them.

### Function limits_from_toml_path

```rust
pub fn limits_from_toml_path(path: &str) -> Result<BrokerLimits, ConfigError>
```
Reads the `[bybit]` section of a limits file on disk, to tune the limits without recompiling.

**Errors**

[`brokapi_core::ConfigError::Message`] when the file cannot be read (the message names the path), and for
every reason `limits_from_toml_str` lists.

### Function limits_from_toml_str

```rust
pub fn limits_from_toml_str(raw: &str) -> Result<BrokerLimits, ConfigError>
```
Reads the `[bybit]` section of a limits file given as text.

Other sections of the file (other brokers) are ignored.

**Errors**

[`brokapi_core::ConfigError::Message`] when the text is not valid TOML, when there is no `[bybit]` section, or
when the section is not a valid set of limits (see [`BrokerLimits::from_toml_section`]).

### Function map_command_err

```rust
pub fn map_command_err(err: BybitApiError) -> Error
```
Maps an error of a command that changes an order.

### Function map_config_err

```rust
pub fn map_config_err(err: BybitApiError) -> Error
```
Maps an error of the construction of the client.

### Function map_err

```rust
pub fn map_err(err: BybitApiError) -> Error
```
Maps an error of a read.

### Function market_trade

```rust
pub fn market_trade(
    r: &PublicTradeRecord,
    instrument: &InstrumentId,
    now: Instant,
) -> Option<MarketTrade>
```
A public trade of the REST answer.

### Function ms_to_nanos

```rust
pub fn ms_to_nanos(ms: i64) -> UnixNanos
```
Nanoseconds from milliseconds since the epoch.

### Function nanos_to_ms

```rust
pub fn nanos_to_ms(nanos: UnixNanos) -> i64
```
Milliseconds since the epoch from nanoseconds.

### Function order_ack

```rust
pub fn order_ack(cid: ClientOrderId, order_id: &str, now: Instant) -> OrderAck
```
The ack of a command.

### Function order_book

```rust
pub fn order_book(
    bids: &[(Decimal, Decimal)],
    asks: &[(Decimal, Decimal)],
    ts_ms: i64,
    instrument: &InstrumentId,
    now: Instant,
) -> OrderBook
```
A snapshot of the book from the REST answer.

### Function order_snapshot

```rust
pub fn order_snapshot(
    r: &OrderRecord,
    cid: ClientOrderId,
    category: &str,
    now: Instant,
) -> Option<OrderSnapshot>
```
The snapshot of an order. `category` names the instrument when the record does not.

### Function order_topic

```rust
pub fn order_topic(category: &str) -> String
```
The topic of the own orders of a category.

### Function portfolio

```rust
pub fn portfolio(
    wallets: &[WalletRecord],
    records: &[PositionRecord],
    category: &str,
    now: Instant,
) -> Portfolio
```
The whole portfolio.

### Function position

```rust
pub fn position(r: &PositionRecord, category: &str, now: Instant) -> Position
```
A position. The quantity is signed: a short position is negative.

### Function position_topic

```rust
pub fn position_topic(category: &str) -> String
```
The topic of the positions of a category.

### Function positions

```rust
pub fn positions(
    records: &[PositionRecord],
    category: &str,
    now: Instant,
) -> Vec<Position>
```
The open positions (a size of zero is no position).

### Function quote

```rust
pub fn quote(
    bid: Option<&Level>,
    ask: Option<&Level>,
    ts_ms: i64,
    instrument: &InstrumentId,
    now: Instant,
) -> Quote
```
The best bid and ask from the first level of each side.

### Function run_pump

```rust
pub async fn run_pump<C, F, Fut>(
    core: Arc<Mutex<C>>,
    emitter: Arc<Emitter>,
    rx: AsyncReceiver<WsIncoming>,
    cancel: CancellationToken,
    after: F,
)where
    C: BrokerCore<Frame = WsIncoming>,
    F: Fn() -> Fut,
    Fut: Future<Output = ()>,
```
Reads frames from `rx` until `cancel`, runs them through `core`, and gives the events to `emitter`. `after` runs
after every frame with the core locked out, so a connector can act on what the core noted (a book to ask for
again).

### Function side_of

```rust
pub fn side_of(raw: &str) -> Option<Side>
```
The side from the word of the API.

### Function side_word

```rust
pub fn side_word(side: Side) -> &'static str
```
The word of the API for a side.

### Function snapshot

```rust
pub fn snapshot(
    r: &OrderRecord,
    cid: Option<ClientOrderId>,
    category: &str,
    now: Instant,
) -> Option<StopOrderSnapshot>
```
The snapshot of a conditional order.

### Function state_of

```rust
pub fn state_of(status: &str) -> StopOrderState
```
The state of a conditional order by the status of the exchange.

### Function symbol_category

```rust
pub fn symbol_category(
    id: &InstrumentId,
    category: &str,
) -> Result<String, Error>
```
The symbol and the category of an instrument id.

**Errors**

[`Error::Config`] when the native id is not of the kind this connector uses (`SymbolMic`), or the category is not
the one the connector works with.

### Function timestamps

```rust
pub fn timestamps(now: Instant, broker_ts: Option<UnixNanos>) -> Timestamps
```
The three clocks of an event: `now` is the local monotonic one, the wall clock is read here, the time of the
exchange is given when the frame has it.

### Function trading_params

```rust
pub fn trading_params(info: &InstrumentInfo, category: &str) -> TradingParams
```
The trading rules.

The quantity is in units of the base coin (`0.001` BTC). `lot_size` is the quantity step and `min_qty` the smallest
order, so a quantity is valid when it is a whole number of steps and not below the minimum. The value of one price
step is the step itself: the contract is linear (one unit of the base coin pays one unit of the settle coin per unit
of price), so a step of `0.10` moves one unit by `0.10` USDT.

### Function trading_status

```rust
pub fn trading_status(
    info: &InstrumentInfo,
    category: &str,
    now_ns: Option<UnixNanos>,
) -> TradingStatus
```
The status: `Trading` is open, the contracts that wait to open or are delisted are closed.

### Function trigger_direction

```rust
pub fn trigger_direction(kind: StopOrderKind, side: Side) -> i64
```
The trigger direction of the API: `1` when the order fires as the price rises to the trigger price, `2` when it
falls to it.

### Function unix_now_nanos

```rust
pub fn unix_now_nanos() -> UnixNanos
```
The wall clock now, as nanoseconds since the epoch.

### Function wait_ack

```rust
pub async fn wait_ack<C>(
    core: &Mutex<C>,
    notify: Arc<Notify>,
    limit: Duration,
    state: impl Fn(&C) -> (SubState, Option<String>),
) -> Result<(), Error>
```
Waits until the exchange has answered a subscription: `Ok` when it is open, [`brokapi_core::Error::Rejected`]
(`subscription_refused`) when the exchange refused it, [`brokapi_core::Error::Transport`] when nothing came in
`limit`.

## Крейт brokapi_bcs_protocol


### Struct BcsConfig

```rust
pub struct BcsConfig {
    pub refresh_token: String,
    pub request_timeout: Duration,
    pub is_write: bool,
    pub http_base_url: String,
    pub ws_base_url: String,
}
```
Configuration of the HTTP and WebSocket clients.

The one secret in it is the refresh token, which you issue in the BCS web cabinet (section “API
tokens” of the account). The broker issues two kinds of refresh tokens:

- a read token: market data, portfolio, orders and trades history, no trading,

- a write token: everything the read token does, plus placing, changing and cancelling orders.

A token belongs to one brokerage account and lives 90 days by the broker docs. The token is shown
once, at issue. `BcsConfig::is_write` must say which kind you hold, because it picks the
`client_id` of the token request (`trade-api-write` or `trade-api-read`).

The `Debug` output of this struct hides the refresh token (`<redacted>`), so a config can be logged.

- `refresh_token: String`
  The long-lived refresh token from the BCS web cabinet.

  The HTTP client exchanges it for short-lived access tokens. If the broker returns a new refresh
  token with an access token (rotation), the client keeps the new one in memory only. It does not
  write it back to this struct or to disk, so a restart starts again from the token you supply.

- `request_timeout: Duration`
  Timeout of one REST request, from sending to the end of the body.

  Default 30 seconds. It does not apply to WebSocket sessions. A request that times out ends with
  `BcsApiError::HttpError` and no status. For an order
  placement that is an unknown outcome: ask the broker for the order before you send it again.

- `is_write: bool`
  `true` if `BcsConfig::refresh_token` is a write (trading) token, `false` for a read token.

  It selects `client_id = "trade-api-write"` or `"trade-api-read"` in the token request, as the
  broker docs describe. What the broker does when the flag and the token kind disagree is not
  verified against the live API. A read token cannot place orders.

- `http_base_url: String`
  Base URL of the REST API, without a trailing slash.

  Default is the production host `HTTP_BASE_URL`. The path of each endpoint is appended to it.

- `ws_base_url: String`
  Base URL of the WebSocket API, without a trailing slash.

  Default is the production host `WS_BASE_URL`. The path of each channel is appended to it.

- `pub fn from_env() -> Result<Self, VarError>`
  Reads the configuration from the environment (and from a `.env` file, if there is one).

  It reads two variables:

  - `BCS_REFRESH_TOKEN`: required, the refresh token.

  - `BCS_IS_WRITE`: optional. The exact text `true` means a write token. Any other value, or no
  variable, means a read token.

  A `.env` file in the working directory is loaded first, without overriding variables that are
  already set. Parent directories are not searched (see `BcsConfig::from_env_file` for another
  path). The URLs and the timeout are the defaults (production host,
  30 seconds). No network call is made.

  Errors

  Returns `std::env::VarError::NotPresent` when `BCS_REFRESH_TOKEN` is not set, and
  `std::env::VarError::NotUnicode` when it is not valid Unicode. A wrong or expired token is not
  detected here. It shows up as `BcsApiError::AuthError`
  at the first request.

  Examples

  The example does not need a token: it handles both outcomes, as a program would.

- `pub fn from_env_file(path: impl AsRef<Path>) -> Result<Self, VarError>`
  Like `BcsConfig::from_env`, but loads the `.env` file at `path` (a file that is not there is
  skipped, variables that are already set are kept).

  Errors

  The same as `BcsConfig::from_env`.

  Examples

- `pub fn new(refresh_token: String) -> Self`
  Builds a read-only configuration for the production host from a refresh token.

  Chain `BcsConfig::with_write` if the token is a write token.

  Examples

- `pub fn for_mock( refresh_token: impl Into<String>, http_base_url: impl Into<String>, ) -> Self`
  Points both the REST and the WebSocket client at a local server, for tests and examples.

  `http_base_url` is the base of the local server (for example `http://127.0.0.1:34567`). The
  WebSocket base is derived from it by swapping the scheme (`http` to `ws`, `https` to `wss`).
  The token is only a label for the imitation, it is never sent to the real broker. The result is
  a read configuration, add `BcsConfig::with_write` for the trading calls.

  Examples

- `pub fn with_write(self, is_write: bool) -> Self`
  Sets whether the refresh token is a write (trading) token. Returns the changed config.

  Pass `true` only when the token really is a write token. See `BcsConfig::is_write`.

  Examples

## Крейт brokapi_tbank_protocol


### Struct TbankConfig

```rust
pub struct TbankConfig {
    pub token: String,
    pub endpoint: String,
    pub sandbox: bool,
    pub account_id: Option<String>,
    pub app_name: Option<String>,
    pub request_timeout: Duration,
    pub connect_timeout: Duration,
    pub root_certificate_pem: Option<Vec<u8>>,
}
```
gRPC client configuration. The token is never printed by `Debug`.

One config describes one token and one endpoint. Several accounts of the same token share the
token and differ in `account_id`: build one config per account with
`with_account`.

- `token: String`
  API token, issued in the T-Bank Invest settings (a sandbox token for the sandbox endpoint).

  It is sent in the `authorization: Bearer` header of every request. Treat it as a secret:
  keep it out of logs and source control. `Debug` prints `<redacted>` instead of it.

- `endpoint: String`
  Full URL, `https://host:443` (TLS) or `http://host:port` (plain, for mocks).

- `sandbox: bool`
  Sandbox flag. Selects the sandbox endpoint in `new`, and tells the connector the account is virtual.

- `account_id: Option<String>`
  Brokerage account id. When `None` the connector picks the first open account of the token.

- `app_name: Option<String>`
  `x-app-name` header (`<github nick>.<repo>` is the T-Bank convention).

- `request_timeout: Duration`
  Deadline of one unary call. Default 30 s.

  `TbankClient` does not apply it. The connectors in `brokapi-tbank` cut
  every call at this deadline. For a command (place, cancel, replace) a timeout means the outcome
  is unknown, because the broker may have acted. See the connector docs.

- `connect_timeout: Duration`
  How long the channel waits for the TCP and TLS connection to be set up. Default 10 s.

  It applies to every (re)connect of the lazy channel. There is no builder method, set the field.

- `root_certificate_pem: Option<Vec<u8>>`
  An extra root certificate (PEM) the TLS connection trusts besides the bundled web PKI roots. `None`
  by default.

  Set it when the broker’s endpoint is served with a certificate of a national root (the Russian
  Ministry of Digital Development one) that the bundled roots do not know. Not verified against the live
  endpoint which certificate it presents. See `with_root_certificate_pem`.

- `pub fn new(token: impl Into<String>, sandbox: bool) -> Self`
  Production or sandbox endpoint chosen by `sandbox`.

  Timeouts take their defaults (30 s per request, 10 s to connect). No account is chosen and no
  app name is set.

  Examples

- `pub fn sandbox(token: impl Into<String>) -> Self`
  Sandbox endpoint (`SANDBOX_ENDPOINT`). Accounts there are virtual. Use a sandbox token.

  Examples

- `pub fn production(token: impl Into<String>) -> Self`
  Production endpoint (`PROD_ENDPOINT`). Orders placed through it are real and cost money.

  Examples

- `pub fn for_mock(token: impl Into<String>, endpoint: impl Into<String>) -> Self`
  Plain-HTTP endpoint of a local mock server (tests and examples).

  `endpoint` is a full URL such as `http://127.0.0.1:50051`. The config is marked as sandbox, so
  a connector built from it reports `sandbox: true` in its capabilities. Do not use it with a
  real token.

  Examples

- `pub fn from_env() -> Result<Self, ProtocolError>`
  Reads the config from the environment. Loads a `.env` file of the working directory first, if
  there is one (parent directories are not searched, see `from_env_file` for
  another path).

  Variables: `TBANK_TOKEN` (required), `TBANK_SANDBOX` (`true`, `1` or `yes`, default `false`, so
  production), `TBANK_ENDPOINT` (overrides the endpoint when not empty, must start with
  `https://`), `TBANK_ACCOUNT_ID` and `TBANK_APP_NAME` (both optional, empty means not set).

  Note the default: without `TBANK_SANDBOX` the config points at production.

  Errors

  `ProtocolError::Config` when `TBANK_TOKEN` is not set or is not valid unicode, or when
  `TBANK_ENDPOINT` is set and is not an `https://` address (a token must not go over plain text
  because of a stray variable). The address of a local imitation is set in code, with
  `for_mock`.

  Examples

- `pub fn from_env_file(path: impl AsRef<Path>) -> Result<Self, ProtocolError>`
  Like `from_env`, but loads the `.env` file at `path` (a file that is not there
  is skipped, variables that are already set are kept).

  Errors

  As `from_env`.

  Examples

- `pub fn with_account(self, account_id: impl Into<String>) -> Self`
  Pins the brokerage account this config works with.

  A user data connector trades on exactly this account. Without it the connector takes the
  token’s only open account and refuses to start when there are several. Use
  `TbankClient::list_accounts` to see the ids.

  Examples

- `pub fn with_app_name(self, app_name: impl Into<String>) -> Self`
  Sets the `x-app-name` header sent with every request.

  The T-Bank convention is `<github nick>.<repo>`. The name must be a valid header value,
  otherwise `TbankClient::new` returns
  `ProtocolError::Config`.

  Examples

- `pub fn with_root_certificate_pem(self, pem: impl Into<Vec<u8>>) -> Self`
  Trusts one more root certificate for the TLS connection, in PEM form (the text between
  `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`).

  The bundled web PKI roots stay trusted. Use it when the endpoint’s certificate chains to a root
  they do not contain (for example the national root certificate). `TbankClient::new`
  refuses a PEM text that holds no certificate.

  Examples

- `pub fn with_request_timeout(self, timeout: Duration) -> Self`
  Sets the deadline of one unary call, see `request_timeout`.

  Examples

## Крейт brokapi_alor_protocol


### Struct AlorConfig

```rust
pub struct AlorConfig {
    pub refresh_token: String,
    pub request_timeout: Duration,
    pub env: AlorEnv,
    pub http_base_url: String,
    pub auth_base_url: String,
    pub ws_base_url: String,
    pub portfolio: Option<String>,
    pub exchange: String,
}
```
Configuration of the HTTP and WebSocket clients.

The one secret in it is the refresh token, which you issue on the developer portal after binding a trade
account (it lives a year in the production contour and a month in the test one). The broker has no
separate read and trade tokens: the access token made from it reaches everything the account may do.

The `Debug` output hides the refresh token (`<redacted>`), so a config can be logged.

- `refresh_token: String`
  The long-lived refresh token from the developer portal.

- `request_timeout: Duration`
  Timeout of one REST request, from sending to the end of the body (default 30 seconds). A request
  that times out ends with `AlorApiError::Http` and no status.
  For an order command that is an unknown outcome: ask the broker before you send it again.

- `env: AlorEnv`
  The contour. Set by `AlorConfig::with_env`, which also sets the three addresses below.

- `http_base_url: String`
  Base URL of the REST API, without a trailing slash.

- `auth_base_url: String`
  Base URL of the authorization server, without a trailing slash.

- `ws_base_url: String`
  Base URL of the WebSocket API, without a trailing slash. The channels are `/ws` and `/cws`.

- `portfolio: Option<String>`
  The portfolio the user data connector works with (for example `D12345`). If it is empty, the connector
  takes the only portfolio named in the access token, and refuses to start when the token names several.

- `exchange: String`
  The exchange of the portfolio: `MOEX` (default) or `SPBX`. Orders, positions and trades are read per
  exchange, so one connector works with one.

- `pub fn new(refresh_token: String) -> Self`
  A configuration for the production contour from a refresh token.

  Examples

- `pub fn from_env() -> Result<Self, VarError>`
  Reads the configuration from the environment (and from a `.env` file of the working directory, if
  there is one; variables that are already set win).

  - `ALOR_REFRESH_TOKEN`: required.

  - `ALOR_PORTFOLIO`: optional, see `AlorConfig::portfolio`.

  - `ALOR_EXCHANGE`: optional, `MOEX` (default) or `SPBX`.

  - `ALOR_ENV`: optional, the exact text `test` selects the test contour.

  No network call is made.

  Errors

  `std::env::VarError::NotPresent` when `ALOR_REFRESH_TOKEN` is not set. A wrong or expired token shows
  up as `AlorApiError::Auth` at the first request.

  Examples

- `pub fn from_env_file(path: impl AsRef<Path>) -> Result<Self, VarError>`
  Like `AlorConfig::from_env`, but loads the `.env` file at `path` (a file that is not there is skipped).

  Errors

  The same as `AlorConfig::from_env`.

- `pub fn with_env(self, env: AlorEnv) -> Self`
  Points all three addresses at the production or the test contour. Returns the changed config.

- `pub fn with_portfolio(self, portfolio: impl Into<String>) -> Self`
  Sets the portfolio (see `AlorConfig::portfolio`). Returns the changed config.

- `pub fn with_exchange(self, exchange: impl Into<String>) -> Self`
  Sets the exchange, `MOEX` or `SPBX` (see `AlorConfig::exchange`). Returns the changed config.

- `pub fn for_mock( refresh_token: impl Into<String>, http_base_url: impl Into<String>, ) -> Self`
  Points the REST, authorization and WebSocket clients at one local server, for tests and examples.

  `http_base_url` is the base of the local server (for example `http://127.0.0.1:34567`). The
  authorization server is the same host, and the WebSocket base is derived by swapping the scheme. The token
  is only a label for the imitation, it is never sent to the real broker. The portfolio is `D00001`.

  Examples

## Крейт brokapi_bybit_protocol


### Struct BybitConfig

```rust
pub struct BybitConfig {
    pub api_key: String,
    pub api_secret: String,
    pub env: BybitEnv,
    pub http_base_url: String,
    pub ws_public_base_url: String,
    pub ws_private_base_url: String,
    pub request_timeout: Duration,
    pub recv_window_ms: u64,
    pub category: Category,
    pub settle_coin: String,
}
```
Settings of the client: the key pair, the contour and the addresses.

The secret never appears in `Debug` output.

- `api_key: String`
  The API key (public half).

- `api_secret: String`
  The API secret (signs requests).

- `env: BybitEnv`
  The contour.

- `http_base_url: String`
  REST base URL, without a trailing slash.

- `ws_public_base_url: String`
  Base URL of the public streams (the path `/v5/public/<category>` is added).

- `ws_private_base_url: String`
  Base URL of the private streams (the path `/v5/private` is added).

- `request_timeout: Duration`
  Timeout of one HTTP request.

- `recv_window_ms: u64`
  The receive window sent with every private request, in milliseconds.

- `category: Category`
  The product category.

- `settle_coin: String`
  The settle coin used to list orders and positions of a whole category (`USDT` for linear).

- `pub fn new(api_key: impl Into<String>, api_secret: impl Into<String>) -> Self`
  A config for the main contour.

  Examples

- `pub fn from_env() -> Result<Self, String>`
  Reads the key pair and the contour from the environment (after loading `.env` from the current directory).

  `BYBIT_ENV` is `mainnet` (default), `demo` or `testnet`. The key pair is `BYBIT_API_KEY` and `BYBIT_API_SECRET`,
  and for the demo contour `BYBIT_DEMO_API_KEY` and `BYBIT_DEMO_API_SECRET`: a demo key is not valid on the main
  host, so the two pairs can live in one `.env`.

  Errors

  The name of the missing variable. An unknown `BYBIT_ENV` is an error too.

- `pub fn from_env_file(path: impl AsRef<Path>) -> Result<Self, String>`
  Like `BybitConfig::from_env`, with the `.env` file at `path`.

- `pub fn with_env(self, env: BybitEnv) -> Self`
  Points the config at a contour: the hosts of the contour replace the current ones.

  Examples

- `pub fn with_recv_window_ms(self, ms: u64) -> Self`
  Sets the receive window in milliseconds.

- `pub fn for_mock( api_key: impl Into<String>, api_secret: impl Into<String>, http_base_url: impl Into<String>, ) -> Self`
  A config for the in-process imitation: every host is `http_base_url` (the streams use `ws://`).

  The config is marked as demo, so a connector built from it reports a virtual account.

- `pub fn ws_public_url(&self) -> String`
  The URL of the public stream of the category.

- `pub fn ws_private_url(&self) -> String`
  The URL of the private stream.

- `pub fn is_demo(&self) -> bool`
  Whether the account is virtual (the demo contour or the imitation).
