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
14 changes: 13 additions & 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.2"
__version__ = "1.0.3"

import logging

Expand All @@ -16,6 +16,7 @@
from .orders import Order, AsyncOrder
from .portfolio import Portfolio, AsyncPortfolio
from .exchange import Exchange, AsyncExchange
from .history import History, AsyncHistory
from .api_keys import APIKeys, AsyncAPIKeys
from .communications import Communications, AsyncCommunications
from .feed import (
Expand Down Expand Up @@ -70,6 +71,10 @@
AssociatedEventModel,
RfqModel,
QuoteModel,
HistoricalCutoffResponse,
HistoricalCandlestick,
HistoricalBidAsk,
HistoricalPrice,
)
from .orderbook import OrderbookManager
from .rate_limiter import RateLimiter, NoOpRateLimiter, AsyncRateLimiter, AsyncNoOpRateLimiter
Expand Down Expand Up @@ -106,6 +111,8 @@
"AsyncPortfolio",
"Exchange",
"AsyncExchange",
"History",
"AsyncHistory",
"APIKeys",
"AsyncAPIKeys",
"Communications",
Expand Down Expand Up @@ -152,6 +159,11 @@
"QueuePositionModel",
"OrderGroupModel",
"ForecastPercentileHistory",
# Historical Models
"HistoricalCutoffResponse",
"HistoricalCandlestick",
"HistoricalBidAsk",
"HistoricalPrice",
# MVE & Communications Models
"MveSelectedLeg",
"MveCollectionModel",
Expand Down
5 changes: 5 additions & 0 deletions pykalshi/_async/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .exchange import AsyncExchange
from .api_keys import AsyncAPIKeys
from .communications import AsyncCommunications
from .history import AsyncHistory
from ..exceptions import RateLimitError
from .._utils import normalize_ticker, normalize_tickers

Expand Down Expand Up @@ -208,6 +209,10 @@ def api_keys(self) -> AsyncAPIKeys:
def communications(self) -> AsyncCommunications:
return AsyncCommunications(self)

@cached_property
def history(self) -> AsyncHistory:
return AsyncHistory(self)

def feed(self) -> AsyncFeed:
"""Create a new async real-time data feed."""
from ..afeed import AsyncFeed
Expand Down
186 changes: 186 additions & 0 deletions pykalshi/_async/history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
from __future__ import annotations

from typing import TYPE_CHECKING
from urllib.parse import urlencode

from .markets import AsyncMarket
from .orders import AsyncOrder
from ..enums import CandlestickPeriod
from ..dataframe import DataFrameList
from .._utils import normalize_ticker
from ..models import (
MarketModel, OrderModel, FillModel, TradeModel,
HistoricalCutoffResponse, HistoricalCandlestick,
)

if TYPE_CHECKING:
from .client import AsyncKalshiClient


class AsyncHistory:
"""Access to historical data that has rolled off the live API."""

def __init__(self, client: AsyncKalshiClient) -> None:
self._client = client

async def get_cutoff(self) -> HistoricalCutoffResponse:
"""Get boundary timestamps between live and historical data."""
data = await self._client.get("/historical/cutoff")
return HistoricalCutoffResponse.model_validate(data)

async def get_markets(
self,
*,
tickers: str | None = None,
event_ticker: str | None = None,
mve_filter: str | None = None,
limit: int = 100,
cursor: str | None = None,
fetch_all: bool = False,
**extra_params,
) -> DataFrameList[AsyncMarket]:
"""Get historical (settled) markets.

Args:
tickers: Comma-separated ticker list.
event_ticker: Filter by event ticker.
mve_filter: "exclude" to exclude multivariate markets.
limit: Results per page (max 1000).
cursor: Pagination cursor.
fetch_all: Automatically fetch all pages.
"""
params = {
"tickers": tickers,
"event_ticker": normalize_ticker(event_ticker),
"mve_filter": mve_filter,
"limit": limit,
"cursor": cursor,
**extra_params,
}
data = await self._client.paginated_get("/historical/markets", "markets", params, fetch_all)
return DataFrameList(AsyncMarket(self._client, MarketModel.model_validate(m)) for m in data)

async def get_market(self, ticker: str) -> AsyncMarket:
"""Get a single historical market by ticker."""
response = await self._client.get(f"/historical/markets/{ticker.upper()}")
model = MarketModel.model_validate(response["market"])
return AsyncMarket(self._client, model)

async def get_candlesticks(
self,
ticker: str,
*,
start_ts: int,
end_ts: int,
period: CandlestickPeriod = CandlestickPeriod.ONE_HOUR,
) -> list[HistoricalCandlestick]:
"""Get historical candlestick data for a settled market.

Args:
ticker: Market ticker.
start_ts: Start Unix timestamp (seconds).
end_ts: End Unix timestamp (seconds).
period: Candlestick interval (1min, 1hr, 1day).
"""
query = urlencode({
"start_ts": start_ts,
"end_ts": end_ts,
"period_interval": period.value,
})
response = await self._client.get(
f"/historical/markets/{ticker.upper()}/candlesticks?{query}"
)
return [
HistoricalCandlestick.model_validate(c)
for c in response.get("candlesticks", [])
]

async def get_fills(
self,
*,
ticker: str | None = None,
max_ts: int | None = None,
limit: int = 100,
cursor: str | None = None,
fetch_all: bool = False,
**extra_params,
) -> DataFrameList[FillModel]:
"""Get historical fills (requires authentication).

Args:
ticker: Filter by market ticker.
max_ts: Filter fills before this Unix timestamp.
limit: Results per page (max 1000).
cursor: Pagination cursor.
fetch_all: Automatically fetch all pages.
"""
params = {
"ticker": normalize_ticker(ticker),
"max_ts": max_ts,
"limit": limit,
"cursor": cursor,
**extra_params,
}
data = await self._client.paginated_get("/historical/fills", "fills", params, fetch_all)
return DataFrameList(FillModel.model_validate(f) for f in data)

async def get_orders(
self,
*,
ticker: str | None = None,
max_ts: int | None = None,
limit: int = 100,
cursor: str | None = None,
fetch_all: bool = False,
**extra_params,
) -> DataFrameList[AsyncOrder]:
"""Get historical orders (requires authentication).

Args:
ticker: Filter by market ticker.
max_ts: Filter orders updated before this Unix timestamp.
limit: Results per page (max 1000).
cursor: Pagination cursor.
fetch_all: Automatically fetch all pages.
"""
params = {
"ticker": normalize_ticker(ticker),
"max_ts": max_ts,
"limit": limit,
"cursor": cursor,
**extra_params,
}
data = await self._client.paginated_get("/historical/orders", "orders", params, fetch_all)
return DataFrameList(AsyncOrder(self._client, OrderModel.model_validate(d)) for d in data)

async def get_trades(
self,
*,
ticker: str | None = None,
min_ts: int | None = None,
max_ts: int | None = None,
limit: int = 100,
cursor: str | None = None,
fetch_all: bool = False,
**extra_params,
) -> DataFrameList[TradeModel]:
"""Get historical public trades.

Args:
ticker: Filter by market ticker.
min_ts: Filter trades after this Unix timestamp.
max_ts: Filter trades before this Unix timestamp.
limit: Results per page (max 1000).
cursor: Pagination cursor.
fetch_all: Automatically fetch all pages.
"""
params = {
"ticker": normalize_ticker(ticker),
"min_ts": min_ts,
"max_ts": max_ts,
"limit": limit,
"cursor": cursor,
**extra_params,
}
data = await self._client.paginated_get("/historical/trades", "trades", params, fetch_all)
return DataFrameList(TradeModel.model_validate(t) for t in data)
5 changes: 5 additions & 0 deletions pykalshi/_sync/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .exchange import Exchange
from .api_keys import APIKeys
from .communications import Communications
from .history import History
from ..exceptions import RateLimitError
from .._utils import normalize_ticker, normalize_tickers

Expand Down Expand Up @@ -210,6 +211,10 @@ def api_keys(self) -> APIKeys:
def communications(self) -> Communications:
return Communications(self)

@cached_property
def history(self) -> History:
return History(self)

def feed(self) -> Feed:
"""Create a new async real-time data feed."""
from ..feed import Feed
Expand Down
Loading
Loading