Skip to main content

Python SDK for the Tektii Trading Gateway

Project description

Trading Gateway Python SDK

PyPI Python versions CI License: MIT

Python SDK for the Trading Gateway — a unified REST + WebSocket trading proxy that connects your strategies to any broker.

Write your strategy once, run it against Alpaca, Binance, Oanda, Saxo, or the Tektii backtesting engine — zero code changes.

Status: v1.5.0 — Beta

Risk disclaimer

This SDK connects real strategies to real brokers. Bugs in your strategy, in this SDK, or in the upstream gateway can cause financial loss. Validate against the mock provider or the Tektii backtest engine before going live, and do not commit credentials to version control. Distributed under the MIT licence — no warranty, see LICENSE.

Install

pip install tektii

Or with uv:

uv add tektii

Prerequisites

A running Trading Gateway. The fastest way to start:

docker run -e GATEWAY_PROVIDER=mock -p 8080:8080 ghcr.io/tektii/gateway:latest

This starts the gateway with a mock provider that simulates AAPL price movements — perfect for testing.

Quickstart

from tektii import TradingGateway

gw = TradingGateway()  # connects to localhost:8080
account = gw.get_account()
print(f"Balance: {account.balance} {account.currency}")

order = gw.submit_order("AAPL", "buy", "1")
print(f"Order {order.id}: {order.status}")

Async

import asyncio
from tektii import AsyncTradingGateway

async def main():
    async with AsyncTradingGateway() as gw:
        account = await gw.get_account()
        print(f"Balance: {account.balance} {account.currency}")

        order = await gw.submit_order("AAPL", "buy", "1", "limit", limit_price="185.00")
        print(f"Order {order.id}: {order.status}")

asyncio.run(main())

Example strategies

End-to-end reference strategies that use this SDK live in the gateway repository under examples/python/:

  • ma_crossover/ — moving-average crossover with bracket orders and a small state machine.
  • rsi_momentum/ — RSI-based momentum strategy.

Each example is a self-contained strategy with its own tests and README, runnable against the mock provider, a live broker, or the Tektii backtest engine.

Credentials

The SDK never hardcodes credentials. Pass the API key via constructor, or via environment variables:

Variable Purpose Default
TRADING_GATEWAY_API_KEY Bearer token sent in the Authorization header unset
TRADING_GATEWAY_URL Gateway base URL http://localhost:8080
# Constructor (highest precedence)
gw = TradingGateway(api_key="tk_live_…", base_url="https://gw.example.com")

# Environment (fallback)
import os
os.environ["TRADING_GATEWAY_API_KEY"] = "tk_live_…"
gw = TradingGateway()  # picks up TRADING_GATEWAY_API_KEY automatically

Never hardcode a production API key in committed source. Use your secrets manager, environment variables, or a local .env that is git-ignored.

The SDK refuses to send an API key over plain http:// to a non-local host, raising ValueError at construction. Use https:// for remote gateways, or pass allow_insecure=True after auditing the network path (e.g. a private VPN).

Streaming Events

Real-time market data and trading events via WebSocket:

import asyncio
from tektii import AsyncTradingGateway, CandleEvent, OrderEvent

async def main():
    async with AsyncTradingGateway() as gw:
        async with gw.stream() as events:
            async for event in events:
                match event:
                    case CandleEvent(bar=bar):
                        print(f"[{bar.symbol}] Close: {bar.close}")
                    case OrderEvent(order=order):
                        print(f"Order {order.id}: {order.status}")

asyncio.run(main())

The stream auto-reconnects with exponential backoff + jitter (capped at max_reconnect_delay), short-circuits on 401/403 handshake failures, and handles ping/pong heartbeats internally. Malformed frames and unknown event types are logged and skipped — they never kill the iterator.

The same strategy code runs unchanged against a live broker and the Tektii backtest engine. Under the hood the SDK coordinates simulation time progression with the engine for you — no flags to set, no code paths to toggle.

Bracket Orders (Stop-Loss / Take-Profit)

Submit orders with built-in exit management:

order = gw.submit_order(
    "AAPL", "buy", "100", "limit",
    limit_price="185.00",
    stop_loss="180.00",      # Exit if price drops to $180
    take_profit="195.00",    # Exit if price rises to $195
)

The gateway manages SL/TP orders across all brokers with a unified interface — even those that don't natively support bracket orders.

API Reference

Client Construction

from tektii import TradingGateway, AsyncTradingGateway

# Sync (notebooks, simple scripts, production too)
gw = TradingGateway(
    base_url="http://localhost:8080",  # or $TRADING_GATEWAY_URL
    api_key=None,                       # or $TRADING_GATEWAY_API_KEY
    timeout=30.0,                       # float or httpx.Timeout
    headers={"X-Tenant": "acme"},       # extra headers (optional)
    max_retries=2,                      # retry idempotent requests on transient failure
    allow_insecure=False,               # opt-in to plain http:// with API key
)

# Async (high-throughput strategies, async frameworks)
gw = AsyncTradingGateway(...)

# Both support context managers; using `with` / `async with` ensures the
# HTTP connection pool is cleaned up on exit.
async with AsyncTradingGateway() as gw:
    ...

with TradingGateway() as gw:
    ...

The sync client shares a single background event loop + HTTP connection pool across all calls, so a polling strategy does not pay a TLS handshake per request. It cannot be called from inside an existing event loop (e.g., inside a Jupyter cell with %autoawait or a FastAPI startup hook) — use AsyncTradingGateway there instead.

Orders

Method Description
submit_order(symbol, side, quantity, ...) Submit a new order
get_order(order_id) Get order details
list_orders(*, symbol=, status=) List open orders
get_order_history(*, symbol=, status=) Get filled/cancelled orders
modify_order(order_id, *, quantity=, ...) Modify an existing order
cancel_order(order_id) Cancel a specific order
cancel_all_orders(*, symbol=) Cancel all orders

Positions

Method Description
list_positions(*, symbol=) List open positions
get_position(position_id) Get position details
close_position(position_id, ...) Close a position
close_all_positions(*, symbol=) Close all positions

Market Data

Method Description
get_quote(symbol) Current bid/ask/last
get_bars(symbol, timeframe, ...) Historical OHLCV bars

Sizing

Method Description
quantity_for_notional(symbol, *, notional=, equity_fraction=, price=) Convert a target notional or fraction of equity into an instrument quantity at the current price

An order quantity is a fixed instrument amount, not a share of capital — so a default like 0.01 BTC is a near-zero position on a six-figure account. quantity_for_notional sizes the trade for you: pass notional="5000" for $5,000 of exposure or equity_fraction=0.10 for 10% of account equity. It uses the quote midpoint as the reference price (override with price=) and returns a Decimal ready for submit_order:

qty = gw.quantity_for_notional("BTC/USD", equity_fraction=0.10)
gw.submit_order("BTC/USD", "buy", qty)

Account & System

Method Description
get_account() Balance, equity, margin
list_trades(*, symbol=, order_id=) Trade history
get_capabilities() Supported order types, assets
get_status() Broker connection status
get_health() Per-provider health details
get_circuit_breakers() Exit management circuit breaker status

WebSocket Streaming

Method Description
stream() Build an event stream (async or sync context manager + iterator)

Event types: CandleEvent, QuoteEvent, OrderEvent, PositionEvent, AccountEvent, TradeEvent, ConnectionEvent, DataStalenessEvent, RateLimitEvent, ErrorEvent

All events can be pattern-matched with Python's match/case syntax.

Error Handling

from tektii import (
    TradingGateway,
    APIStatusError,
    APIConnectionError,
    OrderRejectedError,
    NotFoundError,
)

gw = TradingGateway()
try:
    gw.submit_order("AAPL", "buy", "999999")
except OrderRejectedError as e:
    # Structured reject reason from the gateway, e.g. "INSUFFICIENT_MARGIN".
    print(f"Rejected: {e.message} ({e.code})")
except NotFoundError as e:
    print(f"Not found: {e.message}")
except APIConnectionError as e:
    # Network / timeout / DNS failure — the original httpx exception
    # is preserved on e.__cause__ for debugging.
    print(f"Transport failure: {e}")
except APIStatusError as e:
    print(f"API error [{e.status_code}]: {e.code} - {e.message}")

Exception hierarchy:

  • TektiiError — base for all SDK errors
    • APIConnectionError — network/timeout/DNS failure. Wraps the underlying httpx exception (available on __cause__) so user code never needs to import httpx.
    • APIProtocolError — gateway returned something the SDK cannot parse (unexpected content type, malformed JSON, oversized body). Carries status_code, method, path.
    • APIStatusError — gateway returned a structured error response (has status_code, code, message, details)
      • BadRequestError (400)
      • AuthenticationError (401)
      • NotFoundError (404)
      • ConflictError (409)
      • OrderRejectedError (422) — includes details.reject_reason
      • RateLimitedError (429)
      • ServerError (500)
      • ProviderUnavailableError (503)

APIStatusError.details carries the gateway's structured error envelope. When you ship provider adapters that echo upstream broker payloads, treat the field as potentially sensitive — do not blindly forward it into third-party log sinks.

Retries

Idempotent requests (GET, DELETE, HEAD) are retried automatically on:

  • httpx.TimeoutException, httpx.TransportError, httpx.NetworkError
  • 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout
  • 429 Too Many Requests — honours the Retry-After header if present

POST and PATCH are never retried automatically — retrying submit_order on a network blip could duplicate fills. Pass max_retries=0 to the client constructor to disable all retries.

Quantities and Prices

All financial values are strings to avoid floating-point precision issues. You can pass str or Decimal:

from decimal import Decimal

# Both work — strings are recommended
gw.submit_order("BTC/USD", "buy", "0.001", "limit", limit_price="50000.00")
gw.submit_order("BTC/USD", "buy", Decimal("0.001"), "limit", limit_price=Decimal("50000.00"))

Response model fields (order.quantity, position.unrealized_pnl, etc.) are strings, matching the gateway's wire format.

Filter datetimes (since, until, start, end) must be timezone-aware — the SDK rejects naive datetimes with a ValueError rather than silently misinterpreting their offset:

from datetime import UTC, datetime

orders = gw.list_orders(since=datetime(2025, 1, 1, tzinfo=UTC))

Requirements

Development

git clone https://github.com/Tektii/trading-gateway-sdk-python
cd trading-gateway-sdk-python
uv sync          # Install deps + create venv
uv run pytest    # Run tests
uv run ruff check src/ tests/  # Lint

Regenerate models from OpenAPI spec

Response models are generated from the gateway's OpenAPI spec using datamodel-code-generator. To regenerate after the spec changes:

./scripts/generate.sh          # Regenerate models
./scripts/generate.sh --check  # Check for drift (useful in CI)

Project structure

src/tektii/
├── __init__.py          # Public exports
├── client.py            # TradingGateway (sync, persistent background loop)
├── async_client.py      # AsyncTradingGateway (async, retries, env vars)
├── _http.py             # Shared HTTP utilities + response safety caps
├── models.py            # Re-exports + WebSocket event models
├── _generated/models.py # Generated from OpenAPI spec (DO NOT EDIT)
├── stream.py            # WebSocket streaming (async + sync)
└── errors.py            # Exception hierarchy

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tektii-1.8.0.tar.gz (61.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tektii-1.8.0-py3-none-any.whl (40.7 kB view details)

Uploaded Python 3

File details

Details for the file tektii-1.8.0.tar.gz.

File metadata

  • Download URL: tektii-1.8.0.tar.gz
  • Upload date:
  • Size: 61.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for tektii-1.8.0.tar.gz
Algorithm Hash digest
SHA256 30af9531e945e1c18dd4fdd10ed15df60552d1313493db9779ce9c5500ed324d
MD5 f7ee0dae79beb306d8fe59c3dff6a774
BLAKE2b-256 8abdffc1fca656b8a9b55723fcdb6b2ec9936743c9a3054fc22aed142bad408d

See more details on using hashes here.

Provenance

The following attestation bundles were made for tektii-1.8.0.tar.gz:

Publisher: release.yml on Tektii/trading-gateway-sdk-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tektii-1.8.0-py3-none-any.whl.

File metadata

  • Download URL: tektii-1.8.0-py3-none-any.whl
  • Upload date:
  • Size: 40.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for tektii-1.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 71215854e1652af7c118bf97b8a364cf7f9ad851f47c84402746d2b7e2bf00ec
MD5 ed5c1bdea0bd97cad751200e36c9b089
BLAKE2b-256 7b6af5fff457f1b80ff7d2eb902667dacd08039bb4df6c1a926b8dd9a37fd6a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for tektii-1.8.0-py3-none-any.whl:

Publisher: release.yml on Tektii/trading-gateway-sdk-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page