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

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.5.1.tar.gz (52.3 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.5.1-py3-none-any.whl (35.5 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for tektii-1.5.1.tar.gz
Algorithm Hash digest
SHA256 d1c6097a618ed89546a46046f5c4fe06b2640b38c955dd3a48fb1866b37c93b3
MD5 14b2dcc819a1f9f31eac052680d1fe65
BLAKE2b-256 5eb0222d9bc486b4892f9ac739f050324f253e6dad5231c55e6d8e53661878e4

See more details on using hashes here.

Provenance

The following attestation bundles were made for tektii-1.5.1.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.5.1-py3-none-any.whl.

File metadata

  • Download URL: tektii-1.5.1-py3-none-any.whl
  • Upload date:
  • Size: 35.5 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.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 080a68d4cc989c1256ff64be0f9c7d5084dbcf39e8b6866732e29b2d6a272fe5
MD5 fb432dc72c9a19a049571d7713de2c8b
BLAKE2b-256 042e3091922d4cac12379bd33329e3155546066b70e113d8a1fb4c4646586bea

See more details on using hashes here.

Provenance

The following attestation bundles were made for tektii-1.5.1-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