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):
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.