Skip to main content

TPlus Python Client Utilities

Python clients for interacting with tplus.

Install

To install, use either pip or uv pip:

uv pip install -e .

Onboarding with Codex CLI

This repo includes a Codex-discoverable onboarding skill at .agents/skills/onboard for connecting an EVM wallet key to T+, checking whether it already controls a T+ account, and guiding account creation when needed.

From the repo root:

codex
/skills

Choose onboard, or invoke it directly with:

$onboard onboard me to T+

The flow reads TPLUS_PRIVATE_KEY and TPLUS_API_BASE_URL from .env. Do not paste private keys into chat.

CLI

tpluspy installs a tplus command for interacting with T+ services from the shell — managing local accounts, placing orders, deposits/withdrawals, and on-chain registry/vault operations. Run tplus --help for the full command tree.

For a comprehensive guide covering every subcommand, global options, and the local dev bootstrap flow, see docs/userguides/cli.md.

Usage Example

REST and WebSocket Client (tplus.client)

The tpluspy library also provides an asynchronous client (OrderBookClient) for interacting with the tplus-core REST API and WebSocket streams.

Initialization

To use the client, first initialize it with a User object (for signing requests) and the base URL of your tplus-core instance:

import asyncio
from tplus.client import OrderBookClient
from tplus.utils.user import User

API_BASE_URL = "http://127.0.0.1:8000"  # Replace with your API URL
user = User()


async def run_client():
    # Use async context manager for automatic cleanup
    async with OrderBookClient(API_BASE_URL, default_user=user) as client:
        print("Client initialized.")
        # ... use client methods ...


asyncio.run(run_client())

Use EVM accounts as T+ accounts

An EVM account can back a T+ account: it signs one fixed message and the T+ identity is derived from that signature, so the same wallet always maps to the same T+ account (the one the T+ frontend derives on wallet login).

Pass the account itself anywhere a User is accepted, whether that is default_user=, a per-call user=, or a signer=:

from ape import accounts

account = accounts.load("me")

async with OrderBookClient(API_BASE_URL, default_user=account) as client:
    await client.get_user_inventory()  # signs as the T+ user behind the account
    await client.create_limit_order(..., user=account)  # per-call override

An eth_account signer works the same way, with no [evm] extra:

from eth_account import Account

client = OrderBookClient(API_BASE_URL, default_user=Account.from_key("0x..."))

Use load_user_from_ape_account to hold the User itself, for example to read its public key or to pick a sub-account. load_user loads stored keyfiles by name, so Ape aliases and keyfile names occupy separate namespaces and cannot collide:

from tplus.utils.user import load_user_from_ape_account

user = load_user_from_ape_account("me")  # by Ape alias (needs the `evm` extra)
user = load_user_from_ape_account(account)  # an already-loaded Ape account

The result is a normal T+ User and signs every request the same way.

REST API Usage

The client offers async methods for common REST endpoints:

Fetching Data:

# Get Order Book Snapshot for asset index 200
from tplus.model.asset_identifier import AssetIdentifier

example_asset = AssetIdentifier(Index=200)
orderbook = await client.get_orderbook_snapshot(example_asset)
print(f"Snapshot Sequence: {orderbook.sequence_number}")

# Get Klines for an asset
klines = await client.get_klines(example_asset)
print(f"Klines: {klines}")

# Get Market Details for an asset
market_details = await client.get_market(example_asset)
print(
    f"Market Details: Price Decimals={market_details.book_price_decimals}, Quantity Decimals={market_details.book_quantity_decimals}"
)

# Get orders for the user
user_orders, _ = await client.get_user_orders()
print(f"User Orders: {user_orders}")

# Get trades for the user and asset (per-user trades live on the market-data-service)
from tplus.client import MarketDataClient

md_client = MarketDataClient("http://localhost:8011", default_user=user)
user_asset_trades = await md_client.get_user_trades_for_asset(example_asset, user=user)
print(f"User Asset Trades: {user_asset_trades}")

# Get user inventory
inventory = await client.get_user_inventory()
print(f"Inventory: {inventory}")

Creating Orders:

# Ensure example_asset is defined (e.g., from "Fetching Data" section)
from tplus.model.asset_identifier import AssetIdentifier

example_asset = AssetIdentifier(200)

# Create a Market for an asset (idempotent)
market_creation_response = await client.create_market(asset_id=example_asset)
print(f"Market Creation Response: {market_creation_response}")

# Create a Market Order for a specific asset
market_response = await client.create_market_order(
    asset_id=example_asset,
    quantity=10,  # integer quantity
    side="Buy",
    fill_or_kill=False,
)
print(f"Market Order Response: {market_response}")

# Create a Limit Order for a specific asset
# Good-Till-Cancelled limit order
from tplus.model.limit_order import GTC

limit_response = await client.create_limit_order(
    asset_id=example_asset,
    quantity=5,
    price=1_000,
    side="Sell",
    time_in_force=GTC(),
)
print(f"Limit Order Response: {limit_response}")

# Cancel an Order
# Order ID should be obtained from an order creation response.
order_id_to_cancel = "actual-order-id-from-api"  # Replace with a real order ID
cancel_response = await client.cancel_order(order_id=order_id_to_cancel, asset_id=example_asset)
print(f"Cancel Order Response: {cancel_response}")

# Replace an Order
# Original Order ID should be from an existing, open order.
original_order_id_to_replace = "actual-original-order-id"  # Replace with a real order ID
replace_response, revision = await client.replace_order(
    original_order_id=original_order_id_to_replace,
    asset_id=example_asset,
    new_quantity=6,  # Effective lifetime-total quantity
    new_price=1050,  # Effective limit price
)
print(f"Replace Order Response: {replace_response}")
# `revision` is the replacement's timestamp_ns; a later amend_order must quote it as
# expected_authorization_revision.

# Replace several open orders in one request (up to 50). Each item is signed on its own;
# the OMS sends one message per market and returns one status per item, in order.
batch_replace_response = await client.replace_multiple_orders(
    [
        await client.prepare_replace_order_request(
            original_order_id=oid, asset_id=example_asset, new_quantity=6, new_price=price
        )
        for oid, price in [("order-a", 1050), ("order-b", 1040)]
    ]
)
for status in batch_replace_response.batch_order_status:
    print(f"{status.order_id}: {status.status} {status.reason or ''}")

See examples/rest_usage.py for a runnable demonstration.

WebSocket Streaming

The client provides async iterators to stream real-time data:

from tplus.model.asset_identifier import AssetIdentifier
from tplus.model.orderbook import OrderBookDiff
from tplus.model.trades import Trade

example_asset = AssetIdentifier(200)

# Stream Order Book Diffs
async for diff_update in client.stream_depth(example_asset):
    if isinstance(diff_update, OrderBookDiff):
        print(
            f"[Depth] Seq={diff_update.sequence_number}, Asks={len(diff_update.asks)}, Bids={len(diff_update.bids)}"
        )
    # Add logic to handle the update, e.g., update a local order book

# Stream Finalized Trades
async for trade in client.stream_finalized_trades():
    if isinstance(trade, Trade):
        print(f"[Trade] ID: {trade.trade_id}, Price: {trade.price}, Qty: {trade.quantity}")
# Add logic to handle the trade

# Other available streams:
# client.stream_orders() -> OrderEvent
# client.stream_all_trades() -> TradeEvent
# client.stream_klines(asset_id) -> Timebar

See examples/websocket_usage.py for a runnable demonstration using asyncio.gather to run multiple streams concurrently.

Token Recovery

When a bearer token stops working before it actually expires (e.g. from a service restart), both transports recover on their own. A REST response or WebSocket handshake rejected with 401 or 403 re-authenticates and retries once; a second rejection is raised to the caller. Requests rejected concurrently share a single re-authentication.

Contracts

To interact with the on-chain T+ contracts (or sign T+ settlement messages), install one of the EVM extras:

pip install "tpluspy[evm]"        # web3.py backend (no Ape)
pip install "tpluspy[evm-ape]"    # adds the Ape backend on top of [evm]

The Python API is identical either way. With the web3.py backend, point it at a node via WEB3_PROVIDER_URI (read by web3.auto) — or call tplus.evm.use_web3(rpc_url=...):

export WEB3_PROVIDER_URI="https://arb1.arbitrum.io/rpc"
from tplus.evm.contracts import registry, vault

registry.admin()
vault.getApprovedSettlers()

With the Ape backend, just use ape console / ape run as usual; it's auto-detected whenever Ape is connected to a network:

ape console --network ethereum:sepolia:alchemy
In [1]: from tplus.evm.contracts import registry
In [2]: registry.getAssets()
Out[2]: [
    getAssets_return(
        assetAddress=HexBytes("0x000000000000000000000000f08a50178dfcde18524640ea6618a1f965821715"),
        chainId=11155111,
        maxDeposits=100,
    )
]
In [3]: registry.admin()
Out[3]: '0x467a95fC5359edE5d5dDc4f10A1F4B680694858E'

See docs/userguides/contracts.md for the full guide, including accounts, settlements, and the higher-level managers.

Metadata

Release files for tpluspy 0.5.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 tpluspy 0.5.0
File Size Uploaded
tpluspy-0.5.0.tar.gz 275.3 kB Details

Built distribution (wheel)

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

Total release size: 597.2 kB

Release files / tpluspy-0.5.0.tar.gz

Download URL tpluspy-0.5.0.tar.gz
Size 275.3 kB
Tags Source
SHA-256 checksum
How to use checksums
8d9b77484fe3606dcfc72a88f0d48c2dbaa1c8ace9e1ebfb7487f3050e70b8a5
BLAKE2b-256 checksum
How to use checksums
c7acd3aed8d8d687aaa29c8afef1dac9126442373439508eb27a413a2309bd60
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 2, 2026.

Transparency log

Release files / tpluspy-0.5.0-py3-none-any.whl

Download URL tpluspy-0.5.0-py3-none-any.whl
Size 321.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ef492398e5e627344f32ced892740c968288a0af6904f53c65816e480e06b1c2
BLAKE2b-256 checksum
How to use checksums
0763429f664c1fad7d87b6c8784b19c8349e41edb9a286d3724ef2e6d091bc69
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 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.6.0

2 release files

0.5.1

2 release files

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.1

2 release files

0.2.0

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