Skip to main content

High-performance, async-first Python SDK for the Topstep (TopstepX / ProjectX Gateway) trading API.

Project description

topstep-sdk

CI License: MIT

A high-performance, async-first Python SDK for the Topstep trading API (TopstepX, powered by the ProjectX Gateway API).

Unofficial. This project is not affiliated with, endorsed by, or sponsored by Topstep, LLC or ProjectX Trading, LLC. "Topstep" and "TopstepX" are trademarks of their respective owners.

Building with an AI agent? See AGENTS.md — a complete capability map plus the non-obvious trading semantics (order side, tick-offset brackets, the live flag, result ordering) that prevent common mistakes.

Why this SDK

  • Async-first core built on httpx, with a synchronous facade for plain scripts.
  • Fast models via msgspec — minimal overhead on high-throughput quote and depth streams.
  • Realtime SignalR client for both the user hub (accounts, orders, positions, trades) and market hub (quotes, trades, depth), with auto-reconnect and resubscribe.
  • Fully typed (py.typed), tiny dependency footprint (httpx, msgspec, websockets).

Status

🚧 Alpha / under active development. The public API may change before 1.0.

Requirements

  • Python 3.12+
  • A TopstepX account and an API key (TopstepX → Settings → API).

Installation

pip install topstep-sdk

Provide credentials as process environment variablesfrom_env() reads os.environ and does not auto-load a .env file (export them, or load a .env yourself first):

export TOPSTEP_USERNAME=your_username
export TOPSTEP_API_KEY=your_api_key

Or pass them directly: AsyncTopstepClient(username="...", api_key="...").

Quickstart (async)

This quickstart is read-only — it never places an order:

import asyncio
from datetime import datetime, timedelta, timezone
from topstep_sdk import AsyncTopstepClient, AggregateBarUnit


async def main() -> None:
    async with AsyncTopstepClient.from_env() as client:  # TOPSTEP_USERNAME / TOPSTEP_API_KEY
        accounts = await client.accounts.search()          # NOTE: may return >1 tradable account
        account = accounts[0]                              # ...select deliberately in real code

        contracts = await client.contracts.search("MNQ")   # fuzzy; pick the intended contract
        mnq = contracts[0]

        now = datetime.now(timezone.utc)
        bars = await client.history.retrieve_bars(
            mnq.id, unit=AggregateBarUnit.MINUTE, unit_number=5,
            start_time=now - timedelta(hours=2), end_time=now, limit=50,
        )
        print(account.name, account.balance, "| last close:", bars[0].close)


asyncio.run(main())

Placing orders

⚠️ This sends a REAL order to your Topstep account. Test on a practice/evaluation account with size=1. Remember accounts.search() may return more than one tradable account — pick the right one deliberately.

# Market buy 1 with a protective OCO bracket. Pass tick DISTANCES (magnitudes) —
# the SDK signs them for the side (stop-loss below a long, take-profit above):
order_id = await client.orders.buy(
    account.id, mnq.id, size=1,
    stop_loss_ticks=40, take_profit_ticks=80,
)
# place() returns immediately; the fill arrives via orders.get(...) or the user hub.
await client.positions.close(account.id, mnq.id)  # flatten the whole contract position

(For exact control you can instead pass the raw, signed stop_loss_bracket= / take_profit_bracket= via PlaceOrderBracket — see AGENTS.md.)

Synchronous

from topstep_sdk import TopstepClient

with TopstepClient.from_env() as client:
    for account in client.accounts.search():
        print(account.id, account.name, account.balance)

Realtime

async with AsyncTopstepClient.from_env() as client:
    market = client.market_hub()

    @market.on_quote
    def _(contract_id: str, quote) -> None:
        print(contract_id, quote.best_bid, quote.best_ask)

    await market.start()
    await market.subscribe_quotes("CON.F.US.MNQ.U26")
    await asyncio.sleep(30)
    await market.stop()

    # User hub: client.user_hub() → on_account / on_order / on_position / on_trade,
    # subscribe_orders(account_id), etc. Auto-reconnects and re-subscribes.

Error handling

Everything the SDK raises derives from TopstepError, so except TopstepError catches the whole surface. Domain rejections (bad order, insufficient funds, not-found, …) surface as APIError with a numeric error_code; transport/auth/rate-limit failures have their own types.

from topstep_sdk import APIError, AuthError, RateLimitError, OrderSide, OrderType
from topstep_sdk.enums import PLACE_ORDER_ERRORS

try:
    order_id = await client.orders.place(
        account.id, mnq.id, side=OrderSide.BUY, type=OrderType.MARKET, size=1
    )
except RateLimitError as e:
    ...  # back off; e.retry_after may be set
except AuthError:
    ...  # bad or expired credentials
except APIError as e:
    name = PLACE_ORDER_ERRORS.get(e.error_code or -1, "Unknown")
    ...  # e.g. "InsufficientFunds", "OutsideTradingHours", "AccountViolation"
Exception Raised when
AuthError 401/403, bad key, login/validate failure
RateLimitError HTTP 429 after retries (.retry_after)
NotFoundError HTTP 404
ServerError HTTP 5xx after retries
APIError domain rejection (success:false); base of the above (.error_code)
TransportError network/timeout after retries
RealtimeError SignalR connect/protocol error
ConfigError missing/invalid credentials

Lookup methods that model "not found" as a value return None instead of raising: contracts.search_by_id(...) and orders.get(...).

Coverage

Full REST surface — Account, Contract (search / by-id / available), History (bars), Order (search / open / by-id / paginated query / place / modify / cancel), Position (open / close / partial-close), Trade (search) — plus both realtime user and market hubs. Auth (API-key login, session validate/refresh), client-side token-bucket rate limiting (50/30s for history, 200/60s otherwise), retry/backoff, and typed error mapping are built in. Verified against the live TopstepX API; see docs/RECONCILIATION.md for spec-vs-reality notes.

Roadmap

  • Project scaffold, config, error hierarchy, CI
  • Auth + HTTP core (login/refresh, rate limiting, retries)
  • REST resources (accounts, contracts, history, orders, positions, trades)
  • Realtime SignalR (user + market hubs)
  • Synchronous facade
  • First PyPI release — pip install topstep-sdk
  • Live-verify bracket/OCO + trailing-stop placement when markets reopen (see docs/TESTING.md)
  • Docs site

Development

uv sync --extra dev
uv run pytest
uv run ruff check .
uv run pyright

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

topstep_sdk-0.1.2.tar.gz (39.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

topstep_sdk-0.1.2-py3-none-any.whl (45.7 kB view details)

Uploaded Python 3

File details

Details for the file topstep_sdk-0.1.2.tar.gz.

File metadata

  • Download URL: topstep_sdk-0.1.2.tar.gz
  • Upload date:
  • Size: 39.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for topstep_sdk-0.1.2.tar.gz
Algorithm Hash digest
SHA256 671207925e5a833a93a44a318f87740a187e01a05ae9e80aa0f9614126da201e
MD5 e24635bc56e2ec7d5eed73920f989cff
BLAKE2b-256 407f6966f6bd420a75f93d1f56d186328bb5d4ab5a372422f77082f053a58025

See more details on using hashes here.

Provenance

The following attestation bundles were made for topstep_sdk-0.1.2.tar.gz:

Publisher: release.yml on tarricsookdeo/topstep-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file topstep_sdk-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: topstep_sdk-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 45.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for topstep_sdk-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d2029373828a4bfec4a53637ba814f980c15b5d895453cc95b946e6513c6c4d8
MD5 f00fc48b16b767b160f471f34407ead2
BLAKE2b-256 a3f8d8d077cf8ef7ecefddb75dc69de6ff1c5de217272475607454c554994d1e

See more details on using hashes here.

Provenance

The following attestation bundles were made for topstep_sdk-0.1.2-py3-none-any.whl:

Publisher: release.yml on tarricsookdeo/topstep-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page