O2Client — High-level client¶
The O2Client class is the primary entry point for interacting with
the O2 Exchange. It orchestrates wallet management, account lifecycle,
session management, trading, market data retrieval, and WebSocket streaming.
See also
The O2 Exchange platform documentation at https://docs.o2.app covers the underlying REST and WebSocket APIs that this client wraps.
Construction and lifecycle¶
- class o2_sdk.client.O2Client(network=Network.TESTNET, custom_config=None)[source]¶
High-level client for the O2 Exchange.
- Parameters:
network (
Network) – Which network to connect to.custom_config (
NetworkConfig| None) – Optional custom network configuration, overriding the built-in config for the selected network.
The client manages an HTTP session (via
aiohttp) and an optional WebSocket connection for streaming. Always callclose()when done, or use the async context manager:async with O2Client(network=Network.TESTNET) as client: ...
Wallet management¶
These static methods create or load wallet objects. No network calls are required.
- static O2Client.generate_wallet()[source]¶
Generate a new Fuel-native wallet with a random private key.
- Returns:
A new wallet with
private_key,public_key, andb256_addresspopulated.- Return type:
owner = O2Client.generate_wallet() # or equivalently: owner = client.generate_wallet()
- static O2Client.generate_evm_wallet()[source]¶
Generate a new EVM-compatible wallet with a random private key.
The wallet derives both an Ethereum-style address (keccak-256) and a Fuel B256 address (the EVM address zero-padded to 32 bytes).
- Returns:
A new wallet with
private_key,public_key,evm_address, andb256_addresspopulated.- Return type:
evm_owner = client.generate_evm_wallet() print(evm_owner.evm_address) # 0x-prefixed, 42 chars print(evm_owner.b256_address) # 0x-prefixed, 66 chars (zero-padded)
Account lifecycle¶
- async O2Client.setup_account(wallet)[source]¶
Set up a trading account idempotently.
This method performs the complete account setup flow:
Check if a trading account already exists for the wallet address.
Create a new account if needed (
POST /v1/accounts).Mint test tokens via the faucet (testnet/devnet only; non-fatal if the faucet is on cooldown).
Whitelist the account for trading (idempotent; non-fatal on error).
Safe to call on every bot startup.
- Parameters:
wallet (
Signer) – The owner wallet (Fuel or EVM).- Returns:
Account info including the
trade_account_id.- Return type:
account = await client.setup_account(owner) print(account.trade_account_id) print(account.exists) # True
Session management¶
- async O2Client.create_session(owner, markets, expiry_days=30)[source]¶
Create a new trading session.
A session delegates signing authority from the
ownerwallet to a temporary session key, scoped to specific market contracts. The session key is used to sign all subsequent trade actions.- Parameters:
- Returns:
Session info with the session key, account ID, and authorized contract IDs.
- Return type:
- Raises:
O2Error – If the account does not exist (call
setup_account()first).
session = await client.create_session( owner=owner, markets=["fFUEL/fUSDC"], expiry_days=7, )
- O2Client.session: :class:`~o2_sdk.models.SessionInfo` | None¶
The active session stored on the client.
Trading¶
- async O2Client.create_order(market, side, price, quantity, order_type=OrderType.SPOT, settle_first=True, collect_orders=True, session=None)[source]¶
Place an order with automatic encoding, signing, and nonce management.
Prices and quantities accept normalized numeric inputs (
str,int,float,Decimal, orChainInt) and are automatically converted to on-chain integers based on market precision. The SDK validates orders against on-chain constraints.- Parameters:
market (str |
Market) – Market pair (e.g.,"FUEL/USDC"),market_id,contract_id, orMarket.side (
OrderSide) – The order side.price (str | int | float | Decimal |
ChainInt) – Price input in human units, or explicit raw chain integer viaChainInt.quantity (str | int | float | Decimal |
ChainInt) – Quantity input in human units, or explicit raw chain integer viaChainInt.order_type (
OrderType|LimitOrder|BoundedMarketOrder) – The order type. UseOrderTypeenum for simple types (SPOT,MARKET,FILL_OR_KILL,POST_ONLY),LimitOrderfor limit orders, orBoundedMarketOrderfor bounded market orders.settle_first (bool) – If
True(default), prepend aSettleBalanceaction to reclaim filled proceeds before placing the order.collect_orders (bool) – If
True(default), the response includes details of the created order.session (
SessionInfo| None) – Optional explicit session override. If omitted, the client’s active session is used.
- Returns:
The action result including
tx_idand optionalorders.- Return type:
Order types:
from o2_sdk import OrderSide, OrderType, LimitOrder, BoundedMarketOrder # Spot (default) — rests on the book await client.create_order("fFUEL/fUSDC", OrderSide.BUY, 0.02, 100.0) # PostOnly — rejected if it would match immediately await client.create_order( "fFUEL/fUSDC", OrderSide.BUY, 0.02, 100.0, order_type=OrderType.POST_ONLY, ) # BoundedMarket — market order with price bounds await client.create_order( "fFUEL/fUSDC", OrderSide.BUY, 0.025, 100.0, order_type=BoundedMarketOrder(max_price=0.03, min_price=0.01), ) # Limit — with expiry timestamp import time await client.create_order( "fFUEL/fUSDC", OrderSide.BUY, 0.02, 100.0, order_type=LimitOrder(price=0.025, timestamp=int(time.time())), )
- async O2Client.cancel_order(order_id, market=None, market_id=None, session=None)[source]¶
Cancel a specific order.
Either
market(pair string) ormarket_id(hex ID) must be provided.- Parameters:
order_id (str) – The
0x-prefixed order ID to cancel.market (str | None) – Market pair string (e.g.,
"FUEL/USDC").market_id (str | None) – Market ID (hex string).
session (
SessionInfo| None) – Optional explicit session override. If omitted, the client’s active session is used.
- Returns:
The action result.
- Return type:
- Raises:
ValueError – If neither
marketnormarket_idis provided.
- async O2Client.cancel_all_orders(market, session=None)[source]¶
Cancel all open orders for a market.
Fetches up to 200 open orders and cancels them in batches of 5. Returns a list of
ActionsResponse(one per batch), or an empty list if there are no open orders.- Parameters:
session (
SessionInfo| None) – Optional explicit session override. If omitted, the client’s active session is used.
- Returns:
A list of action results (one per batch of up to 5 cancellations), or an empty list if there are no open orders.
- Return type:
list[
ActionsResponse]
- async O2Client.settle_balance(market, session=None)[source]¶
Settle filled order proceeds for a market.
After your orders are filled, the proceeds remain locked in the order book contract until you settle them back to your trading account. The
create_order()method does this automatically whensettle_first=True(the default).- Parameters:
session (
SessionInfo| None) – Optional explicit session override. If omitted, the client’s active session is used.
- Returns:
The action result.
- Return type:
- async O2Client.batch_actions(actions, collect_orders=False, session=None)[source]¶
Submit a batch of typed actions with automatic signing and nonce management.
Actions are submitted as typed market groups (
MarketActions) or fluent request groups (MarketActionGroup). The O2 Exchange supports a maximum of 5 actions per request.- Parameters:
actions (Sequence[
MarketActions|MarketActionGroup]) – A list of action groups.collect_orders (bool) – If
True, return created order details.session (
SessionInfo| None) – Optional explicit session override. If omitted, the client’s active session is used.
- Returns:
The action result.
- Return type:
- Raises:
SessionExpired – If the session has expired.
from o2_sdk import ( CancelOrderAction, CreateOrderAction, SettleBalanceAction, MarketActions, OrderSide, OrderType, ) result = await client.batch_actions( actions=[MarketActions( market_id=market.market_id, actions=[ SettleBalanceAction(to=session.trade_account_id), CancelOrderAction(order_id=Id("0xabc...")), CreateOrderAction( side=OrderSide.BUY, price="25000", quantity="100000000000", order_type=OrderType.SPOT, ), ], )], collect_orders=True, )
Market data¶
- async O2Client.get_markets()[source]¶
Get all available markets on the exchange.
Results are cached for the lifetime of the client.
- Returns:
List of all market definitions.
- Return type:
list[
Market]
- async O2Client.get_depth(market, precision=1)[source]¶
Get the current order book depth snapshot for a market.
- Parameters:
- Returns:
The depth snapshot with
buys,sells,best_bid, andbest_ask.- Return type:
depth = await client.get_depth("fFUEL/fUSDC", precision=1) if depth.best_bid: print(f"Best bid: {depth.best_bid.price}") if depth.best_ask: print(f"Best ask: {depth.best_ask.price}")
- async O2Client.get_bars(market, resolution, from_ts, to_ts)[source]¶
Get OHLCV candlestick bars for a market.
- Parameters:
- Returns:
List of OHLCV bars.
- Return type:
list[
Bar]
import time now_ms = int(time.time() * 1000) bars = await client.get_bars( "fFUEL/fUSDC", resolution="1h", from_ts=now_ms - 86_400_000, # last 24 hours to_ts=now_ms, ) for bar in bars: print(f"{bar.time}: O={bar.open} H={bar.high} L={bar.low} C={bar.close} V={bar.volume}")
Account data¶
- async O2Client.get_balances(account)[source]¶
Get balances for all known assets, keyed by asset symbol.
Iterates over all markets to discover assets, then queries the balance for each unique asset.
- Parameters:
account (
AccountInfo| str) – AnAccountInfoobject or atrade_account_idstring.- Returns:
A dict mapping asset symbol to balance info.
- Return type:
dict[str,
Balance]
balances = await client.get_balances(account) for symbol, bal in balances.items(): print(f"{symbol}: available={bal.trading_account_balance}")
- async O2Client.get_orders(market, account, is_open=None, count=20)[source]¶
Get orders for an account on a market.
- Parameters:
market (str) – Market pair string.
account (
AccountInfo| str) – AnAccountInfoobject or atrade_account_idstring.is_open (bool | None) – Filter by open/closed status.
Nonereturns all.count (int) – Maximum number of orders to return (default 20).
- Returns:
List of orders, most recent first.
- Return type:
list[
Order]
WebSocket streaming¶
All streaming methods return async iterators that yield typed update objects. The underlying WebSocket connection is created lazily on first use and supports automatic reconnection with exponential backoff.
- async O2Client.stream_depth(market, precision=1)[source]¶
Stream real-time order book depth updates.
The first message is a full snapshot; subsequent messages are incremental updates.
- Parameters:
- Returns:
An async iterator of depth updates.
- Return type:
AsyncIterator[
DepthUpdate]
async for update in client.stream_depth("fFUEL/fUSDC"): if update.is_snapshot: print(f"Snapshot: {len(update.changes.bids)} bids, {len(update.changes.asks)} asks") else: print(f"Update: best_bid={update.changes.best_bid}")
- async O2Client.stream_orders(account)[source]¶
Stream real-time order updates for an account.
- Parameters:
account (
AccountInfo| str) – AnAccountInfoobject or atrade_account_idstring.- Returns:
An async iterator of order updates.
- Return type:
AsyncIterator[
OrderUpdate]
- async O2Client.stream_trades(market)[source]¶
Stream real-time trade updates for a market.
- Parameters:
market (str) – Market pair string.
- Returns:
An async iterator of trade updates.
- Return type:
AsyncIterator[
TradeUpdate]
- async O2Client.stream_balances(account)[source]¶
Stream real-time balance updates for an account.
- Parameters:
account (
AccountInfo| str) – AnAccountInfoobject or atrade_account_idstring.- Returns:
An async iterator of balance updates.
- Return type:
AsyncIterator[
BalanceUpdate]
- async O2Client.stream_nonce(account)[source]¶
Stream real-time nonce updates for an account.
Useful for monitoring nonce changes caused by other sessions or external transactions.
- Parameters:
account (
AccountInfo| str) – AnAccountInfoobject or atrade_account_idstring.- Returns:
An async iterator of nonce updates.
- Return type:
AsyncIterator[
NonceUpdate]
Withdrawals¶
- async O2Client.withdraw(owner, asset, amount, to=None)[source]¶
Withdraw funds from the trading account to an external address.
This method signs the withdrawal with the owner key (using
personalSign), not the session key.- Parameters:
- Returns:
The withdrawal result.
- Return type:
result = await client.withdraw(owner=owner, asset="fUSDC", amount=10.0) if result.success: print(f"Withdrawal tx: {result.tx_id}")
Nonce management¶
- async O2Client.get_nonce(trade_account_id)[source]¶
Get the current nonce for a trading account.
Returns the cached value if available; otherwise fetches from the API.
- async O2Client.refresh_nonce(session)[source]¶
Re-fetch the nonce from the API and update the local cache.
Call this after catching an error from
batch_actions()if you suspect the nonce is out of sync (the SDK does this automatically on error).- Parameters:
session (
SessionInfo) – The session whose nonce to refresh.- Returns:
The refreshed nonce.
- Return type: