# Async Usage

Kraken clients are async-first and support two usage styles:

- construct a client and call methods directly for quick one-off requests
- use `async with` when you want explicit lifecycle management

## Quick Usage

For short request-response flows, plain construction is fine -- the underlying transport(s)
open lazily on first use.

```python
from typed_kraken import Kraken

client = Kraken.new(public=True)
ticker = await client.spot.market_data.ticker(pair='XBTUSD')
print(ticker)
```

That works because the REST client opens its underlying `httpx.AsyncClient` when the first
request is sent -- there's no separate connect step to forget.

## Context Manager Usage

Use `async with` when you want the client to open up front and close cleanly at the end of
the block.

```python
from typed_kraken import Kraken

async with Kraken.new(public=True) as client:
  ticker = await client.spot.market_data.ticker(pair='XBTUSD')
  book = await client.spot.market_data.pre_trade(symbol='BTC/USD')
```

`Kraken.new(...)` builds all three top-level surfaces -- `spot` (REST), `streams`
(WebSocket subscriptions), and `trading_ws` (WebSocket trading RPCs) -- and its
`__aenter__` enters them concurrently (`asyncio.gather`), so `async with Kraken.new(...)`
is the only thing you need: every sub-surface underneath (`spot.account`, `spot.trading`,
`streams.market_data`, `trading_ws.add_order`, ...) bottoms out lazily on first use, the
same as direct construction. You never enter a sub-client separately.

This is the recommended style for:

- multiple requests in the same flow
- long-lived sessions
- any streaming workflow
- code where explicit cleanup matters

## Streams

`streams.market_data` and `streams.private` methods are channel subscriptions: each returns
a `StreamManager`, not a stream directly. Use `async with` on it so the subscription is
unsubscribed automatically when the block exits:

```python
from typed_kraken import Kraken

async with Kraken.new(public=True) as client:
  async with client.streams.market_data.ticker(symbol=['BTC/USD']) as ticker:
    async for msg in ticker:
      for entry in msg['data']:
        print(entry['symbol'], entry['last'])
```

`await`ing the manager directly also works, but leaves the subscription open until you call
`unsubscribe()` yourself:

```python
from typed_kraken import Kraken

async with Kraken.new(public=True) as client:
  ticker = await client.streams.market_data.ticker(symbol=['BTC/USD'])
  async for msg in ticker:
    print(msg['data'][0]['last'])
    break
  await ticker.unsubscribe()
```

## Trading (WebSocket)

`trading_ws` is a separate top-level surface, not part of `streams`: its methods
(`add_order`, `cancel_order`, `edit_order`, ...) are plain WebSocket RPC calls, not
subscriptions -- `await` them directly, the same as an HTTP call:

```python
from typed_kraken import Kraken

async with Kraken.new() as client:
  result = await client.trading_ws.add_order({
    'order_type': 'limit', 'side': 'buy', 'order_qty': 0.001,
    'symbol': 'BTC/USD', 'limit_price': 10_000,
  })
  print(result.get('order_id'))
```

## Composite/Multi-Surface Client

`Kraken.new()` bundles three independent top-level surfaces:

- `spot` -- REST, under `https://api.kraken.com`. One `HttpRpcClient` shared across
  five product groups exposed as `cached_property`s: `account`, `earn`, `funding`,
  `market_data`, `trading`. Public methods (like `market_data.ticker`) work with no
  credentials; private ones sign each request.
- `streams` -- WebSocket v2 channel subscriptions, over two separate connections.
  `market_client` connects to the public `wss://ws.kraken.com/v2` and backs
  `streams.market_data`. `private_client` connects to the authenticated
  `wss://ws-auth.kraken.com/v2` and backs `streams.private` (account channel
  subscriptions).
- `trading_ws` -- WebSocket v2 trading RPCs (order placement/cancellation), request/reply
  rather than subscriptions. Reaches Kraken over the *same* `private_client` connection
  `streams.private` uses -- Kraken serves both over one authenticated socket -- but is
  its own top-level surface, kept out of `streams` since its calls are `await`ed
  directly rather than iterated.

Pass `public=True` to build a client with no credentials, usable only for
`spot.market_data` and `streams.market_data`.

```python
from typed_kraken import Kraken

async with Kraken.new() as client:
  ticker = await client.spot.market_data.ticker(pair='XBTUSD')
  result = await client.trading_ws.add_order({
    'order_type': 'market', 'side': 'buy', 'order_qty': 0.001, 'symbol': 'BTC/USD',
  })
```

Each subscription's `StreamManager` also supports `.map(f)`/`.filter(f)` to transform or
filter pushes before you iterate them, if you want a narrower or reshaped stream.

## Guidance

Use direct construction for quick reads. Use `async with` by default when doing more than
one call, opening streams, or wanting predictable cleanup.
