Python SDK — tria-trade

A fully async Python port of @tria-sdk/api-trading — one TriaClient, two venues (Hyperliquid + Decibel), normalized shapes, and non-overridable Tria builder-fee attribution. Same method names, same shapes, same TRIA_TRADE_* environment contract as the TypeScript SDK.

Install

pip install tria-trade

Requires Python 3.11+. Every method is async.

Quickstart

import asyncio
from tria_trade import (
    TriaClient, TriaClientConfig, HlConfig, DecibelConfig, generate_client_order_id,
)

async def main():
    client = TriaClient(TriaClientConfig(
        hl=HlConfig(agent_private_key="0x…", account_address="0x…"),
        decibel=DecibelConfig(
            delegate_private_key="0x…",      # omit for a read-only Decibel client
            aptos_owner_address="0x…",        # SDK derives the primary subaccount
            node_api_key="<geomi-node-key>",  # required (Decibel rejects anonymous reads)
            gas_station="<geomi-gs-key>",     # optional — sponsored (no-APT) writes
        ),
    ))

    print(await client.ticker(venue="hl", market="BTC"))

    status = await client.place_order({
        "venue": "hl", "market": "BTC", "side": "buy", "size": "0.001",
        "price": "50000", "tif": "gtc", "clientOrderId": generate_client_order_id(),
    })
    await client.cancel_order(venue="hl", order_id=status["orderId"])

asyncio.run(main())

An SDK environment minted from Settings → API Keys (see Credentials) or the tria-trade provision CLI works unchanged — build the config from os.environ using the TRIA_TRADE_* variables.

Configuration

  • Name
    development
    Type
    bool = False
    Description

    False → Tria's production builder; True → staging builder. Both trade on real mainnet venues — only the builder/treasury address differs.

  • Name
    hl
    Type
    HlConfig | None
    Description

    agent_private_key + account_address. Omit to disable Hyperliquid.

  • Name
    decibel
    Type
    DecibelConfig | None
    Description

    node_api_key (required) enables public market data; add aptos_owner_address for private reads; add delegate_private_key to trade; gas_station enables Geomi-sponsored writes (the delegate then needs no APT).

  • Name
    telemetry
    Type
    TelemetryConfig | None
    Description

    Optional. TelemetryConfig(enabled=False) opts out; client_id attributes usage. Defaults to on (see Telemetry).

Method surface

Identical semantics to the TypeScript surface — see Trading & orders and Market data & streams for per-method detail. The Python signatures:

Writes

  • Name
    place_order(args)
    Type
    -> OrderStatus
    Description

    Submit one order (dict with venue, market, side, size, tif, optional clientOrderId / price / reduceOnly / Decibel tpTriggerPrice / slTriggerPrice). clientOrderId is optional — omit it and the SDK mints one; re-using a live one is rejected. On HL the price is rounded to the venue rule before signing, and a fill populates avgFillPrice.

  • Name
    place_batch(venue, orders)
    Type
    -> list[OrderStatus | TriaError]
    Description

    Up to 20 orders per venue action; per-row success or TriaError.

  • Name
    cancel_order(venue, order_id= | client_order_id=)
    Type
    -> CancelResult
    Description

    Idempotent — a second cancel returns {"status": "not_found"}.

  • Name
    cancel_all_orders(venue, market=None)
    Type
    -> CancelAllResult
    Description

    Bulk cancel with a per-order success / failure breakdown.

  • Name
    modify_order(venue, order_id=/client_order_id=, new_price=, new_size=)
    Type
    -> OrderStatus
    Description

    HL: native modify (new orderId). Decibel: emulated as cancel + replace.

  • Name
    attach_tp_sl(venue, market, take_profit=, stop_loss=)
    Type
    -> dict
    Description

    HL: native reduce-only TP/SL. Decibel: raises (TP/SL is at-placement-time).

  • Name
    close_position(venue, market, slippage_bps=None)
    Type
    -> OrderStatus
    Description

    Flatten via a reduce-only IOC at the slippage band (default 2%).

Reads

  • Name
    order_status(venue, order_id)
    Type
    -> OrderStatus
    Description
  • Name
    open_orders(venue, market=None)
    Type
    -> list[OrderStatus]
    Description
  • Name
    fills(venue, market=, from_=, to=, limit=)
    Type
    -> list[Fill]
    Description
  • Name
    positions(venue, market=None)
    Type
    -> list[Position]
    Description
  • Name
    balances(venue)
    Type
    -> list[Balance]
    Description
  • Name
    margin_state(venue)
    Type
    -> MarginState
    Description
  • Name
    account(venue)
    Type
    -> AccountSnapshot
    Description
    Combined balances + margin + leverage + builder + network + mode. On HL, Unified Accounts are detected and collateral is read from spotClearinghouseState; mode is "unified" / "legacy_cross".
  • Name
    markets / ticker / orderbook / candles / recent_trades
    Type
    public market data
    Description
    See Market data.

Streams & risk

subscribe_fills, subscribe_positions, subscribe_orders, subscribe_account each take venue= + a callback and return an Unsubscribe; on_connection(callback) reports {venue, status} lifecycle events. scheduled_cancel(venue, deadline_ms=) is HL-native (Decibel raises).

def on_fill(fill):  # Fill dict
    print(fill["side"], fill["size"], fill["market"], "@", fill["price"])

unsubscribe = await client.subscribe_fills(venue="hl", callback=on_fill)
# … later …
unsubscribe()
await client.close()  # release WS + HTTP clients + telemetry sink on shutdown

Helpers

from tria_trade import generate_client_order_id, round_to_tick_size

cloid = generate_client_order_id()           # 0x + 32 lowercase hex
px = round_to_tick_size("100.123", "0.1")    # "100.1" — integer-precise, no float drift

Errors

Every failure raises TriaError with a .code of VENUE_REJECTED, VENUE_RATE_LIMIT, VENUE_UNREACHABLE, INVALID_ARGUMENT, MISSING_BUILDER, or CONFIG — never a venue-native error. See Errors & attribution.

from tria_trade import TriaError

try:
    await client.place_order({...})
except TriaError as e:
    if e.code == "VENUE_RATE_LIMIT":
        ...  # back off; e.retry_after_ms may be set

Telemetry

The SDK emits anonymous usage telemetry (trade actions, latencies, error categories) to a Tria-owned proxy on the same New Relic schema the apps feed (platform = "python-sdk"). It is non-blocking and fire-and-forget — a hung or unreachable endpoint is indistinguishable from telemetry-off and never delays or fails a trade. Wallets are truncated and free-text is scrubbed of key/email runs; private keys and full addresses are never sent. Opt out with TRIA_TRADE_TELEMETRY=0 or telemetry=TelemetryConfig(enabled=False). Full details on Telemetry & privacy.

Provisioning CLI

tria-trade provision sets up an operator's HL + Decibel accounts (mint the HL agent, register the Decibel delegate, approve builders, fund the venues) and writes a ready-to-use .env. Every action is signed on-chain with the operator's own master key — it never calls a Tria backend.

export HL_MASTER_PRIVATE_KEY=0x…        # or TRIA_MNEMONIC="word1 word2 …"
export APTOS_MASTER_PRIVATE_KEY=0x…      # for Decibel (or derived from TRIA_MNEMONIC)
export DECIBEL_NODE_API_KEY=<geomi-key>

tria-trade provision --dry-run           # print the plan; no chain writes
tria-trade provision --hl-amount 50 --decibel-amount 50 --output .env

Flags: --hl-amount / --decibel-amount (target USDC), --output, --dry-run, --skip-hl / --skip-decibel, --development, --gas-station-key, --force-new-agent / --force-new-decibel-delegate.

Examples

The package ships runnable examples (place_order, cancel_order, subscribe_fills, multi_venue) that read credentials from the environment. For shared concepts and the TypeScript equivalents, see the SDK Examples.

Was this page helpful?