From 4a076c68d4122a2329a65cc1e7531b50db51ce90 Mon Sep 17 00:00:00 2001 From: Apurv-Salunke Date: Fri, 27 Mar 2026 13:20:20 +0530 Subject: [PATCH 1/2] docs: simplify and restructure docs site from 37 to 22 pages Merge overlapping content (auth, portfolio, websocket, errors), make all examples broker-agnostic with dual sync/async tabs, switch to Material for MkDocs theme, and delete 20 obsolete files. Co-Authored-By: Claude Opus 4.6 --- docs_site/authentication.md | 111 +++ docs_site/basics.md | 80 --- docs_site/broker-differences.md | 111 ++- docs_site/config-and-env.md | 117 --- docs_site/error-handling.md | 134 ++++ docs_site/errors-and-retries.md | 61 -- docs_site/getting-started.md | 103 +++ docs_site/glossary.md | 52 -- docs_site/gtt.md | 132 ++-- docs_site/holdings-and-funds.md | 48 -- docs_site/index.md | 29 +- docs_site/instruments.md | 114 +-- docs_site/login-and-session.md | 83 --- docs_site/market-data.md | 161 +++-- docs_site/orders.md | 104 +-- docs_site/portfolio.md | 126 ++++ docs_site/positions.md | 53 -- docs_site/realtime-websocket.md | 131 ---- docs_site/recipes/cancel-all-open-orders.md | 46 +- docs_site/recipes/close-all-open-positions.md | 50 +- docs_site/recipes/first-order.md | 65 +- docs_site/recipes/index.md | 15 - docs_site/recipes/recover-from-reconnect.md | 70 -- .../recipes/stream-and-store-live-ticks.md | 15 +- docs_site/reference/capabilities.md | 30 - docs_site/reference/clients.md | 161 +++-- docs_site/reference/enums.md | 33 - docs_site/reference/exceptions.md | 11 +- docs_site/reference/index.md | 17 - docs_site/reference/instrument-store.md | 113 --- docs_site/reference/models.md | 59 +- docs_site/reference/operation-notes.md | 55 -- docs_site/safety-and-good-practices.md | 65 -- docs_site/start-here.md | 27 - docs_site/trades.md | 39 - docs_site/troubleshooting/auth-failures.md | 55 +- docs_site/troubleshooting/duplicate-orders.md | 18 +- docs_site/troubleshooting/index.md | 9 - .../troubleshooting/instrument-not-found.md | 57 +- docs_site/troubleshooting/order-rejected.md | 23 +- .../troubleshooting/websocket-reconnect.md | 78 -- docs_site/websocket.md | 143 ++++ mkdocs.yml | 87 ++- poetry.lock | 674 +++++++++++++++++- pyproject.toml | 1 + 45 files changed, 2103 insertions(+), 1663 deletions(-) create mode 100644 docs_site/authentication.md delete mode 100644 docs_site/basics.md delete mode 100644 docs_site/config-and-env.md create mode 100644 docs_site/error-handling.md delete mode 100644 docs_site/errors-and-retries.md create mode 100644 docs_site/getting-started.md delete mode 100644 docs_site/glossary.md delete mode 100644 docs_site/holdings-and-funds.md delete mode 100644 docs_site/login-and-session.md create mode 100644 docs_site/portfolio.md delete mode 100644 docs_site/positions.md delete mode 100644 docs_site/realtime-websocket.md delete mode 100644 docs_site/recipes/index.md delete mode 100644 docs_site/recipes/recover-from-reconnect.md delete mode 100644 docs_site/reference/capabilities.md delete mode 100644 docs_site/reference/enums.md delete mode 100644 docs_site/reference/index.md delete mode 100644 docs_site/reference/instrument-store.md delete mode 100644 docs_site/reference/operation-notes.md delete mode 100644 docs_site/safety-and-good-practices.md delete mode 100644 docs_site/start-here.md delete mode 100644 docs_site/trades.md delete mode 100644 docs_site/troubleshooting/index.md delete mode 100644 docs_site/troubleshooting/websocket-reconnect.md create mode 100644 docs_site/websocket.md diff --git a/docs_site/authentication.md b/docs_site/authentication.md new file mode 100644 index 0000000..3f44775 --- /dev/null +++ b/docs_site/authentication.md @@ -0,0 +1,111 @@ +# Authentication + +## Auth modes + +| Mode | How it works | Broker support | +|------|-------------|----------------| +| `manual` | You provide a fresh access token each trading day | All brokers | +| `auto` | tt-connect logs in automatically (e.g. via TOTP) | Brokers that support it | + +## Basic usage + +=== "Sync" + + ```python + from tt_connect import TTConnect + + config = { + "api_key": "YOUR_API_KEY", + "access_token": "YOUR_ACCESS_TOKEN", + } + + with TTConnect(broker_id, config) as broker: + print(broker.get_profile().name) + ``` + +=== "Async" + + ```python + import asyncio + from tt_connect import AsyncTTConnect + + async def main() -> None: + config = { + "api_key": "YOUR_API_KEY", + "access_token": "YOUR_ACCESS_TOKEN", + } + + async with AsyncTTConnect(broker_id, config) as broker: + profile = await broker.get_profile() + print(profile.name) + + asyncio.run(main()) + ``` + +## Config keys + +### Common keys (all brokers) + +| Key | Type | Default | Description | +|---|---|---|---| +| `api_key` | `str` | required | Broker app API key | +| `access_token` | `str` | required (manual mode) | Daily session token | +| `on_stale` | `"fail"` or `"warn"` | `"fail"` | Behavior when instrument cache refresh fails | +| `cache_session` | `bool` | `False` | Persist session to disk for reuse within same day | + +### Auto mode keys (brokers that support it) + +| Key | Type | Description | +|---|---|---| +| `auth_mode` | `"auto"` | Enable automatic login | +| `client_id` | `str` | Broker client code | +| `pin` | `str` | Trading PIN | +| `totp_secret` | `str` | Base32 TOTP secret for 2FA | + +??? note "Broker-specific config details" + See [Broker Differences](broker-differences.md) for the exact config keys required by each broker. + +## Instrument cache behavior (`on_stale`) + +| Value | Meaning | +|---|---| +| `"fail"` | If instrument refresh fails at startup, client init fails (safest for production) | +| `"warn"` | If refresh fails and a cache exists, continue with stale data | + +## Environment variables + +Keep credentials out of code: + +```python +import os +from tt_connect import TTConnect + +config = { + "api_key": os.environ["BROKER_API_KEY"], + "access_token": os.environ["BROKER_ACCESS_TOKEN"], +} + +with TTConnect(broker_id, config) as broker: + print(broker.get_profile().name) +``` + +## Session lifecycle + +- Client creation triggers login and instrument initialization +- Sessions are valid for the current trading day only + +!!! warning "Daily token expiry (SEBI requirement)" + All Indian broker tokens expire at end of day. This is a SEBI mandate. You must re-authenticate each trading day. If your token expires mid-session, API calls raise `AuthenticationError`. + +## Good practices + +- Keep credentials in environment variables or a secret manager +- Never hardcode tokens in source code +- Always close the client (the `with` statement does this automatically) + +## Common mistakes + +- Using `auto` auth mode on a broker that only supports `manual` +- Missing required keys for auto mode (e.g. `totp_secret`) +- Using an expired access token +- Mixing environment variables from different brokers diff --git a/docs_site/basics.md b/docs_site/basics.md deleted file mode 100644 index a2cbf40..0000000 --- a/docs_site/basics.md +++ /dev/null @@ -1,80 +0,0 @@ -# Basics - -## What this package does -`tt-connect` gives one common API over broker APIs. - -You write one trading flow, then switch brokers by changing client config. - -## Who it is for -- algo trading developers -- backend services that place orders -- teams that want broker portability - -## Common terms -- `Instrument`: what you trade (stock/future/option/index) -- `Order`: intent to buy/sell -- `Trade`: actual fill from an order -- `Position`: current open net quantity -- `Holding`: delivery/carry inventory -- `Tick`: realtime market update - -## Sync vs async — which to use - -| Use case | Client | Why | -|---|---|---| -| Scripts, one-shot tasks, Jupyter notebooks | `TTConnect` (sync) | Simpler — no `async`/`await` needed | -| WebSocket streaming, long-running services | `AsyncTTConnect` (async) | Required for `subscribe()` and non-blocking I/O | -| FastAPI, Django async views | `AsyncTTConnect` (async) | Fits naturally into async frameworks | - -Both clients expose the same core trading and account APIs, with realtime subscription APIs (`subscribe`/`unsubscribe`) available on the async client only. Start with sync — switch to async only when you need streaming or are in an async context. - -## First working script (sync) -```python -from tt_connect import TTConnect - -config = { - "api_key": "YOUR_API_KEY", - "access_token": "YOUR_ACCESS_TOKEN", -} - -with TTConnect("zerodha", config) as broker: - profile = broker.get_profile() - funds = broker.get_funds() - - print(profile.client_id, profile.name) - print("Available funds:", funds.available) -``` - -## Async version -```python -import asyncio -from tt_connect import AsyncTTConnect - -async def main() -> None: - config = { - "api_key": "YOUR_API_KEY", - "access_token": "YOUR_ACCESS_TOKEN", - } - - async with AsyncTTConnect("zerodha", config) as broker: - profile = await broker.get_profile() - print(profile.client_id, profile.name) - -asyncio.run(main()) -``` - -## Quick path -1. Configure credentials -2. Create client -3. Fetch profile/funds -4. Place order -5. Track order/trade/position - -## What's next? -- [Login & Session](login-and-session.md) — set up credentials and auth modes -- [Recipe: First Order](recipes/first-order.md) — place your first live order - -## See also -- [Client methods](reference/clients.md) -- [Models](reference/models.md) -- [Enums](reference/enums.md) diff --git a/docs_site/broker-differences.md b/docs_site/broker-differences.md index 2f35705..49026cb 100644 --- a/docs_site/broker-differences.md +++ b/docs_site/broker-differences.md @@ -1,39 +1,92 @@ # Broker Differences -## Why this page matters -API shape is unified, but broker behavior is not always identical. +The tt-connect API is unified, but brokers differ in capabilities and behavior. This page is the single reference for all per-broker specifics. -## Current capability snapshot +## Capability matrix -| Area | Zerodha | AngelOne | +| Capability | Zerodha | AngelOne | |---|---|---| -| Auth modes | manual | manual, auto | -| Segments | NSE, BSE, NFO, BFO, CDS | NSE, BSE, NFO, CDS, MCX | -| Order types | MARKET, LIMIT, SL, SL_M | MARKET, LIMIT, SL, SL_M | -| Product types | CNC, MIS, NRML | CNC, MIS, NRML | - -## Practical difference examples -- Single order fetch may differ by broker behavior/endpoints. -- GTT payload/rules can differ between brokers. -- Market data depth/fields can differ by broker stream mode. - -## Recommended approach -- check capability before critical flows -- keep broker-specific fallbacks for edge cases -- test same strategy on each broker separately - -## Simple multi-broker pattern +| Auth modes | `manual` | `manual`, `auto` | +| Segments | `NSE`, `BSE`, `NFO`, `BFO`, `CDS` | `NSE`, `BSE`, `NFO`, `CDS`, `MCX` | +| Order types | `MARKET`, `LIMIT`, `SL`, `SL_M` | `MARKET`, `LIMIT`, `SL`, `SL_M` | +| Product types | `CNC`, `MIS`, `NRML` | `CNC`, `MIS`, `NRML` | + +## Pre-order validation + +These checks run automatically before any network call: + +| Check | What is validated | +|---|---| +| Segment | Instrument exchange is supported by broker | +| Order type | Requested order type is allowed | +| Product type | Requested product type is allowed | +| Tradeability | Index instruments are blocked for order placement | + +If validation fails, `UnsupportedFeatureError` is raised immediately. + +## Operation differences + +### Orders + +| Operation | Zerodha | AngelOne | Recommendation | +|---|---|---|---| +| `get_order(id)` | Supported | Fetches order book, filters by ID | Use `get_orders()` + filter for portability | +| `get_orders()` | Full order book | Normalized list (empty on null) | Handle empty list safely | +| `cancel_all_orders()` | List + cancel flow | List + cancel flow | Capture both cancelled and failed IDs | +| `tag` kwarg | Sent as `tag` (max 20 chars) | Sent as `uniqueorderid` | Use short tags for compatibility | + +### GTT + +| Operation | Zerodha | AngelOne | Recommendation | +|---|---|---|---| +| `place_gtt` | 1 or 2 legs | 1 leg only | Keep to single-leg for portability | +| `modify_gtt` | Supported | Supported | Re-fetch rule after modify | +| `cancel_gtt` | Direct cancel | Uses rule details internally | Handle errors, retry only on transient | + +### Authentication + +| Area | Zerodha | AngelOne | Recommendation | +|---|---|---|---| +| Auth modes | `manual` only | `manual` + `auto` | Check broker support before using `auto` | +| Token lifecycle | External OAuth flow daily | Auto mode supports TOTP login | Use `cache_session` in auto mode | + +### Config keys + +| Key | Zerodha | AngelOne (manual) | AngelOne (auto) | +|---|---|---|---| +| `api_key` | Required | Required | Required | +| `access_token` | Required | Required | — | +| `auth_mode` | — | `"manual"` | `"auto"` | +| `client_id` | — | — | Required | +| `pin` | — | — | Required | +| `totp_secret` | — | — | Required | +| `on_stale` | Optional | Optional | Optional | +| `cache_session` | Optional | Optional | Optional | + +### Market data + +| Operation | Zerodha | AngelOne | Recommendation | +|---|---|---|---| +| `get_quotes()` (REST) | Supported | Not supported | Use WebSocket for live quotes across brokers | +| `get_historical()` | Supported | Supported | Same canonical API | +| WebSocket mode | Full mode | Snap-quote mode | Keep tick callbacks tolerant to missing fields | + +### WebSocket + +| Area | Zerodha | AngelOne | Recommendation | +|---|---|---|---| +| Subscribe mode | Full (OI, depth, timestamps) | Snap-quote (OI, best-5 bid/ask) | Callbacks should handle optional fields | +| Reconnect | Auto + resubscribe | Auto + resubscribe | Keep callbacks idempotent and fast | + +## Writing portable code + ```python from tt_connect import TTConnect -def print_profile(broker_name: str, config: dict) -> None: - with TTConnect(broker_name, config) as broker: - p = broker.get_profile() - print(broker_name, p.client_id, p.name) +def run_strategy(broker_id: str, config: dict) -> None: + with TTConnect(broker_id, config) as broker: + profile = broker.get_profile() + print(broker_id, profile.client_id, profile.name) ``` -## See also -- [Broker capabilities](reference/capabilities.md) -- [Broker operation notes](reference/operation-notes.md) -- [Enums](reference/enums.md) -- [Exceptions](reference/exceptions.md) +Test the same strategy on each broker separately. Use `UnsupportedFeatureError` handling for operations that may not be available on all brokers. diff --git a/docs_site/config-and-env.md b/docs_site/config-and-env.md deleted file mode 100644 index 9253a58..0000000 --- a/docs_site/config-and-env.md +++ /dev/null @@ -1,117 +0,0 @@ -# Config & Environment Variables - -This page shows all config keys you should set for each broker. - -## Common config keys - -| Key | Type | Default | Used for | -|---|---|---|---| -| `on_stale` | `"fail" | "warn"` | `"fail"` | Instrument cache refresh behavior | -| `cache_session` | `bool` | `False` | Persist session for reuse | - -## `on_stale` behavior - -| Value | Meaning | -|---|---| -| `fail` | If instrument refresh fails, client init fails | -| `warn` | If refresh fails and cache exists, continue with stale cache | - -## Zerodha config - -### Required keys - -| Key | Required | Notes | -|---|---|---| -| `api_key` | Yes | Kite app API key | -| `access_token` | Yes | Daily token from Zerodha login flow | - -### Optional keys - -| Key | Required | Notes | -|---|---|---| -| `on_stale` | No | `"fail"` or `"warn"` | -| `cache_session` | No | Session caching toggle | - -### Example (dict) -```python -config = { - "api_key": "YOUR_ZERODHA_API_KEY", - "access_token": "YOUR_ZERODHA_ACCESS_TOKEN", - "on_stale": "fail", - "cache_session": False, -} -``` - -## AngelOne config - -### Manual mode (`auth_mode = "manual"`) - -| Key | Required | Notes | -|---|---|---| -| `auth_mode` | Yes | Set to `"manual"` | -| `api_key` | Yes | SmartAPI app key | -| `access_token` | Yes | JWT access token | -| `on_stale` | No | `"fail"` or `"warn"` | -| `cache_session` | No | Session caching toggle | - -### Auto mode (`auth_mode = "auto"`) - -| Key | Required | Notes | -|---|---|---| -| `auth_mode` | Yes | Set to `"auto"` | -| `api_key` | Yes | SmartAPI app key | -| `client_id` | Yes | AngelOne client code | -| `pin` | Yes | Trading PIN | -| `totp_secret` | Yes | Base32 TOTP secret | -| `on_stale` | No | `"fail"` or `"warn"` | -| `cache_session` | No | Useful in auto mode | - -### Example (auto mode) -```python -config = { - "auth_mode": "auto", - "api_key": "YOUR_ANGELONE_API_KEY", - "client_id": "YOUR_CLIENT_ID", - "pin": "1234", - "totp_secret": "BASE32_SECRET", - "cache_session": True, - "on_stale": "warn", -} -``` - -## Environment variable mapping - -| Config key | Environment variable | -|---|---| -| `api_key` (Zerodha) | `ZERODHA_API_KEY` | -| `access_token` (Zerodha) | `ZERODHA_ACCESS_TOKEN` | -| `api_key` (AngelOne) | `ANGELONE_API_KEY` | -| `access_token` (AngelOne manual) | `ANGELONE_ACCESS_TOKEN` | -| `client_id` (AngelOne auto) | `ANGELONE_CLIENT_ID` | -| `pin` (AngelOne auto) | `ANGELONE_PIN` | -| `totp_secret` (AngelOne auto) | `ANGELONE_TOTP_SECRET` | - -## Example: from env vars -```python -import os -from tt_connect import TTConnect - -config = { - "api_key": os.environ["ZERODHA_API_KEY"], - "access_token": os.environ["ZERODHA_ACCESS_TOKEN"], -} - -with TTConnect("zerodha", config) as broker: - print(broker.get_profile().name) -``` - -## Common config mistakes -- using `auto` auth mode on a broker that supports only manual -- missing `totp_secret` in AngelOne auto mode -- using expired access token -- mixing environment variables from different brokers - -## Related -- [Login & Session](login-and-session.md) -- [Troubleshooting: Auth failures](troubleshooting/auth-failures.md) -- [Broker capabilities](reference/capabilities.md) diff --git a/docs_site/error-handling.md b/docs_site/error-handling.md new file mode 100644 index 0000000..ca1688f --- /dev/null +++ b/docs_site/error-handling.md @@ -0,0 +1,134 @@ +# Error Handling + +## Exception types + +```python +from tt_connect.exceptions import ( + TTConnectError, # base library error + AuthenticationError, # invalid/expired auth + ConfigurationError, # missing/invalid config + RateLimitError, # broker rate limit exceeded + UnsupportedFeatureError, # broker does not support this operation + InsufficientFundsError, # not enough funds/margin + BrokerError, # generic broker-side failure + InvalidOrderError, # order payload invalid + OrderNotFoundError, # order ID not found + InstrumentNotFoundError, # instrument not in local master +) +``` + +## Handling pattern + +Catch specific errors first, use `TTConnectError` as fallback: + +```python +try: + order_id = broker.place_order( + instrument=instrument, side=side, qty=1, + order_type=order_type, product=product, + ) +except InsufficientFundsError: + # do not retry — change size/funds first + raise +except UnsupportedFeatureError: + # do not retry — broker does not support this + raise +except RateLimitError: + # retry with backoff + raise +except AuthenticationError: + # refresh token / re-login + raise +except BrokerError: + # broker rejected — inspect message + raise +except TTConnectError: + # generic library error fallback + raise +``` + +## Retry guidance + +**Retry with backoff:** + +- Timeout / network transient failures +- `RateLimitError` + +**Do not retry:** + +- `InsufficientFundsError` — fix funds first +- `UnsupportedFeatureError` — broker doesn't support it +- `ConfigurationError` — fix config +- `InvalidOrderError` — fix order parameters + +## Idempotency tags + +Pass a `tag` kwarg to `place_order` for request correlation. Before retrying, check recent orders for a matching tag to avoid duplicates: + +```python +order_id = broker.place_order( + instrument=instrument, + side=side, + qty=qty, + order_type=order_type, + product=product, + tag="strategyA-20260305-093000-01", +) +``` + +## Pre-order safety checks + +=== "Sync" + + ```python + from tt_connect import TTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange, Side, ProductType, OrderType + + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + funds = broker.get_funds() + if funds.available < 1000: + raise RuntimeError("Not enough funds") + + order_id = broker.place_order( + instrument=Equity(exchange=Exchange.NSE, symbol="SBIN"), + side=Side.BUY, + qty=1, + order_type=OrderType.MARKET, + product=ProductType.CNC, + ) + ``` + +=== "Async" + + ```python + from tt_connect import AsyncTTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange, Side, ProductType, OrderType + + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + funds = await broker.get_funds() + if funds.available < 1000: + raise RuntimeError("Not enough funds") + + order_id = await broker.place_order( + instrument=Equity(exchange=Exchange.NSE, symbol="SBIN"), + side=Side.BUY, + qty=1, + order_type=OrderType.MARKET, + product=ProductType.CNC, + ) + ``` + +## Production checklist + +- Retry policy configured for transient errors +- Alerts for auth / placement failures +- Graceful shutdown closes the client +- Risk-off path tested (`cancel_all_orders`, `close_all_positions`) +- Credentials in environment variables or secret manager +- Sufficient logging for audit / replay diff --git a/docs_site/errors-and-retries.md b/docs_site/errors-and-retries.md deleted file mode 100644 index 30aa2dd..0000000 --- a/docs_site/errors-and-retries.md +++ /dev/null @@ -1,61 +0,0 @@ -# Errors & Retries - -## Common error types -```python -from tt_connect.exceptions import ( - AuthenticationError, - ConfigurationError, - RateLimitError, - UnsupportedFeatureError, - InsufficientFundsError, - BrokerError, - TTConnectError, -) -``` - -## Basic handling pattern -```python -try: - order_id = broker.place_order(instrument=..., side=..., qty=1, order_type=..., product=...) -except InsufficientFundsError: - # do not retry; change size/funds first - raise -except UnsupportedFeatureError: - # do not retry; broker does not support this flow - raise -except RateLimitError: - # retry with backoff - raise -except AuthenticationError: - # refresh/re-login flow - raise -except BrokerError: - # broker rejected request; inspect message - raise -except TTConnectError: - # generic library-level error fallback - raise -``` - -## Retry guidance -Retry (with backoff): -- timeout/network transient failures -- rate-limit failures - -Do not retry blindly: -- validation/config errors -- insufficient funds -- unsupported feature - -## Safe retry idea for place order -- pass a `tag` kwarg to `place_order` for request correlation -- before placing again, check recent orders to avoid duplicates - -## See also -- [Exceptions](reference/exceptions.md) -- [Models](reference/models.md) -- [Client methods (orders)](reference/clients.md) -- [Recipe: Cancel all open orders](recipes/cancel-all-open-orders.md) -- [Troubleshooting: Auth failures](troubleshooting/auth-failures.md) -- [Troubleshooting: Order rejected](troubleshooting/order-rejected.md) -- [Troubleshooting: Duplicate orders](troubleshooting/duplicate-orders.md) diff --git a/docs_site/getting-started.md b/docs_site/getting-started.md new file mode 100644 index 0000000..7529cb5 --- /dev/null +++ b/docs_site/getting-started.md @@ -0,0 +1,103 @@ +# Getting Started + +## What this package does + +`tt-connect` gives one common Python API over multiple Indian broker APIs. Write one trading flow, switch brokers by changing config. + +## Who it is for + +- Algo trading developers +- Backend services that place orders +- Teams that want broker portability + +## Key concepts + +| Term | Meaning | +|------|---------| +| **Instrument** | What you trade — stock, future, option, or index | +| **Exchange** | Market venue/segment: `NSE`, `BSE`, `NFO`, `BFO`, `CDS`, `MCX` | +| **Order** | Request to buy/sell an instrument | +| **Trade** | Actual execution fill from an order | +| **Position** | Current net open quantity for an instrument | +| **Holding** | Delivery inventory carried in account | +| **Tick** | Realtime market update from WebSocket | +| **Quote** | REST snapshot of current market values | +| **Candle** | OHLC bar for a time interval | +| **GTT** | Trigger rule that places an order when a price condition is met | +| **Product Type** | How broker treats margin/holding: `CNC`, `MIS`, `NRML` | +| **Order Type** | Execution style: `MARKET`, `LIMIT`, `SL`, `SL_M` | +| **Access Token** | Session token required for authenticated broker API calls | +| **Auth Mode** | How login is done: `manual` (provide token) or `auto` (where supported) | + +## Sync vs async — which to use + +| Use case | Client | Why | +|---|---|---| +| Scripts, one-shot tasks, Jupyter notebooks | `TTConnect` (sync) | Simpler — no `async`/`await` needed | +| WebSocket streaming, long-running services | `AsyncTTConnect` (async) | Required for `subscribe()` and non-blocking I/O | +| FastAPI, Django async views | `AsyncTTConnect` (async) | Fits naturally into async frameworks | + +Both clients expose the same trading and account APIs. Realtime subscription (`subscribe`/`unsubscribe`) is available on the async client only. Start with sync — switch to async when you need streaming or are in an async context. + +## First working script + +=== "Sync" + + ```python + from tt_connect import TTConnect + + config = { + "api_key": "YOUR_API_KEY", + "access_token": "YOUR_ACCESS_TOKEN", + } + + with TTConnect(broker_id, config) as broker: + profile = broker.get_profile() + funds = broker.get_funds() + + print(profile.client_id, profile.name) + print("Available funds:", funds.available) + ``` + +=== "Async" + + ```python + import asyncio + from tt_connect import AsyncTTConnect + + async def main() -> None: + config = { + "api_key": "YOUR_API_KEY", + "access_token": "YOUR_ACCESS_TOKEN", + } + + async with AsyncTTConnect(broker_id, config) as broker: + profile = await broker.get_profile() + print(profile.client_id, profile.name) + + asyncio.run(main()) + ``` + +Replace `broker_id` with your broker identifier (e.g. `"zerodha"`, `"angelone"`). + +## Learning paths + +**Beginner** — place your first order: + +1. [Authentication](authentication.md) — configure credentials +2. [Recipe: First order](recipes/first-order.md) — place a live order +3. [Troubleshooting](troubleshooting/auth-failures.md) — if blocked + +**Production** — harden for live trading: + +1. [Authentication](authentication.md) — auth modes and config +2. [Error Handling](error-handling.md) — safety and retry patterns +3. [Broker Differences](broker-differences.md) — per-broker behavior +4. Risk controls: [Cancel all orders](recipes/cancel-all-open-orders.md), [Close all positions](recipes/close-all-open-positions.md) +5. [WebSocket](websocket.md) — live monitoring + +**Data-first** — market data and instruments: + +1. [Instruments](instruments.md) — discover what you can trade +2. [Market Data](market-data.md) — quotes, candles, ticks +3. [Recipe: Stream live ticks](recipes/stream-and-store-live-ticks.md) diff --git a/docs_site/glossary.md b/docs_site/glossary.md deleted file mode 100644 index 818d58b..0000000 --- a/docs_site/glossary.md +++ /dev/null @@ -1,52 +0,0 @@ -# Glossary - -## Instrument -A tradable entity like stock, future, option, or index. - -## Exchange -Market venue/segment identifier like `NSE`, `BSE`, `NFO`. - -## Order -Request to buy/sell an instrument. - -## Trade (Fill) -Actual execution event generated from an order. - -## Position -Current net open quantity for an instrument. - -## Holding -Delivery inventory carried in account. - -## Product Type -How broker treats margin/holding (`CNC`, `MIS`, `NRML`). - -## Order Type -Execution style (`MARKET`, `LIMIT`, `SL`, `SL_M`). - -## Tick -Realtime market update from WebSocket. - -## Quote -REST snapshot of current market values. - -## Candle -OHLC bar for a time interval. - -## GTT -Trigger rule that places order when condition is met. - -## OCO -One-cancels-other: two trigger legs where one execution cancels the other. - -## Access Token -Session token required for authenticated broker API calls. - -## Auth Mode -How login is done (`manual` token vs `auto` flow where supported). - -## Stale Instrument Cache -Local symbol/token database is out of date. - -## Idempotency Tag -Client-side unique tag used to reduce duplicate order risk. diff --git a/docs_site/gtt.md b/docs_site/gtt.md index 19865d1..ec184b9 100644 --- a/docs_site/gtt.md +++ b/docs_site/gtt.md @@ -1,68 +1,84 @@ # GTT (Trigger Orders) -## What GTT means -A rule that triggers an order when price condition is met. +A GTT (Good Till Triggered) is a rule that places an order when a price condition is met. ## Create a single-leg GTT -```python -from tt_connect import TTConnect, GttLeg -from tt_connect.instruments import Equity -from tt_connect.enums import Exchange, Side, ProductType - -config = {"api_key": "...", "access_token": "..."} - -with TTConnect("zerodha", config) as broker: - gtt_id = broker.place_gtt( - instrument=Equity(exchange=Exchange.NSE, symbol="SBIN"), - last_price=800.0, - legs=[ - GttLeg( - trigger_price=790.0, - price=789.5, - side=Side.BUY, - qty=1, - product=ProductType.CNC, - ) - ], - ) - print("GTT ID:", gtt_id) -``` -## Read and modify GTT -```python -with TTConnect("zerodha", config) as broker: - gtt = broker.get_gtt("123456") - print(gtt.gtt_id, gtt.status, gtt.symbol) - - broker.modify_gtt( - gtt_id="123456", - instrument=Equity(exchange=Exchange.NSE, symbol="SBIN"), - last_price=805.0, - legs=[ - GttLeg( - trigger_price=792.0, - price=791.5, - side=Side.BUY, - qty=1, - product=ProductType.CNC, - ) - ], - ) -``` +=== "Sync" + + ```python + from tt_connect import TTConnect, GttLeg + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange, Side, ProductType + + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + gtt_id = broker.place_gtt( + instrument=Equity(exchange=Exchange.NSE, symbol="SBIN"), + last_price=800.0, + legs=[ + GttLeg( + trigger_price=790.0, + price=789.5, + side=Side.BUY, + qty=1, + product=ProductType.CNC, + ) + ], + ) + print("GTT ID:", gtt_id) + ``` + +=== "Async" + + ```python + from tt_connect import AsyncTTConnect, GttLeg + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange, Side, ProductType + + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + gtt_id = await broker.place_gtt( + instrument=Equity(exchange=Exchange.NSE, symbol="SBIN"), + last_price=800.0, + legs=[ + GttLeg( + trigger_price=790.0, + price=789.5, + side=Side.BUY, + qty=1, + product=ProductType.CNC, + ) + ], + ) + print("GTT ID:", gtt_id) + ``` + +## Read, modify, and cancel -## Cancel GTT ```python -with TTConnect("zerodha", config) as broker: - broker.cancel_gtt("123456") +# Read +gtt = broker.get_gtt("123456") +print(gtt.gtt_id, gtt.status, gtt.symbol) + +# Modify +broker.modify_gtt( + gtt_id="123456", + instrument=Equity(exchange=Exchange.NSE, symbol="SBIN"), + last_price=805.0, + legs=[ + GttLeg(trigger_price=792.0, price=791.5, side=Side.BUY, qty=1, product=ProductType.CNC) + ], +) + +# Cancel +broker.cancel_gtt("123456") ``` ## Practical notes -- GTT behavior is broker-specific -- always confirm trigger status after creation -- some brokers support richer GTT forms than others - -## See also -- [Client methods (GTT APIs)](reference/clients.md) -- [Models (`Gtt`, `GttLeg`)](reference/models.md) -- [Broker capabilities](reference/capabilities.md) -- [Broker operation notes](reference/operation-notes.md) + +- Always confirm trigger status after creation +- Keep to single-leg GTTs for maximum broker portability +- See [Broker Differences](broker-differences.md) for per-broker GTT behavior diff --git a/docs_site/holdings-and-funds.md b/docs_site/holdings-and-funds.md deleted file mode 100644 index 5ef926a..0000000 --- a/docs_site/holdings-and-funds.md +++ /dev/null @@ -1,48 +0,0 @@ -# Holdings & Funds - -## Holdings vs positions -- Holdings: delivery/carry inventory -- Positions: open trading exposure - -## Read funds -```python -from tt_connect import TTConnect - -config = {"api_key": "...", "access_token": "..."} - -with TTConnect("zerodha", config) as broker: - f = broker.get_funds() - print("Available:", f.available) - print("Used:", f.used) - print("Total:", f.total) - print("Collateral:", f.collateral) -``` - -## Read holdings -```python -with TTConnect("zerodha", config) as broker: - holdings = broker.get_holdings() - for h in holdings: - print( - h.instrument.exchange, - h.instrument.symbol, - "qty=", h.qty, - "avg=", h.avg_price, - "ltp=", h.ltp, - "pnl=", h.pnl, - "pnl%=", h.pnl_percent, - ) -``` - -## Quick usage idea -- use funds check before placing orders -- use holdings for delivery portfolio reporting -- use positions for intraday risk/exposure tracking - -## User expectations -- values can change quickly during market hours -- field names are normalized, but broker math may differ - -## See also -- [Client methods (`get_holdings`, `get_funds`)](reference/clients.md) -- [Models (`Holding`, `Fund`)](reference/models.md) diff --git a/docs_site/index.md b/docs_site/index.md index ee0db47..8fe5118 100644 --- a/docs_site/index.md +++ b/docs_site/index.md @@ -2,23 +2,22 @@ `tt-connect` gives one consistent Python API for multiple Indian brokers. -## Start In 5 Minutes +## Start in 5 minutes -1. [Start Here](start-here.md): install, initialize, and verify setup -2. [Basics](basics.md): core concepts before placing real orders -3. [Login & Session](login-and-session.md): auth modes and token lifecycle -4. [Config & Environment](config-and-env.md): safe defaults and common mistakes +1. [Getting Started](getting-started.md) — install, core concepts, first script +2. [Authentication](authentication.md) — auth modes and config +3. [Recipe: First Order](recipes/first-order.md) — place your first live order -## Common Tasks +## Common tasks -- [Place Your First Order](orders.md) -- [Read Portfolio & Funds](holdings-and-funds.md) -- [Use Market Data](market-data.md) -- [Realtime WebSocket](realtime-websocket.md) +- [Place and manage orders](orders.md) +- [Read portfolio and funds](portfolio.md) +- [Use market data](market-data.md) +- [Stream live ticks](websocket.md) -## Need Help Fast? +## Need help? -- Go to [Troubleshooting](troubleshooting/index.md) for auth/order/reconnect failures. -- Use [Recipes](recipes/index.md) for ready copy-paste workflows. -- Use [API Reference](reference/index.md) for exact models and method behavior. -- Check [Glossary](glossary.md) if you are unsure about trading terms. +- [Troubleshooting](troubleshooting/auth-failures.md) — auth, order, and instrument failures +- [Recipes](recipes/first-order.md) — copy-paste workflows +- [API Reference](reference/clients.md) — exact method signatures and models +- [Broker Differences](broker-differences.md) — per-broker capabilities and quirks diff --git a/docs_site/instruments.md b/docs_site/instruments.md index 0ce7a5f..54eb1f2 100644 --- a/docs_site/instruments.md +++ b/docs_site/instruments.md @@ -1,18 +1,14 @@ # Instruments -## Supported instrument types -- Stock (equity) -- Future -- Option -- Index (mainly for market data and underlying reference) - -## Important fields -- exchange -- symbol -- expiry (for futures/options) -- strike + option type CE/PE (for options) +## Supported types + +- **Equity** — cash market stock +- **Future** — derivative keyed by expiry +- **Option** — derivative keyed by expiry + strike + CE/PE +- **Index** — for market data and underlying reference (not tradeable for orders) ## Create instrument objects + ```python from datetime import date from tt_connect.instruments import Equity, Future, Option, Index @@ -37,47 +33,75 @@ nifty_ce = Option( ``` ## Search instruments -```python -from tt_connect import TTConnect -config = {"api_key": "...", "access_token": "..."} +=== "Sync" -with TTConnect("zerodha", config) as broker: - results = broker.search_instruments("RELIANCE", exchange="NSE") - for i in results: - print(i.exchange, i.symbol) -``` + ```python + from tt_connect import TTConnect -## Discover futures/options/expiries -```python -from tt_connect import TTConnect -from tt_connect.instruments import Equity -from tt_connect.enums import Exchange + config = {"api_key": "...", "access_token": "..."} -config = {"api_key": "...", "access_token": "..."} -underlying = Equity(exchange=Exchange.NSE, symbol="SBIN") + with TTConnect(broker_id, config) as broker: + results = broker.search_instruments("RELIANCE", exchange="NSE") + for i in results: + print(i.exchange, i.symbol) + ``` -with TTConnect("zerodha", config) as broker: - expiries = broker.get_expiries(underlying) - print("Expiries:", expiries) +=== "Async" - futures = broker.get_futures(underlying) - print("Futures count:", len(futures)) + ```python + from tt_connect import AsyncTTConnect - if expiries: - chain = broker.get_options(underlying, expiry=expiries[0]) - print("Options for first expiry:", len(chain)) -``` + async with AsyncTTConnect(broker_id, config) as broker: + results = await broker.search_instruments("RELIANCE", exchange="NSE") + for i in results: + print(i.exchange, i.symbol) + ``` + +## Discover futures, options, and expiries + +=== "Sync" + + ```python + from tt_connect import TTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange + + config = {"api_key": "...", "access_token": "..."} + underlying = Equity(exchange=Exchange.NSE, symbol="SBIN") + + with TTConnect(broker_id, config) as broker: + expiries = broker.get_expiries(underlying) + print("Expiries:", expiries) + + futures = broker.get_futures(underlying) + print("Futures count:", len(futures)) + + if expiries: + chain = broker.get_options(underlying, expiry=expiries[0]) + print("Options for first expiry:", len(chain)) + ``` + +=== "Async" + + ```python + from tt_connect import AsyncTTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange + + config = {"api_key": "...", "access_token": "..."} + underlying = Equity(exchange=Exchange.NSE, symbol="SBIN") -## Instrument matching -You pass a canonical instrument. The package maps it to broker token/symbol before API calls. + async with AsyncTTConnect(broker_id, config) as broker: + expiries = await broker.get_expiries(underlying) + futures = await broker.get_futures(underlying) + if expiries: + chain = await broker.get_options(underlying, expiry=expiries[0]) + ``` -## User tips -- always use correct exchange + symbol -- use helper APIs to discover futures/options/expiries -- treat index as non-tradable for order placement +## Tips -## See also -- [Client methods (`get_futures`, `get_options`, `get_expiries`, `search_instruments`)](reference/clients.md) -- [Models (Instrument, Equity, Future, Option, Index)](reference/models.md) -- [Enums (`Exchange`, `OptionType`)](reference/enums.md) +- Always use the correct exchange + symbol combination +- Use helper APIs to discover futures/options/expiries rather than guessing +- Index instruments cannot be used for order placement +- The package maps canonical instruments to broker-specific tokens automatically diff --git a/docs_site/login-and-session.md b/docs_site/login-and-session.md deleted file mode 100644 index 3b5bf26..0000000 --- a/docs_site/login-and-session.md +++ /dev/null @@ -1,83 +0,0 @@ -# Login & Session - -## Login modes -- Manual: you provide token -- Auto: package logs in and refreshes token (if broker supports) - -## Zerodha (manual token) -```python -from tt_connect import TTConnect - -config = { - "api_key": "YOUR_API_KEY", - "access_token": "YOUR_ACCESS_TOKEN", -} - -with TTConnect("zerodha", config) as broker: - print(broker.get_profile().name) -``` - -## AngelOne auto mode (TOTP) -```python -from tt_connect import TTConnect - -config = { - "auth_mode": "auto", - "api_key": "YOUR_API_KEY", - "client_id": "YOUR_CLIENT_ID", - "pin": "1234", - "totp_secret": "BASE32_SECRET", - "cache_session": True, -} - -with TTConnect("angelone", config) as broker: - print(broker.get_profile().name) -``` - -## AngelOne manual mode -```python -from tt_connect import TTConnect - -config = { - "auth_mode": "manual", - "api_key": "YOUR_API_KEY", - "access_token": "YOUR_JWT_TOKEN", -} - -with TTConnect("angelone", config) as broker: - print(broker.get_profile().name) -``` - -## Session lifecycle -- Client creation triggers login/init. -- Sessions can be reused within the same trading day. - -!!! warning "Daily token expiry (SEBI requirement)" - All Indian broker tokens expire at end-of-day. This is a SEBI mandate, not a tt-connect limitation. You must re-authenticate each trading day. - - - **Zerodha**: Complete the OAuth login flow daily to get a fresh `access_token`. - - **AngelOne (auto mode)**: tt-connect will auto-login via TOTP each day. Set `cache_session: True` to persist the session to disk so it survives restarts within the same day. - - **AngelOne (manual mode)**: Obtain a fresh `access_token` JWT daily. - - If your token expires mid-session, API calls will raise `AuthenticationError`. - -## Good practices -- keep credentials in env vars/secret manager -- do not hardcode tokens -- always close the client (`with` does this automatically) - -## Common issues -- missing API key/token -- expired token -- wrong auth mode for selected broker - -## What's next? -- [Config & Environment](config-and-env.md) — all config keys and env var mapping -- [Instruments](instruments.md) — understand what you can trade - -## See also -- [Client methods](reference/clients.md) -- [Enums (`AuthMode`)](reference/enums.md) -- [Exceptions](reference/exceptions.md) -- [Broker capabilities](reference/capabilities.md) -- [Troubleshooting: Auth failures](troubleshooting/auth-failures.md) diff --git a/docs_site/market-data.md b/docs_site/market-data.md index b73ae5c..f3d7893 100644 --- a/docs_site/market-data.md +++ b/docs_site/market-data.md @@ -1,67 +1,108 @@ # Market Data -!!! warning "Broker support" - `get_quotes()` is only available for Zerodha. AngelOne does not expose a REST quotes endpoint — use WebSocket streaming (`subscribe`) instead. `get_historical()` works on both brokers. - ## Data types -- Quotes: snapshot from REST -- Ticks: live updates from WebSocket -- Candles: historical OHLC bars + +| Type | Source | Use case | +|------|--------|----------| +| **Quotes** | REST snapshot | Current price check | +| **Ticks** | WebSocket stream | Live monitoring | +| **Candles** | Historical API | Backtesting, charting | + +??? note "Broker-specific" + REST quotes (`get_quotes`) may not be available on all brokers. Use WebSocket streaming for guaranteed live data across brokers. Historical candles (`get_historical`) work on all brokers. ## Get quotes -```python -from tt_connect import TTConnect -from tt_connect.instruments import Equity -from tt_connect.enums import Exchange - -config = {"api_key": "...", "access_token": "..."} - -with TTConnect("zerodha", config) as broker: - instruments = [ - Equity(exchange=Exchange.NSE, symbol="RELIANCE"), - Equity(exchange=Exchange.NSE, symbol="SBIN"), - ] - quotes = broker.get_quotes(instruments) - for q in quotes: - print(q.instrument.symbol, q.ltp, q.volume) -``` + +=== "Sync" + + ```python + from tt_connect import TTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange + + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + instruments = [ + Equity(exchange=Exchange.NSE, symbol="RELIANCE"), + Equity(exchange=Exchange.NSE, symbol="SBIN"), + ] + quotes = broker.get_quotes(instruments) + for q in quotes: + print(q.instrument.symbol, q.ltp, q.volume) + ``` + +=== "Async" + + ```python + from tt_connect import AsyncTTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange + + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + instruments = [ + Equity(exchange=Exchange.NSE, symbol="RELIANCE"), + Equity(exchange=Exchange.NSE, symbol="SBIN"), + ] + quotes = await broker.get_quotes(instruments) + for q in quotes: + print(q.instrument.symbol, q.ltp, q.volume) + ``` ## Get historical candles -```python -from datetime import datetime, timedelta -from tt_connect.enums import CandleInterval - -end = datetime.now() -start = end - timedelta(days=5) - -candles = broker.get_historical( - instrument=Equity(exchange=Exchange.NSE, symbol="RELIANCE"), - interval=CandleInterval.MINUTE_5, - from_date=start, - to_date=end, -) - -for c in candles[:3]: - print(c.timestamp, c.open, c.high, c.low, c.close, c.volume) -``` - -## Tick fields you may see -- ltp -- volume -- oi -- bid/ask -- timestamp - -## Reality checks -- some fields may be missing by broker or segment -- timestamps may differ from your local clock - -## What's next? -- [Realtime (WebSocket)](realtime-websocket.md) — stream live ticks with feed health callbacks -- [Recipe: Stream and store live ticks](recipes/stream-and-store-live-ticks.md) — save ticks to CSV - -## See also -- [Client methods (`get_quotes`, `get_historical`, `subscribe`)](reference/clients.md) -- [Models (`Tick`, `Candle`)](reference/models.md) -- [Enums (`CandleInterval`)](reference/enums.md) -- [Broker operation notes](reference/operation-notes.md) + +=== "Sync" + + ```python + from datetime import datetime, timedelta + from tt_connect.enums import CandleInterval + + end = datetime.now() + start = end - timedelta(days=5) + + candles = broker.get_historical( + instrument=Equity(exchange=Exchange.NSE, symbol="RELIANCE"), + interval=CandleInterval.MINUTE_5, + from_date=start, + to_date=end, + ) + + for c in candles[:3]: + print(c.timestamp, c.open, c.high, c.low, c.close, c.volume) + ``` + +=== "Async" + + ```python + from datetime import datetime, timedelta + from tt_connect.enums import CandleInterval + + end = datetime.now() + start = end - timedelta(days=5) + + candles = await broker.get_historical( + instrument=Equity(exchange=Exchange.NSE, symbol="RELIANCE"), + interval=CandleInterval.MINUTE_5, + from_date=start, + to_date=end, + ) + ``` + +## Tick fields + +| Field | Always present | Notes | +|-------|---------------|-------| +| `ltp` | Yes | Last traded price | +| `volume` | Optional | May be `None` | +| `oi` | Optional | Open interest | +| `bid` / `ask` | Optional | Best bid/ask | +| `timestamp` | Optional | Broker-provided time | + +Field availability varies by broker and segment. Write tick callbacks that handle `None` values. + +## What's next + +- [WebSocket](websocket.md) — stream live ticks with feed health callbacks +- [Recipe: Stream live ticks](recipes/stream-and-store-live-ticks.md) — save ticks to CSV diff --git a/docs_site/orders.md b/docs_site/orders.md index 4003846..9c780ad 100644 --- a/docs_site/orders.md +++ b/docs_site/orders.md @@ -1,34 +1,56 @@ # Orders -!!! warning "Broker differences" - AngelOne does not support `get_order(order_id)` — use `get_orders()` and filter by ID instead. See [operation notes](reference/operation-notes.md) for full per-broker behavior. - ## Core actions -- place order -- modify order -- cancel order -- list orders + +- Place order +- Modify order +- Cancel order +- List orders ## Place order (market) -```python -from tt_connect import TTConnect -from tt_connect.instruments import Equity -from tt_connect.enums import Exchange, Side, ProductType, OrderType - -config = {"api_key": "...", "access_token": "..."} - -with TTConnect("zerodha", config) as broker: - order_id = broker.place_order( - instrument=Equity(exchange=Exchange.NSE, symbol="RELIANCE"), - side=Side.BUY, - qty=1, - order_type=OrderType.MARKET, - product=ProductType.CNC, - ) - print("placed:", order_id) -``` + +=== "Sync" + + ```python + from tt_connect import TTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange, Side, ProductType, OrderType + + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + order_id = broker.place_order( + instrument=Equity(exchange=Exchange.NSE, symbol="RELIANCE"), + side=Side.BUY, + qty=1, + order_type=OrderType.MARKET, + product=ProductType.CNC, + ) + print("placed:", order_id) + ``` + +=== "Async" + + ```python + from tt_connect import AsyncTTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange, Side, ProductType, OrderType + + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + order_id = await broker.place_order( + instrument=Equity(exchange=Exchange.NSE, symbol="RELIANCE"), + side=Side.BUY, + qty=1, + order_type=OrderType.MARKET, + product=ProductType.CNC, + ) + print("placed:", order_id) + ``` ## Place order (limit) + ```python order_id = broker.place_order( instrument=Equity(exchange=Exchange.NSE, symbol="SBIN"), @@ -41,36 +63,32 @@ order_id = broker.place_order( ``` ## Modify and cancel + ```python broker.modify_order(order_id=order_id, price=801.0, qty=10) broker.cancel_order(order_id) ``` ## Read orders + ```python orders = broker.get_orders() for o in orders: print(o.id, o.status, o.qty) ``` -## Order flow -`PENDING -> OPEN -> COMPLETE` or `CANCELLED` or `REJECTED` +## Order lifecycle + +`PENDING → OPEN → COMPLETE` or `CANCELLED` or `REJECTED` ## Good patterns -- save returned order id -- check order state after placement -- handle rejection and avoid blind retries - -## What's next? -- [Trades](trades.md) — check fills after placing orders -- [Positions](positions.md) — monitor net open quantity -- [GTT (Trigger Orders)](gtt.md) — set up automated trigger-based orders - -## See also -- [Client methods (order APIs)](reference/clients.md) -- [Models (`Order`)](reference/models.md) -- [Enums (`OrderType`, `ProductType`, `Side`, `OrderStatus`)](reference/enums.md) -- [Exceptions](reference/exceptions.md) -- [Broker operation notes](reference/operation-notes.md) -- [Recipe: First order](recipes/first-order.md) -- [Recipe: Cancel all open orders](recipes/cancel-all-open-orders.md) + +- Save the returned order ID +- Check order status after placement +- Handle rejection — do not retry blindly + +## What's next + +- [Portfolio](portfolio.md) — check trades, positions, and holdings +- [GTT Orders](gtt.md) — set up automated trigger-based orders +- [Error Handling](error-handling.md) — handle failures in production diff --git a/docs_site/portfolio.md b/docs_site/portfolio.md new file mode 100644 index 0000000..27e1a72 --- /dev/null +++ b/docs_site/portfolio.md @@ -0,0 +1,126 @@ +# Portfolio + +## Overview + +| API | Returns | Use case | +|-----|---------|----------| +| `get_positions()` | Open trading exposure | Intraday P&L, risk monitoring | +| `get_holdings()` | Delivery inventory | Long-term portfolio reporting | +| `get_funds()` | Account balances | Pre-order fund checks | +| `get_trades()` | Execution fills | Reconciliation, audit trail | + +## Positions + +Positions represent current net open quantity for an instrument. + +- Positive qty = long +- Negative qty = short +- Zero qty = flat/closed + +=== "Sync" + + ```python + from tt_connect import TTConnect + + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + for p in broker.get_positions(): + print(p.instrument.symbol, "qty=", p.qty, "pnl=", p.pnl) + ``` + +=== "Async" + + ```python + from tt_connect import AsyncTTConnect + + async with AsyncTTConnect(broker_id, config) as broker: + for p in await broker.get_positions(): + print(p.instrument.symbol, "qty=", p.qty, "pnl=", p.pnl) + ``` + +### Close all positions + +=== "Sync" + + ```python + with TTConnect(broker_id, config) as broker: + placed, failed = broker.close_all_positions() + print("Close orders:", placed) + print("Failed:", failed) + ``` + +=== "Async" + + ```python + async with AsyncTTConnect(broker_id, config) as broker: + placed, failed = await broker.close_all_positions() + ``` + +!!! warning + `close_all_positions()` sends market orders. Always verify product/segment limits and confirm positions are flat afterward. + +## Holdings + +Holdings are delivery inventory — stocks carried in your demat account. + +=== "Sync" + + ```python + with TTConnect(broker_id, config) as broker: + for h in broker.get_holdings(): + print(h.instrument.symbol, "qty=", h.qty, "pnl=", h.pnl) + ``` + +=== "Async" + + ```python + async with AsyncTTConnect(broker_id, config) as broker: + for h in await broker.get_holdings(): + print(h.instrument.symbol, "qty=", h.qty, "pnl=", h.pnl) + ``` + +## Funds + +=== "Sync" + + ```python + with TTConnect(broker_id, config) as broker: + f = broker.get_funds() + print("Available:", f.available, "Used:", f.used, "Total:", f.total) + ``` + +=== "Async" + + ```python + async with AsyncTTConnect(broker_id, config) as broker: + f = await broker.get_funds() + print("Available:", f.available) + ``` + +## Trades + +A trade is an executed fill of an order. One order can generate multiple trades (partial fills). + +=== "Sync" + + ```python + with TTConnect(broker_id, config) as broker: + for t in broker.get_trades(): + print(t.order_id, t.instrument.symbol, t.side, "qty=", t.qty, "avg=", t.avg_price) + ``` + +=== "Async" + + ```python + async with AsyncTTConnect(broker_id, config) as broker: + for t in await broker.get_trades(): + print(t.order_id, t.instrument.symbol, t.side, "qty=", t.qty) + ``` + +## Tips + +- Values change during market hours — fetch fresh data before acting +- Use fund checks before placing orders +- Use trades for reconciliation — match `trade.order_id` back to orders +- Field names are normalized across brokers, but broker math may differ slightly diff --git a/docs_site/positions.md b/docs_site/positions.md deleted file mode 100644 index 7bff9e3..0000000 --- a/docs_site/positions.md +++ /dev/null @@ -1,53 +0,0 @@ -# Positions - -## What is a position -Current net open quantity for an instrument. - -## Key points -- positive qty: long -- negative qty: short -- zero qty: closed/flat - -## Read open positions -```python -from tt_connect import TTConnect - -config = {"api_key": "...", "access_token": "..."} - -with TTConnect("zerodha", config) as broker: - positions = broker.get_positions() - for p in positions: - print( - p.instrument.exchange, - p.instrument.symbol, - "qty=", p.qty, - "avg=", p.avg_price, - "ltp=", p.ltp, - "pnl=", p.pnl, - "product=", p.product, - ) -``` - -## Close all open positions -```python -with TTConnect("zerodha", config) as broker: - placed_order_ids, failed_symbols = broker.close_all_positions() - print("Close orders:", placed_order_ids) - print("Failed:", failed_symbols) -``` - -## Suggested check after close -```python -with TTConnect("zerodha", config) as broker: - still_open = [p for p in broker.get_positions() if p.qty != 0] - print("Open count after close:", len(still_open)) -``` - -## Caution -close-all sends market actions; always verify product/segment limits. - -## See also -- [Client methods (`get_positions`, `close_all_positions`)](reference/clients.md) -- [Models (`Position`)](reference/models.md) -- [Enums (`ProductType`, `Side`)](reference/enums.md) -- [Recipe: Close all open positions](recipes/close-all-open-positions.md) diff --git a/docs_site/realtime-websocket.md b/docs_site/realtime-websocket.md deleted file mode 100644 index 2df0d6c..0000000 --- a/docs_site/realtime-websocket.md +++ /dev/null @@ -1,131 +0,0 @@ -# Realtime (WebSocket) - -Use `AsyncTTConnect` for WebSocket subscribe/unsubscribe. - -## Minimal example - -```python -import asyncio -from tt_connect import AsyncTTConnect -from tt_connect.instruments import Equity -from tt_connect.enums import Exchange -from tt_connect import Tick - -async def on_tick(tick: Tick) -> None: - print(tick.instrument.symbol, tick.ltp, tick.timestamp) - -async def main() -> None: - config = {"api_key": "...", "access_token": "..."} - - async with AsyncTTConnect("zerodha", config) as broker: - watch = [ - Equity(exchange=Exchange.NSE, symbol="RELIANCE"), - Equity(exchange=Exchange.NSE, symbol="SBIN"), - ] - await broker.subscribe(watch, on_tick) - await asyncio.sleep(30) - await broker.unsubscribe(watch) - -asyncio.run(main()) -``` - -## Feed health callbacks - -Subscribe accepts two optional callbacks that fire when the feed goes silent or recovers: - -```python -import asyncio -from tt_connect import AsyncTTConnect -from tt_connect.instruments import Equity -from tt_connect.enums import Exchange -from tt_connect import Tick - -async def on_tick(tick: Tick) -> None: - print(tick.instrument.symbol, tick.ltp) - -async def on_stale() -> None: - # No tick received for 30 seconds — alert or pause your algo - print("Feed is stale — no data") - -async def on_recovered() -> None: - # Ticks are flowing again after a stale period - print("Feed recovered — resuming") - -async def main() -> None: - config = {"api_key": "...", "access_token": "..."} - - async with AsyncTTConnect("angelone", config) as broker: - watch = [Equity(exchange=Exchange.NSE, symbol="RELIANCE")] - await broker.subscribe( - watch, - on_tick, - on_stale=on_stale, - on_recovered=on_recovered, - ) - await asyncio.sleep(120) - -asyncio.run(main()) -``` - -`on_stale` fires once when the feed crosses the **30-second silence threshold**. -`on_recovered` fires on the first tick after a stale period. - -Both callbacks work identically on Zerodha and AngelOne. - -## Feed state - -Check `broker.feed_state` at any point to read the current stream health: - -```python -from tt_connect.enums import FeedState - -if broker.feed_state == FeedState.STALE: - print("No data — market may be closed or connection degraded") - -if broker.feed_state == FeedState.CONNECTED: - print("Stream is healthy") -``` - -| State | Meaning | -|---|---| -| `CONNECTING` | Initial state before first connect | -| `CONNECTED` | Ticks are flowing normally | -| `STALE` | Connected but no tick for 30+ seconds | -| `RECONNECTING` | Connection lost — reconnect attempt in progress | -| `CLOSED` | Client was closed or `unsubscribe` called | - -## Per-instrument last tick time - -Check when a specific instrument last produced a tick: - -```python -from datetime import datetime, timezone, timedelta - -IST = timezone(timedelta(hours=5, minutes=30)) -age = (datetime.now(IST) - broker.last_tick_at(reliance)).total_seconds() -if age > 60: - print(f"RELIANCE tick is {age:.0f}s old") -``` - -Returns `None` if no tick has been received yet for that instrument. - -## Reliability behavior - -- Reconnect is automatic with exponential backoff (cap: 60 seconds) -- All subscriptions are restored after every reconnect -- `on_stale` / `on_recovered` continue to work across reconnects - -## Callback best practices - -- Keep callbacks fast and non-blocking -- Push heavy work (DB writes, HTTP calls) to a queue/worker -- Catch your own business logic errors inside callbacks — unhandled exceptions are logged but do not crash the stream - -## See also - -- [Client methods (`subscribe`, `unsubscribe`, `feed_state`)](reference/clients.md) -- [Enums (`FeedState`)](reference/enums.md) -- [Models (`Tick`)](reference/models.md) -- [Recipe: Stream and store live ticks](recipes/stream-and-store-live-ticks.md) -- [Recipe: Recover from reconnect](recipes/recover-from-reconnect.md) -- [Troubleshooting: WebSocket reconnect](troubleshooting/websocket-reconnect.md) diff --git a/docs_site/recipes/cancel-all-open-orders.md b/docs_site/recipes/cancel-all-open-orders.md index 813a560..069fbc3 100644 --- a/docs_site/recipes/cancel-all-open-orders.md +++ b/docs_site/recipes/cancel-all-open-orders.md @@ -2,26 +2,38 @@ Use this during risk-off or session cleanup. -```python -from tt_connect import TTConnect +=== "Sync" -config = {"api_key": "...", "access_token": "..."} + ```python + from tt_connect import TTConnect -with TTConnect("zerodha", config) as broker: - cancelled, failed = broker.cancel_all_orders() - print("Cancelled:", cancelled) - print("Failed:", failed) -``` + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + cancelled, failed = broker.cancel_all_orders() + print("Cancelled:", cancelled) + print("Failed:", failed) + ``` + +=== "Async" + + ```python + from tt_connect import AsyncTTConnect + + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + cancelled, failed = await broker.cancel_all_orders() + print("Cancelled:", cancelled) + print("Failed:", failed) + ``` ## Suggested checks -- Run once and record counts. -- If any failed, fetch `get_orders()` and inspect status/reason. -## What's next? -- [Close all open positions](close-all-open-positions.md) — exit all positions after cancelling orders -- [Safety & Good Practices](../safety-and-good-practices.md) — production checklist +- Run once and record counts +- If any failed, fetch `get_orders()` and inspect status/reason -## Related reference -- [Client methods (`cancel_all_orders`, `get_orders`)](../reference/clients.md) -- [Exceptions](../reference/exceptions.md) -- [Troubleshooting: Duplicate orders](../troubleshooting/duplicate-orders.md) +## What's next + +- [Close all open positions](close-all-open-positions.md) — exit all positions after cancelling orders +- [Error Handling](../error-handling.md) — production checklist diff --git a/docs_site/recipes/close-all-open-positions.md b/docs_site/recipes/close-all-open-positions.md index 5517088..c5c5037 100644 --- a/docs_site/recipes/close-all-open-positions.md +++ b/docs_site/recipes/close-all-open-positions.md @@ -1,28 +1,40 @@ # Recipe: Close All Open Positions -This places offsetting market actions for open positions. +This places offsetting market orders for all open positions. -```python -from tt_connect import TTConnect +=== "Sync" -config = {"api_key": "...", "access_token": "..."} + ```python + from tt_connect import TTConnect -with TTConnect("zerodha", config) as broker: - placed_order_ids, failed_symbols = broker.close_all_positions() - print("Placed close orders:", placed_order_ids) - print("Failed symbols:", failed_symbols) -``` + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + placed, failed = broker.close_all_positions() + print("Placed close orders:", placed) + print("Failed symbols:", failed) + ``` + +=== "Async" + + ```python + from tt_connect import AsyncTTConnect + + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + placed, failed = await broker.close_all_positions() + print("Placed close orders:", placed) + print("Failed symbols:", failed) + ``` ## Important -- This is a high-impact action. -- Verify open positions before and after. -- Run in controlled environments first. -## What's next? -- [Cancel all open orders](cancel-all-open-orders.md) — cancel pending orders before closing positions -- [Errors & Retries](../errors-and-retries.md) — handle partial failures gracefully +- This is a high-impact action +- Verify open positions before and after +- Run in controlled environments first -## Related reference -- [Client methods (`get_positions`, `close_all_positions`)](../reference/clients.md) -- [Models (`Position`)](../reference/models.md) -- [Safety & Good Practices](../safety-and-good-practices.md) +## What's next + +- [Cancel all open orders](cancel-all-open-orders.md) — cancel pending orders before closing positions +- [Error Handling](../error-handling.md) — handle partial failures gracefully diff --git a/docs_site/recipes/first-order.md b/docs_site/recipes/first-order.md index 2fbd304..fc6b182 100644 --- a/docs_site/recipes/first-order.md +++ b/docs_site/recipes/first-order.md @@ -1,22 +1,35 @@ # Recipe: First Order -This is the shortest safe path to place your first order. +The shortest safe path to place your first order. -## 1) Create client -```python -from tt_connect import TTConnect +## 1) Create client and check funds -config = {"api_key": "...", "access_token": "..."} -broker = TTConnect("zerodha", config) -``` +=== "Sync" -## 2) Check funds -```python -funds = broker.get_funds() -print("Available:", funds.available) -``` + ```python + from tt_connect import TTConnect + + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + funds = broker.get_funds() + print("Available:", funds.available) + ``` + +=== "Async" + + ```python + from tt_connect import AsyncTTConnect + + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + funds = await broker.get_funds() + print("Available:", funds.available) + ``` + +## 2) Place a small order -## 3) Place a small order ```python from tt_connect.instruments import Equity from tt_connect.enums import Exchange, Side, ProductType, OrderType @@ -31,29 +44,21 @@ order_id = broker.place_order( print("Order ID:", order_id) ``` -## 4) Confirm status +## 3) Confirm status + ```python orders = broker.get_orders() match = next((o for o in orders if o.id == order_id), None) print(match.status if match else "not found") ``` -## 5) Close client -```python -broker.close() -``` - ## Notes -- Use very small quantity for first run. -- If rejected, print full order details and verify product/order type. -## What's next? +- Use very small quantity for your first run +- If rejected, print full order details and verify product / order type + +## What's next + - [Cancel all open orders](cancel-all-open-orders.md) — clean up during testing -- [Stream and store live ticks](stream-and-store-live-ticks.md) — get live market data -- [Errors & Retries](../errors-and-retries.md) — handle failures in production - -## Related reference -- [Client methods (orders)](../reference/clients.md) -- [Models (`Order`)](../reference/models.md) -- [Enums (`OrderType`, `ProductType`, `Side`)](../reference/enums.md) -- [Troubleshooting: Order rejected](../troubleshooting/order-rejected.md) +- [Stream live ticks](stream-and-store-live-ticks.md) — get live market data +- [Error Handling](../error-handling.md) — handle failures in production diff --git a/docs_site/recipes/index.md b/docs_site/recipes/index.md deleted file mode 100644 index 84fdab7..0000000 --- a/docs_site/recipes/index.md +++ /dev/null @@ -1,15 +0,0 @@ -# Recipes - -This section contains task-focused examples. - -Available recipes: -- [First order](first-order.md) -- [Cancel all open orders](cancel-all-open-orders.md) -- [Close all open positions](close-all-open-positions.md) -- [Stream and store live ticks](stream-and-store-live-ticks.md) -- [Recover from reconnect during market hours](recover-from-reconnect.md) - -Helpful references: -- [Client methods](../reference/clients.md) -- [Models](../reference/models.md) -- [Broker operation notes](../reference/operation-notes.md) diff --git a/docs_site/recipes/recover-from-reconnect.md b/docs_site/recipes/recover-from-reconnect.md deleted file mode 100644 index ef92a58..0000000 --- a/docs_site/recipes/recover-from-reconnect.md +++ /dev/null @@ -1,70 +0,0 @@ -# Recipe: Recover From Reconnect During Market Hours - -The WebSocket client reconnects automatically. Use `on_stale` and `on_recovered` to -react when data stops flowing and when it resumes — no manual timestamp tracking needed. - -```python -import asyncio -from tt_connect import AsyncTTConnect -from tt_connect.instruments import Equity -from tt_connect.enums import Exchange -from tt_connect import Tick - -algo_active = True - -async def on_tick(tick: Tick) -> None: - if not algo_active: - return - print(tick.instrument.symbol, tick.ltp) - -async def on_stale() -> None: - global algo_active - algo_active = False - print("Feed stale — algo paused") - -async def on_recovered() -> None: - global algo_active - algo_active = True - print("Feed recovered — algo resumed") - -async def main() -> None: - config = {"api_key": "...", "access_token": "..."} - watch = [Equity(exchange=Exchange.NSE, symbol="RELIANCE")] - - async with AsyncTTConnect("zerodha", config) as broker: - await broker.subscribe( - watch, - on_tick, - on_stale=on_stale, - on_recovered=on_recovered, - ) - await asyncio.sleep(120) - -asyncio.run(main()) -``` - -## How it works - -| Event | When | What to do | -|---|---|---| -| `on_stale` | No tick for 30 seconds | Pause algo, alert, stop placing orders | -| `on_recovered` | First tick after stale period | Resume algo, resync state if needed | - -Both fire across reconnects — you do not need to re-subscribe after a disconnect. - -## Tips - -- Do not assume tick ordering is preserved across a reconnect boundary -- If your strategy holds state per tick (e.g. running VWAP), resync it in `on_recovered` -- `broker.last_tick_at(instrument)` gives the exact wall-clock time of the last received tick if you need finer control - -## What's next? -- [Safety & Good Practices](../safety-and-good-practices.md) — production checklist for live trading -- [Broker Differences](../broker-differences.md) — understand per-broker WebSocket behavior - -## Related reference - -- [Realtime (WebSocket)](../realtime-websocket.md) -- [Client methods (`subscribe`, `feed_state`, `last_tick_at`)](../reference/clients.md) -- [Enums (`FeedState`)](../reference/enums.md) -- [Troubleshooting: WebSocket reconnect](../troubleshooting/websocket-reconnect.md) diff --git a/docs_site/recipes/stream-and-store-live-ticks.md b/docs_site/recipes/stream-and-store-live-ticks.md index 3f12810..8567a86 100644 --- a/docs_site/recipes/stream-and-store-live-ticks.md +++ b/docs_site/recipes/stream-and-store-live-ticks.md @@ -5,10 +5,9 @@ This example streams ticks and writes simple CSV rows. ```python import asyncio from pathlib import Path -from tt_connect import AsyncTTConnect +from tt_connect import AsyncTTConnect, Tick from tt_connect.instruments import Equity from tt_connect.enums import Exchange -from tt_connect import Tick OUT = Path("ticks.csv") @@ -24,7 +23,7 @@ async def main() -> None: Equity(exchange=Exchange.NSE, symbol="SBIN"), ] - async with AsyncTTConnect("zerodha", config) as broker: + async with AsyncTTConnect(broker_id, config) as broker: await broker.subscribe(watch, on_tick) await asyncio.sleep(30) await broker.unsubscribe(watch) @@ -33,13 +32,9 @@ asyncio.run(main()) ``` ## Practical note + For high tick rates, use an in-memory queue + background writer instead of writing per tick. -## What's next? -- [Recover from reconnect](recover-from-reconnect.md) — handle feed interruptions gracefully -- [Realtime (WebSocket)](../realtime-websocket.md) — full feed health and staleness guide +## What's next -## Related reference -- [Client methods (`subscribe`, `unsubscribe`)](../reference/clients.md) -- [Models (`Tick`)](../reference/models.md) -- [Broker operation notes](../reference/operation-notes.md) +- [WebSocket](../websocket.md) — full feed health and reconnect guide diff --git a/docs_site/reference/capabilities.md b/docs_site/reference/capabilities.md deleted file mode 100644 index c69a44a..0000000 --- a/docs_site/reference/capabilities.md +++ /dev/null @@ -1,30 +0,0 @@ -# Broker Capabilities - -## Capability matrix - -| Capability | Zerodha | AngelOne | -|---|---|---| -| Auth modes | `manual` | `manual`, `auto` | -| Segments | `NSE`, `BSE`, `NFO`, `BFO`, `CDS` | `NSE`, `BSE`, `NFO`, `CDS`, `MCX` | -| Order types | `MARKET`, `LIMIT`, `SL`, `SL_M` | `MARKET`, `LIMIT`, `SL`, `SL_M` | -| Product types | `CNC`, `MIS`, `NRML` | `CNC`, `MIS`, `NRML` | - -## Pre-order validation checks - -| Check | What is validated | -|---|---| -| Segment check | Instrument exchange is supported by broker | -| Order type check | Requested order type is allowed | -| Product check | Requested product type is allowed | -| Tradeability check | Index instruments are blocked for order placement | - -## Practical guidance -- Validate capability-sensitive flows in integration tests per broker. -- Keep fallback behavior for broker-specific endpoint differences. -- For strategy portability, treat broker differences as expected behavior. - -## Related guides -- [Broker Differences](../broker-differences.md) -- [Broker operation notes](operation-notes.md) -- [Orders](../orders.md) -- [GTT](../gtt.md) diff --git a/docs_site/reference/clients.md b/docs_site/reference/clients.md index 02144e3..ba5d8e2 100644 --- a/docs_site/reference/clients.md +++ b/docs_site/reference/clients.md @@ -1,76 +1,149 @@ -# Client Methods +# Client API -## Constructors +## Trading clients + +### Constructors | Client | Signature | Notes | |---|---|---| | Sync | `TTConnect(broker: str, config: dict[str, Any])` | Initializes and logs in during construction | | Async | `AsyncTTConnect(broker: str, config: dict[str, Any])` | Call `await init()` before use (or use `async with`) | -## Lifecycle methods +### Lifecycle methods | Client | Method | Params | Returns | Common errors | |---|---|---|---|---| -| Sync | `close` | none | `None` | `TTConnectError` | -| Async | `init` | none | `None` | `AuthenticationError`, `ConfigurationError`, `TTConnectError` | -| Async | `close` | none | `None` | `TTConnectError` | -| Async | `subscribe` | `instruments`, `on_tick`, `on_stale=None`, `on_recovered=None` | `None` | `ClientNotConnectedError`, `InstrumentNotFoundError`, `TTConnectError` | -| Async | `unsubscribe` | `instruments` | `None` | `ClientNotConnectedError`, `TTConnectError` | +| Sync | `close` | — | `None` | `TTConnectError` | +| Async | `init` | — | `None` | `AuthenticationError`, `ConfigurationError`, `TTConnectError` | +| Async | `close` | — | `None` | `TTConnectError` | +| Async | `subscribe` | `instruments`, `on_tick`, `on_stale=None`, `on_recovered=None` | `None` | `ClientNotConnectedError`, `InstrumentNotFoundError` | +| Async | `unsubscribe` | `instruments` | `None` | `ClientNotConnectedError` | | Async | `feed_state` *(property)* | — | `FeedState` | — | | Async | `last_tick_at` | `instrument: Instrument` | `datetime \| None` | — | -## Portfolio / account methods +### Portfolio / account methods | Method | Params | Returns | Common errors | |---|---|---|---| -| `get_profile` | none | `Profile` | `ClientNotConnectedError`, `AuthenticationError`, `BrokerError` | -| `get_funds` | none | `Fund` | `ClientNotConnectedError`, `AuthenticationError`, `BrokerError` | -| `get_holdings` | none | `list[Holding]` | `ClientNotConnectedError`, `AuthenticationError`, `BrokerError` | -| `get_positions` | none | `list[Position]` | `ClientNotConnectedError`, `AuthenticationError`, `BrokerError` | -| `get_trades` | none | `list[Trade]` | `ClientNotConnectedError`, `AuthenticationError`, `BrokerError` | -| `get_quotes` | `instruments: list[Instrument]` | `list[Tick]` | `ClientNotConnectedError`, `InstrumentNotFoundError`, `UnsupportedFeatureError` | -| `get_historical` | `instrument`, `interval`, `from_date`, `to_date` | `list[Candle]` | `ClientNotConnectedError`, `InstrumentNotFoundError`, `UnsupportedFeatureError` | +| `get_profile` | — | `Profile` | `AuthenticationError`, `BrokerError` | +| `get_funds` | — | `Fund` | `AuthenticationError`, `BrokerError` | +| `get_holdings` | — | `list[Holding]` | `AuthenticationError`, `BrokerError` | +| `get_positions` | — | `list[Position]` | `AuthenticationError`, `BrokerError` | +| `get_trades` | — | `list[Trade]` | `AuthenticationError`, `BrokerError` | +| `get_quotes` | `instruments: list[Instrument]` | `list[Tick]` | `InstrumentNotFoundError`, `UnsupportedFeatureError` | +| `get_historical` | `instrument`, `interval`, `from_date`, `to_date` | `list[Candle]` | `InstrumentNotFoundError`, `UnsupportedFeatureError` | -## Order methods +### Order methods | Method | Params | Returns | Common errors | |---|---|---|---| -| `place_order` | `instrument, side, qty, order_type, product, price=None, trigger_price=None, tag=None` | `str` (order id) | `UnsupportedFeatureError`, `InstrumentNotFoundError`, `InsufficientFundsError`, `BrokerError` | - -!!! note "About `tag`" - `tag` is an optional client-side correlation ID for tracing orders. If omitted, a UUID is auto-generated. Useful for idempotency checks — pass the same tag and verify via `get_orders()` to detect duplicates before retrying. Sent as `tag` (Zerodha, max 20 chars) or `uniqueorderid` (AngelOne). +| `place_order` | `instrument, side, qty, order_type, product, price=None, trigger_price=None, tag=None` | `str` (order id) | `UnsupportedFeatureError`, `InsufficientFundsError`, `BrokerError` | | `modify_order` | `order_id, qty=None, price=None, trigger_price=None, order_type=None` | `None` | `OrderNotFoundError`, `InvalidOrderError`, `BrokerError` | | `cancel_order` | `order_id: str` | `None` | `OrderNotFoundError`, `BrokerError` | -| `cancel_all_orders` | none | `tuple[list[str], list[str]]` | `BrokerError`, `TTConnectError` | -| `get_order` | `order_id: str` | `Order` | `OrderNotFoundError`, `UnsupportedFeatureError`, `BrokerError` | -| `get_orders` | none | `list[Order]` | `BrokerError` | -| `close_all_positions` | none | `tuple[list[str], list[str]]` | `BrokerError`, `TTConnectError` | +| `cancel_all_orders` | — | `tuple[list[str], list[str]]` | `BrokerError` | +| `get_order` | `order_id: str` | `Order` | `OrderNotFoundError`, `BrokerError` | +| `get_orders` | — | `list[Order]` | `BrokerError` | +| `close_all_positions` | — | `tuple[list[str], list[str]]` | `BrokerError` | + +!!! note "About `tag`" + `tag` is an optional client-side correlation ID for tracing orders. If omitted, a UUID is auto-generated. Useful for idempotency checks — pass the same tag and verify via `get_orders()` to detect duplicates before retrying. -## GTT methods +### GTT methods | Method | Params | Returns | Common errors | |---|---|---|---| -| `place_gtt` | `instrument, last_price, legs: list[GttLeg]` | `str` (gtt id) | `UnsupportedFeatureError`, `InstrumentNotFoundError`, `BrokerError` | -| `modify_gtt` | `gtt_id, instrument, last_price, legs: list[GttLeg]` | `None` | `UnsupportedFeatureError`, `InstrumentNotFoundError`, `BrokerError` | +| `place_gtt` | `instrument, last_price, legs: list[GttLeg]` | `str` (gtt id) | `UnsupportedFeatureError`, `BrokerError` | +| `modify_gtt` | `gtt_id, instrument, last_price, legs: list[GttLeg]` | `None` | `UnsupportedFeatureError`, `BrokerError` | | `cancel_gtt` | `gtt_id: str` | `None` | `UnsupportedFeatureError`, `BrokerError` | | `get_gtt` | `gtt_id: str` | `Gtt` | `UnsupportedFeatureError`, `BrokerError` | -| `get_gtts` | none | `list[Gtt]` | `UnsupportedFeatureError`, `BrokerError` | +| `get_gtts` | — | `list[Gtt]` | `UnsupportedFeatureError`, `BrokerError` | -## Instrument helper methods +### Instrument helper methods | Method | Params | Returns | Common errors | |---|---|---|---| -| `get_futures` | `instrument: Instrument` | `list[Future]` | `ClientNotConnectedError`, `TTConnectError` | -| `get_options` | `instrument: Instrument`, `expiry: date | None = None` | `list[Option]` | `ClientNotConnectedError`, `TTConnectError` | -| `get_expiries` | `instrument: Instrument` | `list[date]` | `ClientNotConnectedError`, `TTConnectError` | -| `search_instruments` | `query: str`, `exchange: str | None = None` | `list[Equity]` | `ClientNotConnectedError`, `TTConnectError` | - -## Notes -- `TTConnect` calls async internals in a dedicated background event-loop thread. -- Use `AsyncTTConnect` for websocket-heavy flows. - -## Related guides -- [Basics](../basics.md) -- [Orders](../orders.md) -- [Market Data](../market-data.md) -- [Realtime (WebSocket)](../realtime-websocket.md) +| `get_futures` | `instrument: Instrument` | `list[Future]` | `ClientNotConnectedError` | +| `get_options` | `instrument: Instrument`, `expiry: date \| None = None` | `list[Option]` | `ClientNotConnectedError` | +| `get_expiries` | `instrument: Instrument` | `list[date]` | `ClientNotConnectedError` | +| `search_instruments` | `query: str`, `exchange: str \| None = None` | `list[Equity]` | `ClientNotConnectedError` | + +### Notes + +- `TTConnect` runs async internals in a dedicated background event-loop thread. +- Use `AsyncTTConnect` for WebSocket-heavy flows. + +--- + +## InstrumentStore + +`InstrumentStore` and `AsyncInstrumentStore` provide **read-only** access to the local instrument cache without authenticating with a broker. Use them for instrument discovery, metadata, or option-chain browsing without placing trades. + +!!! note "Seed the DB first" + The store reads from a local SQLite DB populated by `TTConnect` / `AsyncTTConnect` during `init()`. If the DB has not been seeded, store initialization fails with a clear error. + +### Constructors + +| Class | Signature | Notes | +|---|---|---| +| Sync | `InstrumentStore(broker: str)` | Opens DB and starts background event loop | +| Async | `AsyncInstrumentStore(broker: str)` | Call `await init()` or use `async with` | + +Both support context managers (`with` / `async with`). + +### Quick example + +=== "Sync" + + ```python + from tt_connect import InstrumentStore + from tt_connect.instruments import Index + from tt_connect.enums import Exchange + + with InstrumentStore(broker_id) as store: + results = store.search("NIFTY") + + nifty = Index(exchange=Exchange.NSE, symbol="NIFTY") + expiries = store.get_expiries(nifty) + info = store.get_instrument_info(nifty) + print(f"Lot size: {info.lot_size}, Tick size: {info.tick_size}") + + chain = store.get_option_chain(nifty, expiries[0]) + for entry in chain.entries[:5]: + print(f" {entry.strike} CE={entry.ce} PE={entry.pe}") + ``` + +=== "Async" + + ```python + from tt_connect import AsyncInstrumentStore + from tt_connect.instruments import Index + from tt_connect.enums import Exchange + + async with AsyncInstrumentStore(broker_id) as store: + nifty = Index(exchange=Exchange.NSE, symbol="NIFTY") + expiries = await store.get_expiries(nifty) + chain = await store.get_option_chain(nifty, expiries[0]) + ``` + +### Methods + +| Method | Params | Returns | Description | +|---|---|---|---| +| `list_instruments` | `instrument_type=None, exchange=None, underlying=None, expiry=None, option_type=None, strike=None, strike_min=None, strike_max=None, has_derivatives=None, limit=100` | `list[Instrument]` | Filter instruments with any criteria | +| `get_expiries` | `instrument: Instrument` | `list[date]` | Distinct expiry dates for an underlying | +| `search` | `query: str, exchange: str \| None = None` | `list[Equity \| Index]` | Search underlyings by symbol substring | +| `get_instrument_info` | `instrument: Instrument` | `InstrumentInfo` | Metadata: lot size, tick size, segment, name | +| `get_option_chain` | `underlying: Instrument, expiry: date` | `OptionChain` | CE/PE pairs for all strikes | +| `execute` | `sql: str, params: tuple = ()` | `list[tuple]` | Raw SQL against the local DB | +| `close` | — | `None` | Close the DB connection | + +### When to use InstrumentStore vs TTConnect + +| | `TTConnect` | `InstrumentStore` | +|---|---|---| +| Authenticates | Yes | No | +| Refreshes instruments | Yes (on init) | No | +| Place orders | Yes | No | +| Instrument discovery | Yes | Yes (+ `list_instruments`, `search`, `get_option_chain`) | +| Raw SQL | No | Yes (`execute`) | +| Use case | Trading | Research, strategy tooling, option chain analysis | diff --git a/docs_site/reference/enums.md b/docs_site/reference/enums.md deleted file mode 100644 index f2491ea..0000000 --- a/docs_site/reference/enums.md +++ /dev/null @@ -1,33 +0,0 @@ -# Enums - -## Public enums - -Import from `tt_connect.enums`: - -| Enum | Values | Typical usage | -|---|---|---| -| `Exchange` | `NSE`, `BSE`, `NFO`, `BFO`, `CDS`, `MCX` | Instrument exchange/segment | -| `OptionType` | `CE`, `PE` | Option side | -| `ProductType` | `CNC`, `MIS`, `NRML` | Order product/margin type | -| `OrderType` | `MARKET`, `LIMIT`, `SL`, `SL_M` | Order execution style | -| `Side` | `BUY`, `SELL` | Order direction | -| `OrderStatus` | `PENDING`, `OPEN`, `COMPLETE`, `CANCELLED`, `REJECTED` | Normalized order state | -| `FeedState` | `connecting`, `connected`, `reconnecting`, `stale`, `closed` | WebSocket feed health state | -| `CandleInterval` | `1minute`, `3minute`, `5minute`, `10minute`, `15minute`, `30minute`, `60minute`, `day` | Historical candle interval | - -## Internal enums - -These are used by config dicts and client internals — do not import them in user code: - -| Enum | Used where | -|---|---| -| `OnStale` | `on_stale` config key (`"fail"` or `"warn"`) | -| `AuthMode` | `auth_mode` config key (`"manual"` or `"auto"`) | -| `ClientState` | Internal client lifecycle tracking | - -## Related guides -- [Orders](../orders.md) -- [Instruments](../instruments.md) -- [Login & Session](../login-and-session.md) -- [Market Data](../market-data.md) -- [Realtime (WebSocket)](../realtime-websocket.md) diff --git a/docs_site/reference/exceptions.md b/docs_site/reference/exceptions.md index 29e6b90..c8e4af5 100644 --- a/docs_site/reference/exceptions.md +++ b/docs_site/reference/exceptions.md @@ -20,11 +20,12 @@ | `ClientClosedError` | Method called after client closed | No | ## Handling pattern + - Catch specific errors first (`InsufficientFundsError`, `RateLimitError`, `AuthenticationError`). - Use `TTConnectError` as final fallback. -- Retry only when it is clearly transient (for example `RateLimitError`). +- Retry only when clearly transient (e.g. `RateLimitError`). + +## Related -## Related guides -- [Errors & Retries](../errors-and-retries.md) -- [Login & Session](../login-and-session.md) -- [Safety & Good Practices](../safety-and-good-practices.md) +- [Error Handling](../error-handling.md) +- [Authentication](../authentication.md) diff --git a/docs_site/reference/index.md b/docs_site/reference/index.md deleted file mode 100644 index bcbc8a5..0000000 --- a/docs_site/reference/index.md +++ /dev/null @@ -1,17 +0,0 @@ -# API Reference - -This section is for exact method/model details. - -Available pages: -- [Client methods](clients.md) -- [Models](models.md) -- [Enums](enums.md) -- [Exceptions](exceptions.md) -- [Broker capabilities](capabilities.md) -- [Broker operation notes](operation-notes.md) - -## Quick jump from guides -- [Orders guide](../orders.md) -> [Client methods](clients.md), [Models](models.md), [Enums](enums.md) -- [Market Data guide](../market-data.md) -> [Client methods](clients.md), [Models](models.md), [Enums](enums.md) -- [Login & Session guide](../login-and-session.md) -> [Client methods](clients.md), [Exceptions](exceptions.md), [Capabilities](capabilities.md) -- [Broker Differences guide](../broker-differences.md) -> [Capabilities](capabilities.md), [Operation notes](operation-notes.md) diff --git a/docs_site/reference/instrument-store.md b/docs_site/reference/instrument-store.md deleted file mode 100644 index be83b99..0000000 --- a/docs_site/reference/instrument-store.md +++ /dev/null @@ -1,113 +0,0 @@ -# InstrumentStore - -`InstrumentStore` and `AsyncInstrumentStore` provide **read-only** access to the local instrument cache without authenticating with a broker. - -Use them when you need instrument discovery, metadata, or option-chain browsing without placing trades. - -!!! note "Seed the DB first" - The store reads from a local SQLite DB that is populated by `TTConnect` / `AsyncTTConnect` during `init()`. If the DB has not been seeded yet, store initialization will fail with a clear error. - -## Quick example - -=== "Sync" - - ```python - from tt_connect import InstrumentStore - from tt_connect.instruments import Index, Future, Option - from tt_connect.enums import Exchange - - with InstrumentStore("zerodha") as store: - # Search by symbol - results = store.search("NIFTY") - - # List expiries - nifty = Index(exchange=Exchange.NSE, symbol="NIFTY") - expiries = store.get_expiries(nifty) - - # Get metadata - info = store.get_instrument_info(nifty) - print(f"Lot size: {info.lot_size}, Tick size: {info.tick_size}") - - # Option chain - chain = store.get_option_chain(nifty, expiries[0]) - for entry in chain.entries[:5]: - print(f" {entry.strike} CE={entry.ce} PE={entry.pe}") - ``` - -=== "Async" - - ```python - from tt_connect import AsyncInstrumentStore - from tt_connect.instruments import Index - from tt_connect.enums import Exchange - - async with AsyncInstrumentStore("zerodha") as store: - nifty = Index(exchange=Exchange.NSE, symbol="NIFTY") - expiries = await store.get_expiries(nifty) - chain = await store.get_option_chain(nifty, expiries[0]) - ``` - -## Constructor - -| Class | Signature | Notes | -|---|---|---| -| Sync | `InstrumentStore(broker: str)` | Opens DB and starts background event loop | -| Async | `AsyncInstrumentStore(broker: str)` | Call `await init()` or use `async with` | - -Both support context managers (`with` / `async with`) for automatic cleanup. - -## Methods - -| Method | Params | Returns | Description | -|---|---|---|---| -| `list_instruments` | `instrument_type=None, exchange=None, underlying=None, expiry=None, option_type=None, strike=None, strike_min=None, strike_max=None, has_derivatives=None, limit=100` | `list[Instrument]` | Filter instruments with any combination of criteria | -| `get_expiries` | `instrument: Instrument` | `list[date]` | All distinct expiry dates for an underlying | -| `search` | `query: str, exchange: str | None = None` | `list[Equity | Index]` | Search underlyings by symbol substring | -| `get_instrument_info` | `instrument: Instrument` | `InstrumentInfo` | Metadata: lot size, tick size, segment, name | -| `get_option_chain` | `underlying: Instrument, expiry: date` | `OptionChain` | CE/PE pairs for all strikes at a given expiry | -| `execute` | `sql: str, params: tuple = ()` | `list[tuple]` | Raw SQL escape hatch against the local DB | -| `close` | none | `None` | Close the DB connection | - -## Return types - -### InstrumentInfo - -| Field | Type | Description | -|---|---|---| -| `instrument` | `Instrument` | The resolved instrument | -| `name` | `str | None` | Human-readable name | -| `lot_size` | `int` | Trading lot size | -| `tick_size` | `float` | Minimum price increment | -| `segment` | `str` | Market segment | - -### OptionChain - -| Field | Type | Description | -|---|---|---| -| `underlying` | `Instrument` | The underlying instrument | -| `expiry` | `date` | Expiry date | -| `entries` | `list[OptionChainEntry]` | Strikes sorted ascending | - -### OptionChainEntry - -| Field | Type | Description | -|---|---|---| -| `strike` | `float` | Strike price | -| `ce` | `Option | None` | Call option at this strike | -| `pe` | `Option | None` | Put option at this strike | - -## When to use InstrumentStore vs TTConnect - -| | `TTConnect` | `InstrumentStore` | -|---|---|---| -| Authenticates | Yes | No | -| Refreshes instrument data | Yes (on `init()`) | No | -| Place orders | Yes | No | -| Instrument discovery | Yes (`get_futures`, `get_options`, `get_expiries`) | Yes (same + `list_instruments`, `search`, `get_option_chain`) | -| Raw SQL | No | Yes (`execute`) | -| Use case | Trading | Research, strategy tooling, option chain analysis | - -## Related - -- [Instruments guide](../instruments.md) -- [Example: `examples/angelone_store.py`](https://github.com/Tiny-Trader/connect/blob/main/examples/angelone_store.py) diff --git a/docs_site/reference/models.md b/docs_site/reference/models.md index 739d4e4..e469040 100644 --- a/docs_site/reference/models.md +++ b/docs_site/reference/models.md @@ -1,22 +1,20 @@ -# Models +# Models & Enums ## Instrument models -| Model | Required fields | Optional fields | Notes | -|---|---|---|---| -| `Instrument` | `exchange`, `symbol` | none | Base canonical instrument | -| `Index` | `exchange`, `symbol` | none | Not tradeable for order placement | -| `Equity` | `exchange`, `symbol` | none | Cash market instrument | -| `Future` | `exchange`, `symbol`, `expiry` | none | Derivative keyed by expiry | -| `Option` | `exchange`, `symbol`, `expiry`, `strike`, `option_type` | none | Derivative keyed by strike + CE/PE | -| `Currency` | `exchange`, `symbol` | none | Currency derivative shape | -| `Commodity` | `exchange`, `symbol` | none | Commodity derivative shape | +| Model | Required fields | Notes | +|---|---|---| +| `Instrument` | `exchange`, `symbol` | Base canonical instrument | +| `Index` | `exchange`, `symbol` | Not tradeable for order placement | +| `Equity` | `exchange`, `symbol` | Cash market instrument | +| `Future` | `exchange`, `symbol`, `expiry` | Derivative keyed by expiry | +| `Option` | `exchange`, `symbol`, `expiry`, `strike`, `option_type` | Derivative keyed by strike + CE/PE | +| `Currency` | `exchange`, `symbol` | Currency derivative | +| `Commodity` | `exchange`, `symbol` | Commodity derivative | ## GTT leg model -Order methods (`place_order`, `modify_order`, etc.) and GTT methods take keyword arguments -directly — there are no request objects to construct. The one exception is `GttLeg`, which -you compose to describe each leg of a GTT: +Order and GTT methods take keyword arguments directly — no request objects. The one exception is `GttLeg`: | Model | Required fields | Notes | |---|---|---| @@ -36,12 +34,12 @@ leg = GttLeg(trigger_price=790.0, price=789.5, side=Side.BUY, qty=1, product=Pro | `Profile` | `client_id`, `name`, `email` | `phone=None` | | `Fund` | `available`, `used`, `total` | `collateral=0.0`, `m2m_unrealized=0.0`, `m2m_realized=0.0` | | `Holding` | `instrument`, `qty`, `avg_price`, `ltp`, `pnl` | `pnl_percent=0.0` | -| `Position` | `instrument`, `qty`, `avg_price`, `ltp`, `pnl`, `product` | none | +| `Position` | `instrument`, `qty`, `avg_price`, `ltp`, `pnl`, `product` | — | | `Order` | `id`, `side`, `qty`, `filled_qty`, `product`, `order_type`, `status` | `instrument=None`, `price=None`, `trigger_price=None`, `avg_price=None`, `timestamp=None` | | `Trade` | `order_id`, `instrument`, `side`, `qty`, `avg_price`, `trade_value`, `product` | `timestamp=None` | | `Tick` | `instrument`, `ltp` | `volume=None`, `oi=None`, `bid=None`, `ask=None`, `timestamp=None` | | `Candle` | `instrument`, `timestamp`, `open`, `high`, `low`, `close`, `volume` | `oi=None` | -| `Gtt` | `gtt_id`, `status`, `symbol`, `exchange`, `legs` | none | +| `Gtt` | `gtt_id`, `status`, `symbol`, `exchange`, `legs` | — | | `Margin` | `total`, `span`, `exposure`, `final_total`, `benefit` | `option_premium=0.0` | ## Mutability @@ -49,10 +47,29 @@ leg = GttLeg(trigger_price=790.0, price=789.5, side=Side.BUY, qty=1, product=Pro | Model class | Mutability | |---|---| | `GttLeg` | Mutable | -| Most response models | Frozen/read-only | +| All response models | Frozen / read-only | + +## Public enums -## Related guides -- [Instruments](../instruments.md) -- [Orders](../orders.md) -- [Market Data](../market-data.md) -- [GTT](../gtt.md) +Import from `tt_connect.enums`: + +| Enum | Values | Typical usage | +|---|---|---| +| `Exchange` | `NSE`, `BSE`, `NFO`, `BFO`, `CDS`, `MCX` | Instrument exchange/segment | +| `OptionType` | `CE`, `PE` | Option side | +| `ProductType` | `CNC`, `MIS`, `NRML` | Order product/margin type | +| `OrderType` | `MARKET`, `LIMIT`, `SL`, `SL_M` | Order execution style | +| `Side` | `BUY`, `SELL` | Order direction | +| `OrderStatus` | `PENDING`, `OPEN`, `COMPLETE`, `CANCELLED`, `REJECTED` | Normalized order state | +| `FeedState` | `connecting`, `connected`, `reconnecting`, `stale`, `closed` | WebSocket feed health | +| `CandleInterval` | `1minute`, `3minute`, `5minute`, `10minute`, `15minute`, `30minute`, `60minute`, `day` | Historical candle interval | + +## Internal enums + +These are used by config dicts and client internals — do not import in user code: + +| Enum | Used where | +|---|---| +| `OnStale` | `on_stale` config key (`"fail"` or `"warn"`) | +| `AuthMode` | `auth_mode` config key (`"manual"` or `"auto"`) | +| `ClientState` | Internal client lifecycle tracking | diff --git a/docs_site/reference/operation-notes.md b/docs_site/reference/operation-notes.md deleted file mode 100644 index d619464..0000000 --- a/docs_site/reference/operation-notes.md +++ /dev/null @@ -1,55 +0,0 @@ -# Broker Operation Notes - -This page lists behavior differences that matter in real user flows. - -## Orders and order book - -| Operation | Zerodha | AngelOne | What you should do | -|---|---|---|---| -| `get_order(order_id)` | Supported | Not supported as single-order endpoint | Use `get_orders()` and filter by `order_id` for AngelOne | -| `get_orders()` | Returns full order book | Returns normalized list (empty list when broker returns null) | Always handle empty list safely | -| `cancel_all_orders()` | Works through list + cancel flow | Works through list + cancel flow | Capture both `cancelled` and `failed` IDs | - -## GTT - -| Operation | Zerodha | AngelOne | What you should do | -|---|---|---|---| -| `place_gtt` | Supported | Supported | Keep payload simple and broker-compatible | -| `modify_gtt` | Supported | Supported | Re-fetch rule after modify | -| `cancel_gtt` | Direct cancel by id | Uses rule details internally before cancel | Handle broker errors and retry only when transient | -| `get_gtts` | Returns list | Normalized to list even when broker gives dict/null | Treat response as list in user code | - -## Auth/session - -| Area | Zerodha | AngelOne | What you should do | -|---|---|---|---| -| Auth modes | `manual` only | `manual` + `auto` | Pick mode per broker capability | -| Token lifecycle | Access token expected from external login flow | Auto mode supports TOTP login + renew path | Use `cache_session` where appropriate | - -## Market data (REST) - -| Operation | Zerodha | AngelOne | What you should do | -|---|---|---|---| -| `get_quotes` | Supported | Not currently supported in adapter as REST quotes call | Prefer WebSocket for AngelOne live quote use cases | -| `get_historical` | Supported | Supported | Use same canonical candle request path | - -## WebSocket streaming - -| Area | Zerodha | AngelOne | What you should do | -|---|---|---|---| -| Subscribe mode | Uses full mode for richer fields | Uses snap-quote mode for richer fields | Keep callback tolerant to missing fields | -| Reconnect | Auto reconnect + resubscribe | Auto reconnect + resubscribe | Keep callback idempotent and fast | - -## Instrument and capability checks - -| Check | Behavior | -|---|---| -| Segment/order/product validation | Checked before order placement | -| Index tradeability | Index instruments are blocked for order placement | -| Instrument resolution | Canonical instrument is mapped to broker token/symbol before call | - -## Related guides -- [Broker Differences](../broker-differences.md) -- [Orders](../orders.md) -- [GTT](../gtt.md) -- [Market Data](../market-data.md) diff --git a/docs_site/safety-and-good-practices.md b/docs_site/safety-and-good-practices.md deleted file mode 100644 index cd8541a..0000000 --- a/docs_site/safety-and-good-practices.md +++ /dev/null @@ -1,65 +0,0 @@ -# Safety & Good Practices - -## Before placing orders -- validate instrument and quantity -- check available funds -- check market state/session - -## Pre-order safety check example -```python -from tt_connect import TTConnect -from tt_connect.instruments import Equity -from tt_connect.enums import Exchange, Side, ProductType, OrderType - -config = {"api_key": "...", "access_token": "..."} - -with TTConnect("zerodha", config) as broker: - funds = broker.get_funds() - if funds.available < 1000: - raise RuntimeError("Not enough funds for this strategy") - - order_id = broker.place_order( - instrument=Equity(exchange=Exchange.NSE, symbol="SBIN"), - side=Side.BUY, - qty=1, - order_type=OrderType.MARKET, - product=ProductType.CNC, - ) - print("placed:", order_id) -``` - -## Logging -- log order request id, order id, status changes -- keep enough data for audit/replay - -## Secrets -- never hardcode tokens -- use env vars or secret manager -- rotate credentials on leaks - -## Basic error guard example -```python -from tt_connect.exceptions import TTConnectError, AuthenticationError - -try: - profile = broker.get_profile() -except AuthenticationError: - # trigger re-auth flow - raise -except TTConnectError as e: - # alert + log for ops - print("tt-connect error:", e) - raise -``` - -## Production checklist -- retry policy configured -- alerts for auth/placement failures -- graceful shutdown closes client -- risk-off path tested (`cancel_all_orders`, `close_all_positions`) - -## See also -- [Errors & retries](errors-and-retries.md) -- [Exceptions](reference/exceptions.md) -- [Broker capabilities](reference/capabilities.md) -- [Recipe: Close all open positions](recipes/close-all-open-positions.md) diff --git a/docs_site/start-here.md b/docs_site/start-here.md deleted file mode 100644 index ff0ba7e..0000000 --- a/docs_site/start-here.md +++ /dev/null @@ -1,27 +0,0 @@ -# Start Here - -Pick your path. - -## Path A: Beginner (first successful order) -1. Read [Basics](basics.md) -2. Configure [Login & Session](login-and-session.md) -3. Verify keys in [Config & Environment](config-and-env.md) -4. Run [Recipe: First order](recipes/first-order.md) -5. If blocked, use [Troubleshooting](troubleshooting/index.md) - -## Path B: Production setup -1. Read [Config & Environment](config-and-env.md) -2. Read [Safety & Good Practices](safety-and-good-practices.md) -3. Read [Errors & Retries](errors-and-retries.md) -4. Read [Broker Differences](broker-differences.md) -5. Read [Broker Operation Notes](reference/operation-notes.md) -6. Test risk controls: - - [Cancel all open orders](recipes/cancel-all-open-orders.md) - - [Close all open positions](recipes/close-all-open-positions.md) -7. Add live monitoring from [Realtime (WebSocket)](realtime-websocket.md) - -## Path C: Data-first user -1. [Instruments](instruments.md) -2. [Market Data](market-data.md) -3. [Recipe: Stream and store live ticks](recipes/stream-and-store-live-ticks.md) -4. [API Reference: Models](reference/models.md) diff --git a/docs_site/trades.md b/docs_site/trades.md deleted file mode 100644 index bf852ff..0000000 --- a/docs_site/trades.md +++ /dev/null @@ -1,39 +0,0 @@ -# Trades - -## What is a trade -A trade is an executed fill of an order. - -## Important behavior -- one order can create many trades (partial fills) -- avg price is computed from fills -- trade timestamps are broker-provided - -## Read trades -```python -from tt_connect import TTConnect - -config = {"api_key": "...", "access_token": "..."} - -with TTConnect("zerodha", config) as broker: - trades = broker.get_trades() - for t in trades: - print( - t.order_id, - t.instrument.symbol, - t.side, - "qty=", t.qty, - "avg=", t.avg_price, - "value=", t.trade_value, - "ts=", t.timestamp, - ) -``` - -## Usage -- use trades for execution reports -- reconcile trades with final order status -- one order may appear in multiple trade entries if partially filled - -## See also -- [Client methods (`get_trades`)](reference/clients.md) -- [Models (`Trade`, `Order`)](reference/models.md) -- [Enums (`Side`, `OrderStatus`)](reference/enums.md) diff --git a/docs_site/troubleshooting/auth-failures.md b/docs_site/troubleshooting/auth-failures.md index c2e6997..6eccbdf 100644 --- a/docs_site/troubleshooting/auth-failures.md +++ b/docs_site/troubleshooting/auth-failures.md @@ -1,37 +1,50 @@ # Auth Failures ## Common symptoms + - `AuthenticationError` -- profile/funds call fails right after startup -- token works in one session but fails in another +- Profile/funds call fails right after startup +- Token works in one session but fails in another ## Fast checks -1. Confirm broker id is correct (`zerodha` or `angelone`). -2. Confirm required config keys are present. -3. Confirm token/session is still valid. -4. Confirm auth mode is supported by broker. + +1. Confirm broker ID is correct. +2. Confirm required config keys are present for your broker and auth mode. +3. Confirm token/session is still valid (tokens expire daily per SEBI rules). +4. Confirm auth mode is supported by your broker. ## Minimal test -```python -from tt_connect import TTConnect -config = {"api_key": "...", "access_token": "..."} +=== "Sync" + + ```python + from tt_connect import TTConnect + + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + print(broker.get_profile()) + ``` -with TTConnect("zerodha", config) as broker: - print(broker.get_profile()) -``` +=== "Async" -## Broker-specific notes -- Zerodha: manual token flow only. -- AngelOne: supports manual and auto; auto needs `client_id`, `pin`, `totp_secret`. + ```python + from tt_connect import AsyncTTConnect + + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + print(await broker.get_profile()) + ``` ## What to do next -- refresh/recreate token -- verify system clock and TOTP secret for auto login -- turn off stale cached session and retry + +- Refresh or recreate your access token +- Verify system clock and TOTP secret for auto login +- Disable cached session (`cache_session: False`) and retry +- Check [Broker Differences](../broker-differences.md) for per-broker auth details ## Related -- [Login & Session](../login-and-session.md) -- [Config & Environment](../config-and-env.md) + +- [Authentication](../authentication.md) - [Exceptions](../reference/exceptions.md) -- [Broker operation notes](../reference/operation-notes.md) diff --git a/docs_site/troubleshooting/duplicate-orders.md b/docs_site/troubleshooting/duplicate-orders.md index 8964e44..16561fe 100644 --- a/docs_site/troubleshooting/duplicate-orders.md +++ b/docs_site/troubleshooting/duplicate-orders.md @@ -1,16 +1,19 @@ # Duplicate Order Protection ## Common symptoms -- same intent appears multiple times in order book -- retries after timeout create accidental duplicates + +- Same intent appears multiple times in order book +- Retries after timeout create accidental duplicates ## Safety checklist + 1. Always store `order_id` from each placement. 2. Pass a `tag` kwarg for request correlation. 3. Before retrying, check recent orders for matching intent. 4. Retry only on clearly transient errors. ## Basic pattern + ```python order_id = broker.place_order( instrument=instrument, @@ -25,11 +28,12 @@ order_id = broker.place_order( ``` ## Operational controls -- keep short-term in-memory dedupe key -- alert when same symbol+side+qty repeats quickly -- use manual approval for replays in production + +- Keep a short-term in-memory dedupe key +- Alert when same symbol + side + qty repeats quickly +- Use manual approval for replays in production ## Related -- [Errors & Retries](../errors-and-retries.md) + +- [Error Handling](../error-handling.md) - [Orders](../orders.md) -- [Models (`Order`)](../reference/models.md) diff --git a/docs_site/troubleshooting/index.md b/docs_site/troubleshooting/index.md deleted file mode 100644 index d627a75..0000000 --- a/docs_site/troubleshooting/index.md +++ /dev/null @@ -1,9 +0,0 @@ -# Troubleshooting - -Use these pages when a flow fails in real use. - -- [Auth failures](auth-failures.md) -- [Instrument not found](instrument-not-found.md) -- [Order rejected](order-rejected.md) -- [WebSocket disconnect/reconnect](websocket-reconnect.md) -- [Duplicate order protection](duplicate-orders.md) diff --git a/docs_site/troubleshooting/instrument-not-found.md b/docs_site/troubleshooting/instrument-not-found.md index f78797c..d5830f6 100644 --- a/docs_site/troubleshooting/instrument-not-found.md +++ b/docs_site/troubleshooting/instrument-not-found.md @@ -1,35 +1,56 @@ # Instrument Not Found ## Common symptoms + - `InstrumentNotFoundError` -- order/quote call fails for specific symbol+expiry+strike +- Order or quote call fails for a specific symbol + expiry + strike ## Fast checks + 1. Verify exchange is correct (`NSE`, `BSE`, etc.). 2. Verify symbol spelling exactly. -3. For derivatives, verify expiry/strike/CE-PE exactly. -4. Use search/helper APIs before placing order. +3. For derivatives, verify expiry / strike / CE-PE exactly. +4. Use search and helper APIs before placing an order. ## Debug pattern -```python -from tt_connect import TTConnect -from tt_connect.instruments import Equity -from tt_connect.enums import Exchange -config = {"api_key": "...", "access_token": "..."} +=== "Sync" + + ```python + from tt_connect import TTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange + + config = {"api_key": "...", "access_token": "..."} + + with TTConnect(broker_id, config) as broker: + print(broker.search_instruments("SBIN", exchange="NSE")) + underlying = Equity(exchange=Exchange.NSE, symbol="SBIN") + print(broker.get_expiries(underlying)) + ``` -with TTConnect("zerodha", config) as broker: - print(broker.search_instruments("SBIN", exchange="NSE")) - underlying = Equity(exchange=Exchange.NSE, symbol="SBIN") - print(broker.get_expiries(underlying)) -``` +=== "Async" + + ```python + from tt_connect import AsyncTTConnect + from tt_connect.instruments import Equity + from tt_connect.enums import Exchange + + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + print(await broker.search_instruments("SBIN", exchange="NSE")) + underlying = Equity(exchange=Exchange.NSE, symbol="SBIN") + print(await broker.get_expiries(underlying)) + ``` ## Common root causes -- wrong exchange -- stale/invalid derivative contract values -- typo in symbol + +- Wrong exchange +- Stale or invalid derivative contract values +- Typo in symbol ## Related + - [Instruments](../instruments.md) -- [Models](../reference/models.md) -- [Client methods](../reference/clients.md) +- [Client API](../reference/clients.md) diff --git a/docs_site/troubleshooting/order-rejected.md b/docs_site/troubleshooting/order-rejected.md index f79b9cf..a0256c4 100644 --- a/docs_site/troubleshooting/order-rejected.md +++ b/docs_site/troubleshooting/order-rejected.md @@ -1,16 +1,19 @@ # Order Rejected ## Common symptoms -- order status becomes `REJECTED` + +- Order status becomes `REJECTED` - `BrokerError`, `InvalidOrderError`, or `InsufficientFundsError` ## Fast checks + 1. Check available funds before placing order. -2. Verify product type/order type is supported for broker+segment. -3. Verify required fields for order type (price/trigger). -4. Read latest order book entry for broker message. +2. Verify product type and order type are supported for your broker and segment. +3. Verify required fields for order type (price for LIMIT, trigger_price for SL). +4. Read latest order book entry for the broker's rejection message. ## Debug pattern + ```python orders = broker.get_orders() for o in orders[:10]: @@ -18,11 +21,13 @@ for o in orders[:10]: ``` ## Safe recovery -- Do not blindly retry same payload. -- Fix root cause first (funds/params/capability). -- Re-submit only after checks pass. + +- Do not blindly retry the same payload +- Fix the root cause first (funds, parameters, capability) +- Re-submit only after checks pass ## Related + - [Orders](../orders.md) -- [Errors & Retries](../errors-and-retries.md) -- [Broker capabilities](../reference/capabilities.md) +- [Error Handling](../error-handling.md) +- [Broker Differences](../broker-differences.md) diff --git a/docs_site/troubleshooting/websocket-reconnect.md b/docs_site/troubleshooting/websocket-reconnect.md deleted file mode 100644 index 235ddbe..0000000 --- a/docs_site/troubleshooting/websocket-reconnect.md +++ /dev/null @@ -1,78 +0,0 @@ -# WebSocket Disconnect/Reconnect - -## Expected behavior - -- WebSocket reconnect is automatic with exponential backoff (cap: 60 seconds) -- All subscriptions are restored after every reconnect -- `on_stale` / `on_recovered` callbacks continue to fire correctly across reconnects - -## Common symptoms - -- Ticks stop suddenly -- Short data gaps during reconnect -- `on_stale` fires but `on_recovered` never fires - -## Detecting a stale feed - -The library detects feed silence automatically. Use `on_stale` and `on_recovered` callbacks: - -```python -async def on_stale() -> None: - print("No tick for 30s — feed is stale") - # halt algo, alert, etc. - -async def on_recovered() -> None: - print("Feed recovered") - -await broker.subscribe(watch, on_tick, on_stale=on_stale, on_recovered=on_recovered) -``` - -Or poll `broker.feed_state` directly: - -```python -from tt_connect.enums import FeedState - -while True: - if broker.feed_state == FeedState.STALE: - print("Feed stale — waiting") - elif broker.feed_state == FeedState.CONNECTED: - pass # healthy - await asyncio.sleep(5) -``` - -## Per-instrument staleness - -```python -from datetime import datetime, timezone, timedelta - -IST = timezone(timedelta(hours=5, minutes=30)) -ts = broker.last_tick_at(reliance) -if ts: - age = (datetime.now(IST) - ts).total_seconds() - if age > 60: - print(f"No RELIANCE tick for {age:.0f}s") -``` - -## When `on_stale` fires but `on_recovered` never fires - -- **Market is closed** — no ticks are sent outside exchange hours; this is expected -- **Subscribed to wrong instrument** — verify the symbol resolves to an actively traded contract -- **Connection is permanently dropped** — check `broker.feed_state` for `RECONNECTING`; if stuck, the broker endpoint may be down - -## Callback exceptions hide errors - -If your `on_tick` raises, the stream continues but the exception is only logged. Always wrap business logic: - -```python -async def on_tick(tick): - try: - process(tick) - except Exception as e: - logger.error("tick processing failed", exc_info=e) -``` - -## Related - -- [Realtime (WebSocket)](../realtime-websocket.md) -- [Recipe: Recover from reconnect](../recipes/recover-from-reconnect.md) -- [Broker operation notes](../reference/operation-notes.md) diff --git a/docs_site/websocket.md b/docs_site/websocket.md new file mode 100644 index 0000000..7bdcff5 --- /dev/null +++ b/docs_site/websocket.md @@ -0,0 +1,143 @@ +# WebSocket + +Live market data streaming is available via `AsyncTTConnect`. + +## Subscribe to ticks + +```python +import asyncio +from tt_connect import AsyncTTConnect, Tick +from tt_connect.instruments import Equity +from tt_connect.enums import Exchange + +async def on_tick(tick: Tick) -> None: + print(tick.instrument.symbol, tick.ltp, tick.timestamp) + +async def main() -> None: + config = {"api_key": "...", "access_token": "..."} + + async with AsyncTTConnect(broker_id, config) as broker: + watch = [ + Equity(exchange=Exchange.NSE, symbol="RELIANCE"), + Equity(exchange=Exchange.NSE, symbol="SBIN"), + ] + await broker.subscribe(watch, on_tick) + await asyncio.sleep(30) + await broker.unsubscribe(watch) + +asyncio.run(main()) +``` + +## Feed health callbacks + +`subscribe` accepts two optional callbacks for when the feed goes silent or recovers: + +```python +async def on_stale() -> None: + print("No tick for 30s — feed is stale") + +async def on_recovered() -> None: + print("Feed recovered — resuming") + +await broker.subscribe( + watch, + on_tick, + on_stale=on_stale, + on_recovered=on_recovered, +) +``` + +`on_stale` fires once when the feed crosses the **30-second silence threshold**. +`on_recovered` fires on the first tick after a stale period. Both work identically across all brokers and survive reconnects. + +## Feed state + +Check `broker.feed_state` at any point to read the current stream health: + +```python +from tt_connect.enums import FeedState + +if broker.feed_state == FeedState.STALE: + print("No data — market may be closed or connection degraded") + +if broker.feed_state == FeedState.CONNECTED: + print("Stream is healthy") +``` + +| State | Meaning | +|---|---| +| `CONNECTING` | Initial state before first connect | +| `CONNECTED` | Ticks are flowing normally | +| `STALE` | Connected but no tick for 30+ seconds | +| `RECONNECTING` | Connection lost — reconnect in progress | +| `CLOSED` | Client closed or `unsubscribe` called | + +## Per-instrument last tick time + +```python +from datetime import datetime, timezone, timedelta + +IST = timezone(timedelta(hours=5, minutes=30)) +ts = broker.last_tick_at(reliance) +if ts: + age = (datetime.now(IST) - ts).total_seconds() + if age > 60: + print(f"RELIANCE tick is {age:.0f}s old") +``` + +Returns `None` if no tick has been received yet for that instrument. + +## Reconnect behavior + +- Reconnect is automatic with exponential backoff (cap: 60 seconds) +- All subscriptions are restored after every reconnect +- `on_stale` / `on_recovered` continue to fire across reconnects +- You do **not** need to re-subscribe after a disconnect + +## Handling disconnects in your algo + +Use `on_stale` and `on_recovered` to pause and resume: + +```python +algo_active = True + +async def on_tick(tick: Tick) -> None: + if not algo_active: + return + process(tick) + +async def on_stale() -> None: + global algo_active + algo_active = False + print("Feed stale — algo paused") + +async def on_recovered() -> None: + global algo_active + algo_active = True + print("Feed recovered — algo resumed") +``` + +### When `on_stale` fires but `on_recovered` never fires + +- **Market is closed** — no ticks outside exchange hours; this is expected +- **Wrong instrument** — verify the symbol resolves to an actively traded contract +- **Permanent disconnect** — check `broker.feed_state`; if stuck on `RECONNECTING`, the broker endpoint may be down + +## Troubleshooting callback exceptions + +If `on_tick` raises, the stream continues but the error is only logged. Always wrap business logic: + +```python +async def on_tick(tick: Tick) -> None: + try: + process(tick) + except Exception as e: + logger.error("tick processing failed", exc_info=e) +``` + +## Tips + +- Do not assume tick ordering is preserved across reconnect boundaries +- If your strategy holds state per tick (e.g. running VWAP), resync in `on_recovered` +- Keep callbacks fast and non-blocking — push heavy work to a queue +- `broker.last_tick_at(instrument)` gives exact IST wall-clock time of the last tick diff --git a/mkdocs.yml b/mkdocs.yml index 7928213..ac3a9f9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,65 +1,84 @@ -site_name: tt-connect docs -site_description: Unified broker API docs for Indian markets +site_name: tt-connect +site_description: Unified broker API for Indian markets site_url: https://tiny-trader.github.io/connect/ docs_dir: docs_site +repo_url: https://github.com/Tiny-Trader/connect +repo_name: Tiny-Trader/connect theme: - name: readthedocs + name: material + palette: + - scheme: default + primary: deep purple + accent: amber + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + primary: deep purple + accent: amber + toggle: + icon: material/brightness-4 + name: Switch to light mode + font: + text: Inter + code: JetBrains Mono + features: + - content.tabs.link + - content.code.copy + - navigation.sections + - navigation.top + - navigation.instant + - navigation.tracking + - search.highlight + - search.suggest + - toc.follow markdown_extensions: - admonition - attr_list - md_in_html - tables + - toc: + permalink: true - pymdownx.details - pymdownx.superfences + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets - pymdownx.tabbed: alternate_style: true nav: - - Start: + - Getting Started: - Home: index.md - - Start Here: start-here.md - - Basics: basics.md - - Glossary: glossary.md - - Trading Flows: - - Login & Session: login-and-session.md - - Config & Environment: config-and-env.md + - Getting Started: getting-started.md + - Guides: + - Authentication: authentication.md - Instruments: instruments.md - - Market Data: market-data.md - Orders: orders.md - - Trades: trades.md - - Positions: positions.md - - Holdings & Funds: holdings-and-funds.md - - GTT (Trigger Orders): gtt.md - - Reliability: + - Portfolio: portfolio.md + - Market Data: market-data.md + - GTT Orders: gtt.md + - WebSocket: websocket.md + - Operations: + - Error Handling: error-handling.md - Broker Differences: broker-differences.md - - Errors & Retries: errors-and-retries.md - - Realtime (WebSocket): realtime-websocket.md - - Safety & Good Practices: safety-and-good-practices.md - Troubleshooting: - - Overview: troubleshooting/index.md - Auth Failures: troubleshooting/auth-failures.md - Instrument Not Found: troubleshooting/instrument-not-found.md - Order Rejected: troubleshooting/order-rejected.md - - WebSocket Reconnect: troubleshooting/websocket-reconnect.md - Duplicate Orders: troubleshooting/duplicate-orders.md - Recipes: - - Overview: recipes/index.md - First Order: recipes/first-order.md - - Cancel All Open Orders: recipes/cancel-all-open-orders.md - - Close All Open Positions: recipes/close-all-open-positions.md - - Stream And Store Live Ticks: recipes/stream-and-store-live-ticks.md - - Recover From Reconnect: recipes/recover-from-reconnect.md - - API Reference: - - Overview: reference/index.md - - Client Methods: reference/clients.md - - Instrument Store: reference/instrument-store.md - - Models: reference/models.md - - Enums: reference/enums.md + - Cancel All Orders: recipes/cancel-all-open-orders.md + - Close All Positions: recipes/close-all-open-positions.md + - Stream Live Ticks: recipes/stream-and-store-live-ticks.md + - Reference: + - Client API: reference/clients.md + - Models & Enums: reference/models.md - Exceptions: reference/exceptions.md - - Broker Capabilities: reference/capabilities.md - - Broker Operation Notes: reference/operation-notes.md diff --git a/poetry.lock b/poetry.lock index 00821bb..3ba9b33 100644 --- a/poetry.lock +++ b/poetry.lock @@ -47,6 +47,41 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] trio = ["trio (>=0.31.0) ; python_version < \"3.10\"", "trio (>=0.32.0) ; python_version >= \"3.10\""] +[[package]] +name = "babel" +version = "2.18.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35"}, + {file = "babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d"}, +] + +[package.extras] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] + +[[package]] +name = "backrefs" +version = "6.2" +description = "A wrapper around re and regex that adds additional back references." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "backrefs-6.2-py310-none-any.whl", hash = "sha256:0fdc7b012420b6b144410342caeb8adc54c6866cf12064abc9bb211302e496f8"}, + {file = "backrefs-6.2-py311-none-any.whl", hash = "sha256:08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be"}, + {file = "backrefs-6.2-py312-none-any.whl", hash = "sha256:c3f4b9cb2af8cda0d87ab4f57800b57b95428488477be164dd2b47be54db0c90"}, + {file = "backrefs-6.2-py313-none-any.whl", hash = "sha256:12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b"}, + {file = "backrefs-6.2-py314-none-any.whl", hash = "sha256:e5f805ae09819caa1aa0623b4a83790e7028604aa2b8c73ba602c4454e665de7"}, + {file = "backrefs-6.2-py39-none-any.whl", hash = "sha256:664e33cd88c6840b7625b826ecf2555f32d491800900f5a541f772c485f7cda7"}, + {file = "backrefs-6.2.tar.gz", hash = "sha256:f44ff4d48808b243b6c0cdc6231e22195c32f77046018141556c66f8bab72a49"}, +] + +[package.extras] +extras = ["regex"] + [[package]] name = "certifi" version = "2026.1.4" @@ -59,6 +94,160 @@ files = [ {file = "certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120"}, ] +[[package]] +name = "charset-normalizer" +version = "3.4.6" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:659a1e1b500fac8f2779dd9e1570464e012f43e580371470b45277a27baa7532"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f61aa92e4aad0be58eb6eb4e0c21acf32cf8065f4b2cae5665da756c4ceef982"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f50498891691e0864dc3da965f340fada0771f6142a378083dc4608f4ea513e2"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bf625105bb9eef28a56a943fec8c8a98aeb80e7d7db99bd3c388137e6eb2d237"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2bd9d128ef93637a5d7a6af25363cf5dec3fa21cf80e68055aad627f280e8afa"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:d08ec48f0a1c48d75d0356cea971921848fb620fdeba805b28f937e90691209f"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ed80ff870ca6de33f4d953fda4d55654b9a2b340ff39ab32fa3adbcd718f264"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f98059e4fcd3e3e4e2d632b7cf81c2faae96c43c60b569e9c621468082f1d104"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:ab30e5e3e706e3063bc6de96b118688cb10396b70bb9864a430f67df98c61ecc"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:d5f5d1e9def3405f60e3ca8232d56f35c98fb7bf581efcc60051ebf53cb8b611"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:461598cd852bfa5a61b09cae2b1c02e2efcd166ee5516e243d540ac24bfa68a7"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:71be7e0e01753a89cf024abf7ecb6bca2c81738ead80d43004d9b5e3f1244e64"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:df01808ee470038c3f8dc4f48620df7225c49c2d6639e38f96e6d6ac6e6f7b0e"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-win32.whl", hash = "sha256:69dd852c2f0ad631b8b60cfbe25a28c0058a894de5abb566619c205ce0550eae"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:517ad0e93394ac532745129ceabdf2696b609ec9f87863d337140317ebce1c14"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win32.whl", hash = "sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8"}, + {file = "charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69"}, + {file = "charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6"}, +] + +[[package]] +name = "click" +version = "8.3.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, + {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -66,7 +255,6 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] -markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -206,6 +394,24 @@ files = [ [package.dependencies] python-dateutil = ">=2.7" +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + [[package]] name = "h11" version = "0.16.0" @@ -292,6 +498,24 @@ files = [ {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, ] +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + [[package]] name = "librt" version = "0.8.1" @@ -393,6 +617,223 @@ files = [ {file = "librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73"}, ] +[[package]] +name = "markdown" +version = "3.10.2" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36"}, + {file = "markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950"}, +] + +[package.extras] +docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python] (>=0.28.3)"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +description = "Project documentation with Markdown." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +jinja2 = ">=2.11.1" +markdown = ">=3.3.6" +markupsafe = ">=2.0.1" +mergedeep = ">=1.3.4" +mkdocs-get-deps = ">=0.2.0" +packaging = ">=20.5" +pathspec = ">=0.11.1" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.2" +description = "An extra command for MkDocs that infers required PyPI packages from `plugins` in mkdocs.yml" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650"}, + {file = "mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1"}, +] + +[package.dependencies] +mergedeep = ">=1.3.4" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" + +[[package]] +name = "mkdocs-material" +version = "9.7.6" +description = "Documentation that simply works" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba"}, + {file = "mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69"}, +] + +[package.dependencies] +babel = ">=2.10" +backrefs = ">=5.7.post1" +colorama = ">=0.4" +jinja2 = ">=3.1" +markdown = ">=3.2" +mkdocs = ">=1.6,<2" +mkdocs-material-extensions = ">=1.3" +paginate = ">=0.5" +pygments = ">=2.16" +pymdown-extensions = ">=10.2" +requests = ">=2.30" + +[package.extras] +git = ["mkdocs-git-committers-plugin-2 (>=1.1)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4)"] +imaging = ["cairosvg (>=2.6)", "pillow (>=10.2)"] +recommended = ["mkdocs-minify-plugin (>=0.7)", "mkdocs-redirects (>=1.2)", "mkdocs-rss-plugin (>=1.6)"] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +description = "Extension pack for Python Markdown and MkDocs Material." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + [[package]] name = "mypy" version = "1.19.1" @@ -478,6 +919,22 @@ files = [ {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, ] +[[package]] +name = "paginate" +version = "0.5.7" +description = "Divides large result sets into pages for easier browsing" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, +] + +[package.extras] +dev = ["pytest", "tox"] +lint = ["black"] + [[package]] name = "pathspec" version = "1.0.4" @@ -496,6 +953,18 @@ optional = ["typing-extensions (>=4)"] re2 = ["google-re2 (>=1.1)"] tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] +[[package]] +name = "platformdirs" +version = "4.9.4" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868"}, + {file = "platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934"}, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -683,6 +1152,25 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pymdown-extensions" +version = "10.21" +description = "Extension pack for Python Markdown." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pymdown_extensions-10.21-py3-none-any.whl", hash = "sha256:91b879f9f864d49794c2d9534372b10150e6141096c3908a455e45ca72ad9d3f"}, + {file = "pymdown_extensions-10.21.tar.gz", hash = "sha256:39f4a020f40773f6b2ff31d2cd2546c2c04d0a6498c31d9c688d2be07e1767d5"}, +] + +[package.dependencies] +markdown = ">=3.6" +pyyaml = "*" + +[package.extras] +extra = ["pygments (>=2.19.1)"] + [[package]] name = "pyotp" version = "2.9.0" @@ -808,6 +1296,127 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +description = "A custom YAML tag for referencing environment variables in YAML files." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"}, + {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"}, +] + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "requests" +version = "2.33.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b"}, + {file = "requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652"}, +] + +[package.dependencies] +certifi = ">=2023.5.7" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.26,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +test = ["PySocks (>=1.5.6,!=1.5.7)", "pytest (>=3)", "pytest-cov", "pytest-httpbin (==2.1.0)", "pytest-mock", "pytest-xdist"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] + [[package]] name = "respx" version = "0.22.0" @@ -890,6 +1499,67 @@ files = [ [package.dependencies] typing-extensions = ">=4.12.0" +[[package]] +name = "urllib3" +version = "2.6.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, + {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, +] + +[package.extras] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] + +[[package]] +name = "watchdog" +version = "6.0.0" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, + {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, + {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, + {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, + {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + [[package]] name = "websockets" version = "16.0" @@ -964,4 +1634,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.11" -content-hash = "7badb256f32874ef3d23ca9e632e0020b786048c4055c1b43c2ca9c23b3f2ec2" +content-hash = "80f127d4963ace32bd225071733bfd6d419dc418163d3d7d62daae37d8083e67" diff --git a/pyproject.toml b/pyproject.toml index 417a105..06065f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ respx = ">=0.21.1" freezegun = ">=1.4.0" pytest-cov = ">=5.0.0" pytest-mock = ">=3.14.0" +mkdocs-material = "^9.7.6" [build-system] requires = ["poetry-core"] From 61d58de0ae583883c2deddde4d5be5daff31b995 Mon Sep 17 00:00:00 2001 From: Apurv-Salunke Date: Sun, 5 Jul 2026 05:01:00 +0530 Subject: [PATCH 2/2] chore(docs): add changelog entry for docs site restructure Unblocks the changelog-check CI gate on this PR. Co-authored-by: Cursor --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dac9fb..745cd26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Changed +- **Docs site** — restructured from 37 to 22 pages; merged overlapping guides, added + broker-agnostic examples with sync/async tabs, and switched to Material for MkDocs. + ## [0.9.1] - 2026-03-27 ### Fixed - **AngelOne duplicate instruments** — symbols appearing as both index (`AMXIDX`) and plain