Skip to content

Recipes

Short, copy-paste task snippets across the StoneX (CIAPI v2) resource groups. Each snippet defines its own variables so you can paste and adapt it directly.

All snippets use the synchronous StoneXClient. The AsyncStoneXClient exposes the same resource groups and method names with async/await and async with.

Note

Replace https://example.com/ciapi and all credential / account / market values with your own. The base_url points at your CIAPI v2 root.

Log on and reuse the session

Build a ClientConfig, open the client as a context manager, and call client.session.log_on(...) with an ApiLogOnRequestDTO. The client stores the returned session token internally and attaches it to later endpoints that use session authentication. A successful manual log-on also installs the callable used for proactive and reactive refresh.

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

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

with StoneXClient(config) as client:
    response = client.session.log_on(
        ApiLogOnRequestDTO(
            user_name="my-username",
            password="my-password",
            app_key="my-app-key",
            app_version="stonepy",
            app_comments="",
        )
    )
    print("session token:", response.session)

    # The token is now reused automatically by subsequent calls on `client`.
    positions = client.order.list_open_positions()
    print("open positions:", len(positions.open_positions or []))

To log off explicitly, call client.session.delete_session(user_name, session):

client.session.delete_session("my-username", response.session or "")

Configure automatic session refresh from the environment

If you set credentials on the ClientConfig (directly or via from_env()), the client can recover authentication without an explicit log_on() call. The first authenticated request is sent without a token and may receive HTTP 401 or ErrorCode 4011 (never 4010); the client then logs on, replays that request once, and stores the new token. This one-time authentication replay applies to every endpoint, including non-idempotent order calls, because the rejection means the server did not process the request. Transport, 5xx, and 429 retries remain idempotency-gated. A later request refreshes an old token based on its age before it is sent. from_env() reads STONEX_BASE_URL, STONEX_APP_KEY, STONEX_USERNAME, and STONEX_PASSWORD; base_url is required.

export STONEX_BASE_URL="https://example.com/ciapi"
export STONEX_APP_KEY="my-app-key"
export STONEX_USERNAME="my-username"
export STONEX_PASSWORD="my-password"
from stonepy import StoneXClient, ClientConfig

config = ClientConfig.from_env()

with StoneXClient(config) as client:
    # No explicit log_on() call needed - a 401 or ErrorCode 4011 triggers one refresh and replay.
    positions = client.order.list_open_positions()
    print("open positions:", len(positions.open_positions or []))

Tip

from_env() accepts keyword overrides, e.g. ClientConfig.from_env(proactive_refresh_seconds=600.0). Config credentials enable refresh without an explicit log-on. A successful manual log_on() also installs a refresh callable.

Search markets (all 9 required arguments)

list_market_search_paginated takes nine required positional-or-keyword arguments before any keyword-only paging options. Omitting one is a common mistake. The required arguments, in order, are: query, search_by_market_code, search_by_market_name, spread_product_type, cfd_product_type, binary_product_type, ascending_order, include_options, client_account_id.

from stonepy import StoneXClient, ClientConfig

config = ClientConfig.from_env()

with StoneXClient(config) as client:
    results = client.market.list_market_search_paginated(
        "GBP/USD",  # query
        True,  # search_by_market_code
        True,  # search_by_market_name
        True,  # spread_product_type
        True,  # cfd_product_type
        False,  # binary_product_type
        True,  # ascending_order
        False,  # include_options
        123456,  # client_account_id
        page=0,
        page_size=10,
        order_by="Name",
    )
    print("total matches:", results.total_number_of_results)

The keyword-only options and their defaults are page=0, page_size=10, order_by="Name", use_mobile_short_name=False, and trading_account_id=None.

Fetch open positions

list_open_positions takes an optional keyword-only trading_account_id. It returns a ListOpenPositionsResponseDTO whose open_positions field is a list of position DTOs.

from stonepy import StoneXClient, ClientConfig

config = ClientConfig.from_env()

with StoneXClient(config) as client:
    response = client.order.list_open_positions(trading_account_id=123456)
    for position in response.open_positions or []:
        print(position.order_id, position.market_name, position.direction)

Place a trade order and cancel an order

place_order accepts a NewStopLimitOrderRequestDTO and returns an ApiTradeOrderResponseDTO. cancel_order accepts a CancelOrderRequestDTO.

Warning

The snippet below places and cancels live orders against your account. Run it only against a demo/test account, and double-check market_id, trading_account_id, direction, quantity, and trigger_price before executing. Real orders move real money.

from datetime import UTC, datetime, timedelta
from decimal import Decimal

from stonepy import StoneXClient, ClientConfig
from stonepy.models import NewStopLimitOrderRequestDTO, CancelOrderRequestDTO

config = ClientConfig.from_env()

with StoneXClient(config) as client:
    placed = client.order.place_order(
        NewStopLimitOrderRequestDTO(
            order_id=0,
            market_id=400481000,
            currency="GBP",
            auto_rollover=False,
            direction="buy",
            position_method_id=1,
            quantity=Decimal("1"),
            bid_price=Decimal("0"),
            offer_price=Decimal("0"),
            audit_id="",
            trading_account_id=123456,
            applicability="GTC",
            expiry_date_time_utc=datetime.now(UTC) + timedelta(days=1),
            guaranteed=False,
            trigger_price=Decimal("1.2500"),
            reference="stonepy",
            allocation_profile_id=0,
            order_reference="",
            source="stonepy",
        )
    )
    print("place status:", placed.status, "order id:", placed.order_id)

    # Cancel the order we just placed.
    cancelled = client.order.cancel_order(
        CancelOrderRequestDTO(
            order_id=placed.order_id or 0,
            trading_account_id=123456,
            market_id=400481000,
            reference="stonepy",
        )
    )
    print("cancel status:", cancelled.status)

List and save a watchlist

get_watchlists(client_account_id) returns the client's watchlists. save_watchlist accepts a SaveWatchlistRequestDTO containing a single RequestApiClientAccountWatchlistDTO (with RequestApiClientAccountWatchlistItemDTO items) and returns the saved watchlist_id.

from stonepy import StoneXClient, ClientConfig
from stonepy.models import (
    SaveWatchlistRequestDTO,
    RequestApiClientAccountWatchlistDTO,
    RequestApiClientAccountWatchlistItemDTO,
)

config = ClientConfig.from_env()
client_account_id = 123456

with StoneXClient(config) as client:
    # List existing watchlists.
    existing = client.watchlist.get_watchlists(client_account_id)
    for wl in existing.client_account_watchlists or []:
        print(wl.watchlist_id, wl.watchlist_description)

    # Save a new watchlist holding a single market.
    saved = client.watchlist.save_watchlist(
        SaveWatchlistRequestDTO(
            client_account_id=client_account_id,
            watchlist=RequestApiClientAccountWatchlistDTO(
                watchlist_description="FX Majors",
                display_order=1,
                items=[
                    RequestApiClientAccountWatchlistItemDTO(
                        market_id=400481000,
                        display_order=1,
                    )
                ],
            ),
        )
    )
    print("saved watchlist id:", saved.watchlist_id)

Note

get_watchlists_list(client_account_id, ids, ...) is a related method that fetches specific watchlists by their ids (a list[int]), with optional include_items and include_market_information keyword flags.