diff --git a/API_REFERENCE.md b/API_REFERENCE.md
index 0b4d8d6..f65f926 100644
--- a/API_REFERENCE.md
+++ b/API_REFERENCE.md
@@ -1,8 +1,8 @@
# Kyro API Reference
-Request/response documentation for every modular method: **exchange**, **markets**, **events**, **orders**, **portfolio**.
+Request/response documentation for every modular method: **exchange**, **markets**, **events**, **orders**, **portfolio**, **search**.
-- **Import:** `from kyro.rest import exchange, markets, events, orders, portfolio`
+- **Import:** `from kyro.rest import exchange, markets, events, orders, portfolio, search`
- **Client:** Pass `RestClient` as the first argument: `await exchange.get_exchange_status(client)`
- **Base path:** `{KyroConfig.base_url}` (e.g. `https://api.elections.kalshi.com/trade-api/v2`)
- **Auth:** Endpoints marked *Auth required* need `KyroConfig(auth_headers={...})` with KALSHI-ACCESS-KEY, TIMESTAMP, SIGNATURE.
@@ -532,6 +532,32 @@ data = await events.get_event_metadata(client, "KXBTC")
---
+### `get_event_candlesticks`
+
+**HTTP:** `GET /series/{series_ticker}/events/{event_ticker}/candlesticks`
+**Auth:** No
+
+**Usage:**
+```python
+data = await events.get_event_candlesticks(
+ client,
+ "KXBTC",
+ "KXBTC-24JAN15",
+ start_ts=1704067200,
+ end_ts=1704153600,
+ period_interval=60,
+ limit=24,
+)
+```
+
+If `start_ts`/`end_ts` omitted, uses last 24h.
+
+**Query:** `start_ts`, `end_ts` (Unix), `period_interval` (1|60|1440 minutes), `limit`, `include_latest_before_start`
+
+**Response (200):** `{ "candlesticks": [ { "start_ts", "end_ts", "open", "high", "low", "close", "volume" }, ... ] }` per Kalshi
+
+---
+
### `get_multivariate_events`
**HTTP:** `GET /events/multivariate`
@@ -553,6 +579,40 @@ data = await events.get_multivariate_events(client, limit=100, cursor=None)
---
+## Search
+
+No auth unless noted.
+
+---
+
+### `get_sports_filters`
+
+**HTTP:** `GET /search/filters_by_sport`
+**Auth:** No
+
+**Usage:**
+```python
+data = await search.get_sports_filters(client)
+```
+
+**Response (200):** sport-based filter metadata for search/discovery (structure per Kalshi)
+
+---
+
+### `get_tags_by_categories`
+
+**HTTP:** `GET /search/tags_by_categories`
+**Auth:** No
+
+**Usage:**
+```python
+data = await search.get_tags_by_categories(client)
+```
+
+**Response (200):** tags grouped by category for search/filtering (structure per Kalshi)
+
+---
+
## Orders
All order endpoints **require auth**.
@@ -856,6 +916,20 @@ All portfolio endpoints **require auth**.
---
+### `get_portfolio`
+
+**HTTP:** `GET /portfolio`
+**Auth:** Yes
+
+**Usage:**
+```python
+data = await portfolio.get_portfolio(client)
+```
+
+**Response (200):** portfolio summary (structure per Kalshi). If the endpoint is not available for an account, use `get_balance`, `get_positions`, `get_fills` instead.
+
+---
+
### `get_balance`
**HTTP:** `GET /portfolio/balance`
diff --git a/README.md b/README.md
index 1be14f0..93de870 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,13 @@
-
+
+
+
+
# Kyro
-[](https://github.com/UTXOnly/kyro/actions/workflows/ruff.yml) [](https://github.com/UTXOnly/kyro/actions/workflows/black.yml) [](https://github.com/UTXOnly/kyro/actions/workflows/test.yml) [](https://github.com/UTXOnly/kyro/actions/workflows/benchmarks.yml)
+[](https://pypi.org/project/kyro/) [](https://github.com/UTXOnly/kyro/actions/workflows/ruff.yml) [](https://github.com/UTXOnly/kyro/actions/workflows/black.yml) [](https://github.com/UTXOnly/kyro/actions/workflows/test.yml) [](https://github.com/UTXOnly/kyro/actions/workflows/benchmarks.yml)
Kyro is an async Python client library for the Kalshi REST API.
@@ -16,6 +19,7 @@ API areas are grouped into:
- `exchange`
- `markets`
- `events`
+- `search`
- `orders`
- `portfolio`
@@ -31,7 +35,7 @@ Errors are surfaced as explicit exception types: `KyroError` (base), `KyroHTTPEr
## Install
-From PyPI (after a release):
+From [PyPI](https://pypi.org/project/kyro/):
```bash
pip install kyro
@@ -171,7 +175,7 @@ async with RestClient(cfg) as client:
| Requires auth | Endpoints |
|---------------|-----------|
-| **No** | `exchange.get_exchange_status`, `get_exchange_announcements`, `get_exchange_schedule`, `get_series_fee_changes`; all of `markets.*` and `events.*` |
+| **No** | `exchange.get_exchange_status`, `get_exchange_announcements`, `get_exchange_schedule`, `get_series_fee_changes`; all of `markets.*`, `events.*`, and `search.*` |
| **Yes** | `exchange.get_user_data_timestamp`; all of `orders.*` and `portfolio.*` |
Without auth, public endpoints work as usual. Auth-required calls return `401` if the headers are missing or invalid.
@@ -186,18 +190,19 @@ Without auth, public endpoints work as usual. Auth-required calls return `401` i
---
-## Modular API (exchange, markets, events, orders, portfolio)
+## Modular API (exchange, markets, events, search, orders, portfolio)
```python
from kyro import RestClient, KyroConfig
-from kyro.rest import exchange, markets, events, orders, portfolio
+from kyro.rest import exchange, markets, events, search, orders, portfolio
async with RestClient(KyroConfig()) as client:
- # Exchange (no auth)
+ # Exchange (no auth except get_user_data_timestamp)
status = await exchange.get_exchange_status(client)
await exchange.get_exchange_announcements(client)
await exchange.get_exchange_schedule(client)
await exchange.get_series_fee_changes(client, series_ticker="KXBTC")
+ await exchange.get_user_data_timestamp(client) # auth
# Markets — filters: series_ticker, event_ticker, status, tickers, min/max_*_ts, cursor
ms = await markets.get_markets(
@@ -222,6 +227,8 @@ async with RestClient(KyroConfig()) as client:
period_interval=60,
limit=100,
)
+ await markets.get_live_data(client, "KXBTC-24JAN15")
+ await markets.get_multiple_live_data(client, "KXBTC-24JAN15,INXD-25")
await markets.get_series(client, "KXBTC")
await markets.get_series_list(client, limit=20) # cursor= for pagination
@@ -235,8 +242,15 @@ async with RestClient(KyroConfig()) as client:
)
ev = await events.get_event(client, "INXD-25", with_nested_markets=True)
await events.get_event_metadata(client, "INXD-25")
+ await events.get_event_candlesticks(
+ client, "KXBTC", "INXD-25", period_interval=60, limit=100
+ )
await events.get_multivariate_events(client, limit=10)
+ # Search (no auth)
+ await search.get_sports_filters(client)
+ await search.get_tags_by_categories(client)
+
# Orders (auth) — filters: ticker, event_ticker, status, min_ts, max_ts, cursor, subaccount
ords = await orders.get_orders(
client, ticker="KXBTC-24JAN15", status="resting", limit=50
@@ -255,13 +269,15 @@ async with RestClient(KyroConfig()) as client:
await orders.amend_order(
client, "order-id", ticker="KXBTC-24JAN15", side="yes", action="buy", yes_price=55
)
+ await orders.decrease_order(client, "order-id", reduce_by=1)
await orders.batch_create_orders(
client,
[{"ticker": "KXBTC-24JAN15", "side": "yes", "action": "buy", "count": 1, "yes_price": 50}],
)
- await orders.batch_cancel_orders(client, ids=["id1", "id2"])
+ await orders.batch_cancel_orders(client, order_ids=["id1", "id2"])
# Portfolio (auth) — filters: ticker, event_ticker, min_ts, max_ts, cursor, subaccount
+ await portfolio.get_portfolio(client)
bal = await portfolio.get_balance(client)
pos = await portfolio.get_positions(
client, ticker="KXBTC-24JAN15", limit=100
@@ -283,7 +299,7 @@ async with RestClient(KyroConfig()) as client:
## API Reference
-Full request/response docs for **every method** (exchange, markets, events, orders, portfolio):
+Full request/response docs for **every method** (exchange, markets, events, search, orders, portfolio):
**[API_REFERENCE.md](API_REFERENCE.md)**
---
@@ -410,12 +426,13 @@ kyro/
│ ├── _version.py
│ ├── exceptions.py # KyroError, KyroHTTPError, KyroTimeoutError, KyroConnectionError, KyroValidationError
│ └── rest/
-│ ├── __init__.py # RestClient, exchange, markets, events, orders, portfolio
+│ ├── __init__.py # RestClient, exchange, markets, events, search, orders, portfolio
│ ├── client.py
│ └── api/
│ ├── exchange.py
│ ├── markets.py
│ ├── events.py
+│ ├── search.py
│ ├── orders.py
│ └── portfolio.py
├── benchmarks/ # pytest-benchmark: serialization, REST client vs local mock
diff --git a/assets/logo-light-bg.png b/assets/logo-light-bg.png
new file mode 100644
index 0000000..7c2c920
Binary files /dev/null and b/assets/logo-light-bg.png differ
diff --git a/pyproject.toml b/pyproject.toml
index 57eef99..fe247b5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "kyro"
-version = "0.1.1"
+version = "0.1.2"
description = "Async Kalshi API client (aiohttp, Pydantic). Library for building apps."
readme = "README.md"
license = { text = "MIT" }
diff --git a/scripts/live_api_smoke.py b/scripts/live_api_smoke.py
index 7940120..6251a19 100644
--- a/scripts/live_api_smoke.py
+++ b/scripts/live_api_smoke.py
@@ -42,7 +42,7 @@
from kyro import RestClient, config_from_env
from kyro.exceptions import KyroConnectionError, KyroHTTPError, KyroTimeoutError
-from kyro.rest import events, exchange, markets, orders, portfolio
+from kyro.rest import events, exchange, markets, orders, portfolio, search
AUDIT_LOG_ENV = "KALSHI_SMOKE_AUDIT_LOG"
DEFAULT_AUDIT_LOG = "live_smoke_audit.log"
@@ -71,6 +71,11 @@
("orders", "batch_create_orders"): "POST /portfolio/orders/batched",
("orders", "batch_cancel_orders"): 'DELETE /portfolio/orders/batched body {"ids":[...]}',
("orders", "cancel_order"): "DELETE /portfolio/orders/{order_id}",
+ (
+ "events",
+ "get_event_candlesticks",
+ ): "GET /series/{series_ticker}/events/{event_ticker}/candlesticks?start_ts=&end_ts=&period_interval=1|60|1440",
+ ("portfolio", "get_portfolio"): "GET /portfolio (may 404 for some accounts)",
}
@@ -230,7 +235,10 @@ async def _get_events(client: RestClient, ctx: dict) -> Any:
r = await events.get_events(client, limit=5)
evs = (r or {}).get("events") or []
if evs:
- ctx["event_ticker"] = evs[0].get("event_ticker")
+ e = evs[0]
+ ctx["event_ticker"] = e.get("event_ticker")
+ if e.get("series_ticker") and not ctx.get("series_ticker"):
+ ctx["series_ticker"] = e["series_ticker"]
return r
@@ -339,6 +347,8 @@ async def _run_and_log(
("exchange", "get_exchange_schedule", lambda c, x: exchange.get_exchange_schedule(c), {}),
("exchange", "get_series_fee_changes", lambda c, x: exchange.get_series_fee_changes(c), {}),
("exchange", "get_user_data_timestamp", lambda c, x: exchange.get_user_data_timestamp(c), {}),
+ ("search", "get_sports_filters", lambda c, x: search.get_sports_filters(c), {}),
+ ("search", "get_tags_by_categories", lambda c, x: search.get_tags_by_categories(c), {}),
("events", "get_events", _get_events, {"limit": 5}),
(
"events",
@@ -352,6 +362,19 @@ async def _run_and_log(
lambda c, x: events.get_event_metadata(c, _event_ticker(x)),
lambda ctx: {"event_ticker": _event_ticker(ctx)},
),
+ (
+ "events",
+ "get_event_candlesticks",
+ lambda c, x: events.get_event_candlesticks(
+ c, _series_ticker(x), _event_ticker(x), limit=5, period_interval=60
+ ),
+ lambda ctx: {
+ "series_ticker": _series_ticker(ctx),
+ "event_ticker": _event_ticker(ctx),
+ "limit": 5,
+ "period_interval": 60,
+ },
+ ),
(
"events",
"get_multivariate_events",
@@ -405,6 +428,7 @@ async def _run_and_log(
lambda ctx: {"tickers": _ticker(ctx)},
),
("orders", "get_orders", lambda c, x: orders.get_orders(c, limit=5), {"limit": 5}),
+ ("portfolio", "get_portfolio", lambda c, x: portfolio.get_portfolio(c), {}),
("portfolio", "get_balance", lambda c, x: portfolio.get_balance(c), {}),
("portfolio", "get_positions", lambda c, x: portfolio.get_positions(c, limit=5), {"limit": 5}),
("portfolio", "get_fills", lambda c, x: portfolio.get_fills(c, limit=5), {"limit": 5}),
diff --git a/src/kyro/_version.py b/src/kyro/_version.py
index 0a6cfe5..af5f41c 100644
--- a/src/kyro/_version.py
+++ b/src/kyro/_version.py
@@ -1,3 +1,3 @@
"""Package version."""
-__version__ = "0.1.1"
+__version__ = "0.1.2"
diff --git a/src/kyro/rest/__init__.py b/src/kyro/rest/__init__.py
index 47c47ec..e1c1fba 100644
--- a/src/kyro/rest/__init__.py
+++ b/src/kyro/rest/__init__.py
@@ -1,6 +1,6 @@
-"""REST client and modular Kalshi API (exchange, markets, events, orders, portfolio)."""
+"""REST client and modular Kalshi API (exchange, markets, events, orders, portfolio, search)."""
-from kyro.rest.api import events, exchange, markets, orders, portfolio
+from kyro.rest.api import events, exchange, markets, orders, portfolio, search
from kyro.rest.client import RestClient
__all__ = [
@@ -10,4 +10,5 @@
"markets",
"orders",
"portfolio",
+ "search",
]
diff --git a/src/kyro/rest/api/__init__.py b/src/kyro/rest/api/__init__.py
index a6e20ca..6d97af8 100644
--- a/src/kyro/rest/api/__init__.py
+++ b/src/kyro/rest/api/__init__.py
@@ -2,12 +2,12 @@
Example:
>>> from kyro import RestClient, KyroConfig
- >>> from kyro.rest import exchange, markets, events, orders, portfolio
+ >>> from kyro.rest import exchange, markets, events, orders, portfolio, search
>>> async with RestClient(KyroConfig()) as client:
... status = await exchange.get_exchange_status(client)
... ms = await markets.get_markets(client, limit=10)
"""
-from . import events, exchange, markets, orders, portfolio
+from . import events, exchange, markets, orders, portfolio, search
-__all__ = ["exchange", "events", "markets", "orders", "portfolio"]
+__all__ = ["exchange", "events", "markets", "orders", "portfolio", "search"]
diff --git a/src/kyro/rest/api/events.py b/src/kyro/rest/api/events.py
index d3149ec..5a77738 100644
--- a/src/kyro/rest/api/events.py
+++ b/src/kyro/rest/api/events.py
@@ -5,6 +5,7 @@
from __future__ import annotations
+import time
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
@@ -61,6 +62,42 @@ async def get_event_metadata(client: RestClient, event_ticker: str) -> Any:
return await client.get(f"/events/{event_ticker}/metadata")
+async def get_event_candlesticks(
+ client: RestClient,
+ series_ticker: str,
+ event_ticker: str,
+ *,
+ start_ts: int | None = None,
+ end_ts: int | None = None,
+ period_interval: int | None = None,
+ limit: int | None = None,
+ include_latest_before_start: bool | None = None,
+) -> Any:
+ """Get OHLCV candlesticks for an event. `GET /series/{series_ticker}/events/{event_ticker}/candlesticks`.
+
+ start_ts, end_ts (Unix). period_interval: 1, 60, or 1440 (minutes).
+ If start_ts/end_ts omitted, uses last 24h. limit 1–1000.
+ """
+ now = int(time.time())
+ if start_ts is None:
+ start_ts = now - 86400
+ if end_ts is None:
+ end_ts = now
+ params = _clean(
+ {
+ "start_ts": start_ts,
+ "end_ts": end_ts,
+ "period_interval": period_interval,
+ "limit": limit,
+ "include_latest_before_start": include_latest_before_start,
+ }
+ )
+ return await client.get(
+ f"/series/{series_ticker}/events/{event_ticker}/candlesticks",
+ params=params or None,
+ )
+
+
async def get_multivariate_events(
client: RestClient,
*,
diff --git a/src/kyro/rest/api/portfolio.py b/src/kyro/rest/api/portfolio.py
index ccca6b8..4eb8dbd 100644
--- a/src/kyro/rest/api/portfolio.py
+++ b/src/kyro/rest/api/portfolio.py
@@ -15,6 +15,15 @@ def _clean(params: dict[str, Any]) -> dict[str, Any]:
return {k: v for k, v in params.items() if v is not None}
+async def get_portfolio(client: RestClient) -> Any:
+ """Get portfolio summary. `GET /portfolio`.
+
+ Auth required. May not be available for all accounts; prefer get_balance,
+ get_positions, get_fills for specific data.
+ """
+ return await client.get("/portfolio")
+
+
async def get_balance(client: RestClient) -> Any:
"""Get balance and portfolio value. `GET /portfolio/balance`.
diff --git a/src/kyro/rest/api/search.py b/src/kyro/rest/api/search.py
new file mode 100644
index 0000000..69a68a0
--- /dev/null
+++ b/src/kyro/rest/api/search.py
@@ -0,0 +1,27 @@
+"""Search endpoints.
+
+Ref: https://docs.kalshi.com (search / filters)
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+if TYPE_CHECKING:
+ from kyro.rest.client import RestClient
+
+
+async def get_sports_filters(client: RestClient) -> Any:
+ """Get filters by sport. `GET /search/filters_by_sport`.
+
+ Returns sport-based filter metadata for search/discovery.
+ """
+ return await client.get("/search/filters_by_sport")
+
+
+async def get_tags_by_categories(client: RestClient) -> Any:
+ """Get tags grouped by category. `GET /search/tags_by_categories`.
+
+ Returns tag metadata for search/filtering.
+ """
+ return await client.get("/search/tags_by_categories")
diff --git a/tests/conftest.py b/tests/conftest.py
index f571fc4..53b646f 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -100,24 +100,71 @@ async def _portfolio_order_delete(r: web.Request) -> web.Response:
)
+async def _portfolio_order_decrease(r: web.Request) -> web.Response:
+ return _mk_json(
+ {
+ "order": {"order_id": r.match_info["order_id"]},
+ "reduced_by": 1,
+ "reduced_by_fp": "1.00",
+ }
+ )
+
+
+async def _event_candlesticks(r: web.Request) -> web.Response:
+ return _mk_json({"candlesticks": []})
+
+
+async def _search_filters_by_sport(_: web.Request) -> web.Response:
+ return _mk_json({"sports": []})
+
+
+async def _search_tags_by_categories(_: web.Request) -> web.Response:
+ return _mk_json({"categories": []})
+
+
+async def _portfolio_summary(_: web.Request) -> web.Response:
+ return _mk_json({"portfolio_value": 10000, "balance": 10000})
+
+
+async def _live_data(r: web.Request) -> web.Response:
+ if "tickers" in r.query:
+ return _mk_json({"tickers": r.query.get("tickers", "").split(",")})
+ return _mk_json({"ticker": r.query.get("ticker", "")})
+
+
+async def _exchange_user_data_timestamp(_: web.Request) -> web.Response:
+ return _mk_json({"timestamp": 1704067200})
+
+
def create_kalshi_app() -> web.Application:
"""Minimal aiohttp app that mimics Kalshi-style routes for testing."""
app = web.Application()
# Exchange
app.router.add_get("/exchange/status", _exchange_status)
+ app.router.add_get("/exchange/user-data-timestamp", _exchange_user_data_timestamp)
# Markets
app.router.add_get("/markets", _markets_list)
app.router.add_get(r"/markets/{ticker}", _market_detail)
app.router.add_get(r"/markets/{ticker}/orderbook", _market_orderbook)
app.router.add_get("/markets/trades", _markets_trades)
+ app.router.add_get("/live-data", _live_data)
# Events
app.router.add_get("/events", _events_list)
app.router.add_get(r"/events/{ticker}", _event_detail)
+ app.router.add_get(
+ r"/series/{series_ticker}/events/{event_ticker}/candlesticks",
+ _event_candlesticks,
+ )
+ # Search
+ app.router.add_get("/search/filters_by_sport", _search_filters_by_sport)
+ app.router.add_get("/search/tags_by_categories", _search_tags_by_categories)
# Portfolio (auth-style; we don't enforce auth in tests)
+ app.router.add_get("/portfolio", _portfolio_summary)
app.router.add_get("/portfolio/balance", _portfolio_balance)
app.router.add_get("/portfolio/orders", _portfolio_orders_list)
app.router.add_get(r"/portfolio/orders/{order_id}", _portfolio_order_detail)
app.router.add_post("/portfolio/orders", _portfolio_order_create)
+ app.router.add_post(r"/portfolio/orders/{order_id}/decrease", _portfolio_order_decrease)
app.router.add_delete(r"/portfolio/orders/{order_id}", _portfolio_order_delete)
# Test helpers: empty, errors, echo, params, slow
app.router.add_get("/empty", _empty)
diff --git a/tests/test_api_modules.py b/tests/test_api_modules.py
index d43a360..df7bbba 100644
--- a/tests/test_api_modules.py
+++ b/tests/test_api_modules.py
@@ -1,9 +1,9 @@
-"""Tests for rest.api modules (exchange, markets, events, orders, portfolio)."""
+"""Tests for rest.api modules (exchange, markets, events, search, orders, portfolio)."""
from __future__ import annotations
from kyro import RestClient
-from kyro.rest.api import events, exchange, markets, orders, portfolio
+from kyro.rest.api import events, exchange, markets, orders, portfolio, search
async def test_get_exchange_status(kyro_client: RestClient) -> None:
@@ -12,6 +12,12 @@ async def test_get_exchange_status(kyro_client: RestClient) -> None:
assert "trading_active" in data
+async def test_get_user_data_timestamp(kyro_client: RestClient) -> None:
+ data = await exchange.get_user_data_timestamp(kyro_client)
+ assert "timestamp" in data
+ assert isinstance(data["timestamp"], int)
+
+
async def test_get_markets(kyro_client: RestClient) -> None:
data = await markets.get_markets(kyro_client)
assert "markets" in data
@@ -43,6 +49,18 @@ async def test_get_trades(kyro_client: RestClient) -> None:
assert "cursor" in data
+async def test_get_live_data(kyro_client: RestClient) -> None:
+ data = await markets.get_live_data(kyro_client, "KXBTC-24JAN15")
+ assert "ticker" in data
+ assert data["ticker"] == "KXBTC-24JAN15"
+
+
+async def test_get_multiple_live_data(kyro_client: RestClient) -> None:
+ data = await markets.get_multiple_live_data(kyro_client, "KXBTC-24JAN15,INXD-25")
+ assert "tickers" in data
+ assert isinstance(data["tickers"], list)
+
+
async def test_get_events(kyro_client: RestClient) -> None:
data = await events.get_events(kyro_client)
assert "events" in data
@@ -56,6 +74,25 @@ async def test_get_event(kyro_client: RestClient) -> None:
assert "markets" in data
+async def test_get_event_candlesticks(kyro_client: RestClient) -> None:
+ data = await events.get_event_candlesticks(
+ kyro_client, "KXBTC", "INXD-25", period_interval=60, limit=100
+ )
+ assert "candlesticks" in data
+ assert isinstance(data["candlesticks"], list)
+
+
+async def test_get_sports_filters(kyro_client: RestClient) -> None:
+ data = await search.get_sports_filters(kyro_client)
+ assert "sports" in data
+ assert isinstance(data["sports"], list)
+
+
+async def test_get_tags_by_categories(kyro_client: RestClient) -> None:
+ data = await search.get_tags_by_categories(kyro_client)
+ assert "categories" in data
+
+
async def test_get_orders(kyro_client: RestClient) -> None:
data = await orders.get_orders(kyro_client)
assert "orders" in data
@@ -86,6 +123,20 @@ async def test_cancel_order(kyro_client: RestClient) -> None:
assert "order" in data or "reduced_by" in data
+async def test_decrease_order(kyro_client: RestClient) -> None:
+ data = await orders.decrease_order(kyro_client, "ord-123", reduce_by=1)
+ assert data is not None
+ assert "order" in data
+ assert data["order"]["order_id"] == "ord-123"
+ assert "reduced_by" in data
+
+
+async def test_get_portfolio(kyro_client: RestClient) -> None:
+ data = await portfolio.get_portfolio(kyro_client)
+ assert "portfolio_value" in data
+ assert "balance" in data
+
+
async def test_get_balance(kyro_client: RestClient) -> None:
data = await portfolio.get_balance(kyro_client)
assert "balance" in data