Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pykalshi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
A clean, modular interface for the Kalshi trading API.
"""

__version__ = "1.0.5"
__version__ = "1.0.6"

import logging

Expand Down
2 changes: 1 addition & 1 deletion pykalshi/_async/api_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(self, client: AsyncKalshiClient) -> None:
async def list(self) -> list[APIKey]:
"""List all API keys for this account."""
data = await self._client.get("/api_keys")
return [APIKey.model_validate(k) for k in data.get("api_keys", [])]
return [APIKey.model_validate(k) for k in (data.get("api_keys") or [])]

async def create(self, public_key: str, name: str | None = None) -> str:
"""Create an API key with a provided RSA public key.
Expand Down
4 changes: 2 additions & 2 deletions pykalshi/_async/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ async def paginated_get(
filtered = {k: v for k, v in params.items() if v is not None}
endpoint = f"{path}?{urlencode(filtered)}" if filtered else path
response = await self.get(endpoint)
all_items.extend(response.get(response_key, []))
all_items.extend(response.get(response_key) or [])
cursor = response.get("cursor", "")
if not fetch_all or not cursor:
break
Expand Down Expand Up @@ -409,5 +409,5 @@ async def get_candlesticks_batch(
response = await self.get(f"/markets/candlesticks?{query}")
return {
item["market_ticker"]: CandlestickResponse.model_validate(item)
for item in response.get("markets", [])
for item in (response.get("markets") or [])
}
2 changes: 1 addition & 1 deletion pykalshi/_async/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async def get_schedule(self) -> dict[str, Any]:
async def get_announcements(self) -> list[Announcement]:
"""Get exchange-wide announcements."""
data = await self._client.get("/exchange/announcements")
return [Announcement.model_validate(a) for a in data.get("announcements", [])]
return [Announcement.model_validate(a) for a in (data.get("announcements") or [])]

async def get_user_data_timestamp(self) -> int:
"""Get timestamp of last user data validation (Unix ms)."""
Expand Down
2 changes: 1 addition & 1 deletion pykalshi/_async/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ async def get_candlesticks(
)
return [
HistoricalCandlestick.model_validate(c)
for c in response.get("candlesticks", [])
for c in (response.get("candlesticks") or [])
]

async def get_fills(
Expand Down
10 changes: 5 additions & 5 deletions pykalshi/_async/portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ async def batch_place_orders(self, orders: list[dict]) -> DataFrameList[AsyncOrd
prepared = self._build_batch_orders(orders)
response = await self._client.post("/portfolio/orders/batched", {"orders": prepared})
result = []
for item in response.get("orders", []):
for item in (response.get("orders") or []):
order_data = item.get("order")
if order_data is None:
continue
Expand All @@ -336,7 +336,7 @@ async def batch_cancel_orders(self, order_ids: list[str]) -> DataFrameList[Async
orders = [{"order_id": oid} for oid in order_ids]
response = await self._client.delete("/portfolio/orders/batched", {"orders": orders})
result = []
for item in response.get("orders", []):
for item in (response.get("orders") or []):
order_data = item.get("order")
if order_data is None:
continue
Expand Down Expand Up @@ -373,7 +373,7 @@ async def get_queue_positions(
response = await self._client.get(endpoint)
return DataFrameList(
QueuePositionModel.model_validate(qp)
for qp in response.get("queue_positions", [])
for qp in (response.get("queue_positions") or [])
)

# --- Settlements ---
Expand Down Expand Up @@ -438,7 +438,7 @@ async def get_order_groups(self) -> DataFrameList[OrderGroupModel]:
response = await self._client.get("/portfolio/order_groups")
return DataFrameList(
OrderGroupModel.model_validate(og)
for og in response.get("order_groups", [])
for og in (response.get("order_groups") or [])
)

async def reset_order_group(self, order_group_id: str) -> None:
Expand Down Expand Up @@ -488,7 +488,7 @@ async def get_subaccount_balances(self) -> DataFrameList[SubaccountBalanceModel]
response = await self._client.get("/portfolio/subaccounts/balances")
return DataFrameList(
SubaccountBalanceModel.model_validate(b)
for b in response.get("balances", [])
for b in (response.get("balances") or [])
)

async def get_subaccount_transfers(
Expand Down
2 changes: 1 addition & 1 deletion pykalshi/_sync/api_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __init__(self, client: KalshiClient) -> None:
def list(self) -> list[APIKey]:
"""List all API keys for this account."""
data = self._client.get("/api_keys")
return [APIKey.model_validate(k) for k in data.get("api_keys", [])]
return [APIKey.model_validate(k) for k in (data.get("api_keys") or [])]

def create(self, public_key: str, name: str | None = None) -> str:
"""Create an API key with a provided RSA public key.
Expand Down
4 changes: 2 additions & 2 deletions pykalshi/_sync/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def paginated_get(
filtered = {k: v for k, v in params.items() if v is not None}
endpoint = f"{path}?{urlencode(filtered)}" if filtered else path
response = self.get(endpoint)
all_items.extend(response.get(response_key, []))
all_items.extend(response.get(response_key) or [])
cursor = response.get("cursor", "")
if not fetch_all or not cursor:
break
Expand Down Expand Up @@ -411,5 +411,5 @@ def get_candlesticks_batch(
response = self.get(f"/markets/candlesticks?{query}")
return {
item["market_ticker"]: CandlestickResponse.model_validate(item)
for item in response.get("markets", [])
for item in (response.get("markets") or [])
}
2 changes: 1 addition & 1 deletion pykalshi/_sync/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def get_schedule(self) -> dict[str, Any]:
def get_announcements(self) -> list[Announcement]:
"""Get exchange-wide announcements."""
data = self._client.get("/exchange/announcements")
return [Announcement.model_validate(a) for a in data.get("announcements", [])]
return [Announcement.model_validate(a) for a in (data.get("announcements") or [])]

def get_user_data_timestamp(self) -> int:
"""Get timestamp of last user data validation (Unix ms)."""
Expand Down
2 changes: 1 addition & 1 deletion pykalshi/_sync/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def get_candlesticks(
)
return [
HistoricalCandlestick.model_validate(c)
for c in response.get("candlesticks", [])
for c in (response.get("candlesticks") or [])
]

def get_fills(
Expand Down
10 changes: 5 additions & 5 deletions pykalshi/_sync/portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def batch_place_orders(self, orders: list[dict]) -> DataFrameList[Order]:
prepared = self._build_batch_orders(orders)
response = self._client.post("/portfolio/orders/batched", {"orders": prepared})
result = []
for item in response.get("orders", []):
for item in (response.get("orders") or []):
order_data = item.get("order")
if order_data is None:
continue
Expand All @@ -338,7 +338,7 @@ def batch_cancel_orders(self, order_ids: list[str]) -> DataFrameList[Order]:
orders = [{"order_id": oid} for oid in order_ids]
response = self._client.delete("/portfolio/orders/batched", {"orders": orders})
result = []
for item in response.get("orders", []):
for item in (response.get("orders") or []):
order_data = item.get("order")
if order_data is None:
continue
Expand Down Expand Up @@ -375,7 +375,7 @@ def get_queue_positions(
response = self._client.get(endpoint)
return DataFrameList(
QueuePositionModel.model_validate(qp)
for qp in response.get("queue_positions", [])
for qp in (response.get("queue_positions") or [])
)

# --- Settlements ---
Expand Down Expand Up @@ -440,7 +440,7 @@ def get_order_groups(self) -> DataFrameList[OrderGroupModel]:
response = self._client.get("/portfolio/order_groups")
return DataFrameList(
OrderGroupModel.model_validate(og)
for og in response.get("order_groups", [])
for og in (response.get("order_groups") or [])
)

def reset_order_group(self, order_group_id: str) -> None:
Expand Down Expand Up @@ -490,7 +490,7 @@ def get_subaccount_balances(self) -> DataFrameList[SubaccountBalanceModel]:
response = self._client.get("/portfolio/subaccounts/balances")
return DataFrameList(
SubaccountBalanceModel.model_validate(b)
for b in response.get("balances", [])
for b in (response.get("balances") or [])
)

def get_subaccount_transfers(
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pykalshi"
version = "1.0.5"
version = "1.0.6"
description = "A typed Python client for the Kalshi prediction markets API with WebSocket streaming, automatic retries, and ergonomic interfaces"
readme = "README.md"
license = "MIT"
Expand Down
7 changes: 5 additions & 2 deletions tests/integration/test_portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,8 +531,11 @@ def test_get_queue_position(self, client, market_for_orders):
assert isinstance(queue_pos.queue_position_fp, str)
assert float(queue_pos.queue_position_fp) >= 0

# Cleanup
order.cancel()
# Cleanup (order may already be filled/gone)
try:
order.cancel()
except ResourceNotFoundError:
pass

def test_get_queue_positions_multiple(self, client, market_for_orders):
"""Get queue positions for all resting orders (filtered by market)."""
Expand Down
4 changes: 2 additions & 2 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading