Skip to main content

stonepy

PyPI version Python versions CI Docs License: MIT

Python client for the StoneX (CIAPI) v2 trading API.

📖 Documentation: https://aaronmgn.github.io/stonepy/latest/

Features

  • Typed models. Request and response DTO bodies are Pydantic v2 models; some methods take primitive parameters or return bare scalars or lists. The package ships a py.typed marker and generated model stubs, so editors autocomplete fields and mypy and pyright accept snake_case or wire-alias constructor keywords. See the constructor typing notes for current limitations.
  • Sync and async. Identical APIs on StoneXClient and AsyncStoneXClient.
  • Complete coverage. All 128 endpoints of the frozen catalog revision (CATALOG_VERSION) across 19 resource groups, using the v2 variant of every endpoint that has one.
  • Batteries included. Automatic session refresh, configurable retries, client-side rate limiting, secret-field omission in configuration and model representations, redaction in request representations, and a clear exception hierarchy.

Project status: stonepy is pre-1.0 (alpha). The public API may change between minor releases until 1.0; pin a version for production use.

When upgrading, await async-session mutators in extension code and ensure request-field assignments pass validation. If migrating from an older release, also use Request<Name> variants for shared request DTOs, replace entry-point plugins with explicitly constructed resources, and update deprecated resource names. See the release notes, model guide, and extensibility guide for migration details.

Installation

pip install stonepy

Or with uv:

uv add stonepy

Requires Python >= 3.11 and pydantic >= 2.7 (< 3.0), with pydantic >= 2.12 on Python 3.14 and newer. stonepy ships type information (PEP 561 py.typed) that mypy and pyright discover automatically; both checkers participate in the required CI checks.

Quickstart

from stonepy import ClientConfig, StoneXClient
from stonepy.models import ApiLogOnRequestDTO

config = ClientConfig(base_url="https://ciapi.cityindex.com/TradingAPI")

with StoneXClient(config) as client:
    session = client.session.log_on(
        ApiLogOnRequestDTO(
            UserName="username",
            Password="password",
            AppKey="app-key",
            AppVersion="stonepy",
            AppComments="",
        )
    )
    print(session.status_code)

Environment-based configuration is also available:

from stonepy import ClientConfig, StoneXClient

config = ClientConfig.from_env()

with StoneXClient(config) as client:
    print(client.user_account.get_client_and_trading_account())

ClientConfig.from_env() reads STONEX_BASE_URL, STONEX_APP_KEY, STONEX_USERNAME, and STONEX_PASSWORD. STONEX_BASE_URL is required unless base_url= is passed.

Authentication and Sessions

Calling client.session.log_on(...) establishes the authenticated session token. The client attaches the current token to subsequent endpoints that use session authentication. Refresh can replace the token, and client.session.delete_session(...) clears it.

Automatic refresh is enabled either by supplying app_key, username, and password on ClientConfig (directly or via ClientConfig.from_env()), or by a successful manual log_on(), which installs a replay refresh callable. Proactive refresh runs inline, immediately before the request that needs it - synchronously in StoneXClient and awaited in AsyncStoneXClient, with no background task - once the stored token reaches ClientConfig.proactive_refresh_seconds (default 1080.0, i.e. 18 minutes). This threshold is based on token age; no server expiry timestamp is consulted.

After HTTP 401 or ErrorCode 4011 (never 4010) on an authenticated endpoint, the client refreshes the session once and replays the request once. This applies to every endpoint, including non-idempotent order calls, because this authentication rejection means the server did not process the request. Transport, 5xx, and 429 retries remain idempotency-gated. If proactive refresh fails with a stonepy error, the client logs a warning and tries the request with the existing token so reactive authentication recovery can still run. The ErrorCode 4011 envelope is recognised only on non-2xx responses.

config = ClientConfig(
    base_url="https://ciapi.cityindex.com/TradingAPI",
    app_key="app-key",
    username="username",
    password="password",
)  # credentials present -> automatic proactive session refresh

Async Usage

from stonepy import AsyncStoneXClient, ClientConfig
from stonepy.models import ApiLogOnRequestDTO

config = ClientConfig(base_url="https://ciapi.cityindex.com/TradingAPI")

async with AsyncStoneXClient(config) as client:
    session = await client.session.log_on(
        ApiLogOnRequestDTO(
            UserName="username",
            Password="password",
            AppKey="app-key",
            AppVersion="stonepy",
            AppComments="",
        )
    )
    print(session.status_code)

Use aclose() for async clients when not using async with; use close() for sync clients.

Error Handling

StoneXError is the base of the public runtime error hierarchy. Configuration validation can also raise builtin TypeError or ValueError exceptions.

from stonepy import (
    ClientConfig,
    RateLimitError,
    StoneXAPIError,
    StoneXClient,
    StoneXError,
)
from stonepy.models import ApiLogOnRequestDTO

config = ClientConfig(base_url="https://ciapi.cityindex.com/TradingAPI")

try:
    with StoneXClient(config) as client:
        client.session.log_on(
            ApiLogOnRequestDTO(
                UserName="username",
                Password="password",
                AppKey="app-key",
                AppVersion="stonepy",
                AppComments="",
            )
        )
except RateLimitError as exc:
    print(exc.retry_after)
except StoneXAPIError as exc:
    print(exc.http_status, exc.error_code, exc.error_message)
except StoneXError as exc:
    print(exc)

Important subclasses include AuthenticationError, RateLimitError, OrderRejectedError, OrderStatusUnknownError, ResponseParseError, StoneXAPIError, and TransportError.

Unknown fields in nested request DTOs raise Pydantic ValidationError during validation. Request DTOs also validate direct field assignments; in-place edits to nested containers remain unguarded, and submission does not deeply revalidate existing models. See assignment validation. With the default status checks, an order acknowledgement without a usable status raises OrderStatusUnknownError; verify the order state before resubmitting.

Pagination

Paginated API methods return the page DTO documented by StoneX. For example, client.market.list_market_search_paginated(...) accepts page, page_size, and order_by keyword arguments and returns ListMarketSearchPaginatedResponseDTO:

page = client.market.list_market_search_paginated(
    "gold",
    search_by_market_code=False,
    search_by_market_name=True,
    spread_product_type=True,
    cfd_product_type=True,
    binary_product_type=False,
    ascending_order=True,
    include_options=False,
    client_account_id=12345,
    page=0,
    page_size=100,
)
print(page.total_number_of_results)

API Reference

Full documentation - the guides and a complete API reference - is published at https://aaronmgn.github.io/stonepy/latest/.

Development

uv venv
uv sync --extra dev
uv run pytest -q
uv run ruff check .
uv run ruff format --check .
uv run mypy

See CONTRIBUTING.md for the full contributor guide and CODE_OF_CONDUCT.md, and the changelog for release notes.

Support

AI Use Disclaimer

Portions of this project, including the generated API bindings, DTO models, and documentation, were produced with the assistance of AI tooling and reviewed by a human maintainer. The library is tested against the StoneX CIAPI v2 contract but is provided "as is", without warranty of any kind (see LICENSE).

stonepy is unofficial and is not affiliated with, endorsed by, or supported by StoneX, City Index, or GAIN Capital. Trading carries financial risk; validate all behaviour against the official API documentation before using it with a live account.

Metadata

Release files for stonepy 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for stonepy 0.6.0
File Size Uploaded
stonepy-0.6.0.tar.gz 541.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for stonepy 0.6.0
File Interpreter ABI Platform
stonepy-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / stonepy-0.6.0.tar.gz

Download URL stonepy-0.6.0.tar.gz
Size 541.9 kB
Tags Source
SHA-256 checksum
How to use checksums
b0fa0a10cf58d204a0226e382eb900c4a2bcd1663f60c04063b83ec2297a3104
BLAKE2b-256 checksum
How to use checksums
30a2d43b77b001474011bbcb36c604d850b3c6951e405ab5a0b183347eafc512
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release files / stonepy-0.6.0-py3-none-any.whl

Download URL stonepy-0.6.0-py3-none-any.whl
Size 732.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9259078bc3555dead6e2718d625655ab43dcdba34fce01a07fdca3f22430eba4
BLAKE2b-256 checksum
How to use checksums
211c83d24689709eb9a04311e791f3ce69d4e3328196b28460ce6175a3b6a220
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page