Python
Install the Python client, place an order in dollars and shares, and choose between the sync, async and streaming clients.
agara-sdk is the quickest route from nothing to a working trading script. It
takes dollars and shares, converts them to the wire format for you, and maps
HTTP failures onto a small exception hierarchy. It needs Python 3.10 or newer.
The library concentrates on trading: the orderbook, orders, positions, portfolio
and activity. It does not wrap market or event discovery, so find your
token_id through Markets or the public API first.
Source
The package, its tests and a complete worked trading script under examples/.
API reference
Per-operation request and response shapes behind each client method.
Quickstart
Install the SDK
Not on PyPI yet, so install straight from the public repository. Both extras are
optional: async adds an async-native client, streaming adds the WebSocket
client.
pip install "agara-sdk[async,streaming] @ \
git+https://github.com/Mirai-Tech-Inc/agara-sdks@main#subdirectory=python"Once the package is published that becomes pip install 'agara-sdk[async,streaming]', and nothing else on this page changes.
Create a client
Create a token first, following Authentication:
export AGARA_TOKEN="agt_..."The two clients mirror each other method for method, with the same arguments, return shapes and exceptions:
import os
from agara_sdk import AgaraClient
client = AgaraClient(
token=os.environ["AGARA_TOKEN"],
base_url=os.environ["AGARA_BASE_URL"],
)Both implement the context-manager protocol, so with (or async with) closes
the underlying connection cleanly.
Read a market
with AgaraClient(token=os.environ["AGARA_TOKEN"]) as client:
book = client.get_orderbook(token_id)
print(f"best bid {book.best_bid} / ask {book.best_ask}, mid {book.mid}")Orderbook carries best_bid, best_ask, mid and spread so you do not
have to index into the raw levels.
Place an order and wait for the outcome
accepted = client.place_order(
token_id=token_id,
side="BUY",
price=0.60, # dollars per share
shares=1.0, # shares
)
final = client.wait_for_terminal(accepted["order_id"], timeout=30.0)
print(final["status"])Acceptance is not settlement. wait_for_terminal polls until the order reaches
a terminal status, and that status still needs inspecting: a rejected or
partially filled order is a normal terminal outcome.
Amounts
This is the biggest difference from the other two libraries. You write
price=0.60 and the client sends 600000, then converts amounts back on the way
in. micro_to_float is available when you hold a raw value from elsewhere:
from agara_sdk import micro_to_float
micro_to_float(600000) # 0.6Convenience has a cost: a float cannot represent every decimal exactly. For accounting, or anywhere a rounded cent matters, prefer exact strings or integer micro units and treat the float conversion as a display step. See Conventions.
Pagination
List methods take limit and cursor, and hand back the cursor for the next
page. Loop until it comes back empty:
cursor = None
while True:
page = client.list_trades(limit=500, cursor=cursor)
for trade in page["trades"]:
print(trade["fill_id"], trade["price_micro"])
cursor = page["pagination"]["next_cursor"]
if not cursor:
breakKeep that cursor to resume a scan later rather than replaying from the start.
list_orders, list_open_orders, list_positions and list_activities page
the same way.
Error handling
Every failure inherits from AgaraError, with a subclass per HTTP status, so you
can catch broadly or narrowly:
from agara_sdk import AgaraError, RateLimitedError, RejectedError
try:
client.place_order(token_id=token_id, side="BUY", price=0.60, shares=1.0)
except RejectedError as error: # 422, the order was refused on its merits
print("rejected:", error)
except RateLimitedError as error: # 429, back off before retrying
print("slow down:", error)
except AgaraError: # anything else the server reported
raiseBadRequestError (400), AuthError (401), ForbiddenError (403),
NotFoundError (404), ConflictError (409), RejectedError (422),
RateLimitedError (429) and ServerError (5xx) are the full set.
REST calls are never retried for you. A ServerError surfaces so you can choose
your own backoff, and a request that fails in transit may still have taken
effect, so read the state back before sending it again. Errors
lists the problem codes and which are worth another attempt.
Realtime subscriptions
The streaming extra adds an async WebSocket client with typed frames, callback
or iterator dispatch, and automatic reconnection. One client covers both the
public market channels and your private account events:
import asyncio, os
from agara_sdk import streaming
async def main():
client = streaming.AgaraStreamClient(token=os.environ["AGARA_TOKEN"])
@client.on_trade
async def _(trade: streaming.Trade):
print(f"trade {trade.side} {trade.size} @ {trade.price}")
@client.on_fill
async def _(fill: streaming.Fill):
print(f"my fill {fill.fill_id}")
await client.subscribe([
streaming.trades(condition_id),
streaming.account_events(),
])
await client.run()
asyncio.run(main())Reconnection restores your subscriptions but does not replay what you missed while disconnected. Treat a reconnect as a prompt to take a fresh snapshot. Streaming covers the channels and Account stream the private one.
Next steps
Pre-signed orders
The one-time setup for signing locally, and why it beats server-side signing.
Account batches
Sign your own split, merge or withdrawal, one per batch.
Streaming
Channels, sequence numbers and what a gap obliges you to do.
Errors
Problem codes and the failures you must reconcile rather than retry.