Developer Quick Start - Python â
Build your first o2 trade using the official Python SDK.
Prerequisites â
- Python 3.10â3.13 (3.14+ not yet supported due to dependency constraints)
pip install o2-sdk
AI Tools â
Use AI assistants to help build and run your trading bot. See the Skills (gives AI agents the o2 integration workflow and patterns) and the MCP Server (trade via natural language).
1. Set Up Your Project â
mkdir o2-quickstart && cd o2-quickstart
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install o2-sdkCreate your trading script:
touch main.py2. Get Test Funds â
Faucet is not available on mainnet. Deposit funds from your Fuel wallet to your trade account contract via the web-interface or via the Deposit flow.
3. Complete Trading Flow â
Create main.py. The SDK handles network configuration, signing, encoding, and nonce management automatically.
Select your network (the SDK has built-in endpoints for each):
from o2_sdk import Network
network = Network.TESTNET # https://api.o2.appimport asyncio
from o2_sdk import Network, O2Client, OrderSide, OrderType
# Configuration
PRIVATE_KEY = "0xYOUR_PRIVATE_KEY" # Use env vars in production!
async def main():
# 1. Initialize client
client = O2Client(network=Network.TESTNET)
# 2. Load wallet
# Use load_wallet() for an existing key, or generate_wallet() for a new one
wallet = client.load_wallet(PRIVATE_KEY)
print(f"Owner address: {wallet.b256_address}")
# 3. Set up trading account
# Idempotent: creates account, mints test tokens (testnet/devnet),
# and whitelists â safe to call on every startup
account = await client.setup_account(wallet)
print(f"Trade account: {account.trade_account_id}")
# Wait for faucet funds to settle
await asyncio.sleep(5)
# 4. Fetch markets
markets = await client.get_markets()
market = markets[0]
pair = market.pair
print(f"Trading on: {pair}")
# 5. Check balances
balances = await client.get_balances(account.trade_account_id)
for symbol, bal in balances.items():
print(f"{symbol}: {bal.trading_account_balance}")
# 6. Create session
# Delegates trading authority to an ephemeral keypair.
# The session is stored on the client and used implicitly by trading methods.
# o2 sponsors gas for all session-based actions.
session = await client.create_session(
owner=wallet,
markets=[pair],
expiry_days=30,
)
print(f"Session created, expires: {session.session_expiry}")
# 7. Place an order
# Prices and quantities are human-readable â the SDK auto-scales them.
# SettleBalance is auto-prepended by default (settle_first=True).
# Use a low price to avoid immediate fill; notional must be >= ~5 USDC.
result = await client.create_order(
market=pair,
side=OrderSide.BUY,
price="1",
quantity="5",
order_type=OrderType.SPOT,
)
print(f"Order tx: {result.tx_id}")
if result.success and result.orders:
order = result.orders[0]
print(f"Order ID: {order.order_id}")
# 8. Check open orders
await asyncio.sleep(3)
open_orders = await client.get_orders(
account.trade_account_id,
pair,
is_open=True,
)
print(f"Open orders: {len(open_orders)}")
# 9. Cancel order
if open_orders:
await client.cancel_order(
order_id=open_orders[0].order_id,
market=pair,
)
print("Order cancelled")
# 10. Check final balance
final_balances = await client.get_balances(account.trade_account_id)
for symbol, bal in final_balances.items():
print(f"{symbol}: {bal.trading_account_balance}")
await client.close()
print("Done!")
if __name__ == "__main__":
asyncio.run(main())Run the script:
python main.py4. Batch Orders â
Submit up to 5 actions per request using the fluent builder.
Create a new session scoped to the batch market, refresh the nonce, then submit:
from o2_sdk import OrderSide, OrderType
await client.create_session(owner=wallet, markets=["fFUEL/fUSDC"], expiry_days=30)
await client.refresh_nonce()
batch = (
client.actions_for("fFUEL/fUSDC")
.settle_balance()
.create_order(OrderSide.BUY, "0.1", "50")
.create_order(OrderSide.BUY, "1", "5")
.create_order(OrderSide.SELL, "2", "5", OrderType.POST_ONLY)
.build()
)
result = await client.batch_actions([batch], collect_orders=True)
print(f"Batch tx: {result.tx_id}")
if result.orders:
for order in result.orders:
print(f"{order.side} order: {order.order_id}")WARNING
create_order automatically prepends a SettleBalance call (controlled by settle_first, default True). When using batch_actions, add .settle_balance() explicitly if needed. The total action count (including settle) must not exceed 5.
5. Deposit & Withdraw â
The easiest way to deposit and withdraw funds is through the o2 trading UI. The UI supports deposits from and withdrawals to EVM chains (Base, Ethereum) and Fuel wallets, and handles all bridging, wrapping, and unwrapping automatically.
Programmatic deposits & withdrawals coming soon
Fully integrated deposit and withdrawal support via the o2-sdk is currently in development.
In the meanwhile, if you want to go ahead, you can refer to the Fast Bridge docs and implement the deposit and withdrawal flow directly. For contract addresses and asset IDs, see the Network Identifiers page.
Key Concepts â
| Concept | Description |
|---|---|
| Trading Account | On-chain contract holding your balances and order state |
| Session | Ephemeral keypair authorized to trade. Enables fast trading without wallet popups |
| Nonce | Counter for replay protection. Auto-managed by the SDK |
| Gas Sponsorship | o2 pays gas fees for all session-based actions |
| setup_account() | Idempotent â creates account, funds via faucet, whitelists. Safe on every startup |
| SettleBalance | Moves filled order proceeds into your available balance. Auto-prepended by create_order |
Nonce management
The SDK auto-manages nonces. If you encounter errors after a failed transaction, call refresh_nonce() to re-sync:
await client.refresh_nonce(session)SDK Reference â
For the full API reference, advanced patterns (market making, WebSocket streams, external signers), and more examples, see the SDK documentation on GitHub.