Python SDK for the Tektii Trading Gateway
Project description
Trading Gateway Python SDK
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 errorsAPIConnectionError— network/timeout/DNS failure. Wraps the underlyinghttpxexception (available on__cause__) so user code never needs toimport httpx.APIProtocolError— gateway returned something the SDK cannot parse (unexpected content type, malformed JSON, oversized body). Carriesstatus_code,method,path.APIStatusError— gateway returned a structured error response (hasstatus_code,code,message,details)BadRequestError(400)AuthenticationError(401)NotFoundError(404)ConflictError(409)OrderRejectedError(422) — includesdetails.reject_reasonRateLimitedError(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.NetworkError502 Bad Gateway,503 Service Unavailable,504 Gateway Timeout429 Too Many Requests— honours theRetry-Afterheader 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
- Python 3.11+
- A running Trading Gateway
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1c6097a618ed89546a46046f5c4fe06b2640b38c955dd3a48fb1866b37c93b3
|
|
| MD5 |
14b2dcc819a1f9f31eac052680d1fe65
|
|
| BLAKE2b-256 |
5eb0222d9bc486b4892f9ac739f050324f253e6dad5231c55e6d8e53661878e4
|
Provenance
The following attestation bundles were made for tektii-1.5.1.tar.gz:
Publisher:
release.yml on Tektii/trading-gateway-sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tektii-1.5.1.tar.gz -
Subject digest:
d1c6097a618ed89546a46046f5c4fe06b2640b38c955dd3a48fb1866b37c93b3 - Sigstore transparency entry: 1399447025
- Sigstore integration time:
-
Permalink:
Tektii/trading-gateway-sdk-python@e63228f6092e93575e5ceac8b89a5f8d808e39e5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Tektii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e63228f6092e93575e5ceac8b89a5f8d808e39e5 -
Trigger Event:
workflow_dispatch
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
080a68d4cc989c1256ff64be0f9c7d5084dbcf39e8b6866732e29b2d6a272fe5
|
|
| MD5 |
fb432dc72c9a19a049571d7713de2c8b
|
|
| BLAKE2b-256 |
042e3091922d4cac12379bd33329e3155546066b70e113d8a1fb4c4646586bea
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tektii-1.5.1-py3-none-any.whl -
Subject digest:
080a68d4cc989c1256ff64be0f9c7d5084dbcf39e8b6866732e29b2d6a272fe5 - Sigstore transparency entry: 1399447036
- Sigstore integration time:
-
Permalink:
Tektii/trading-gateway-sdk-python@e63228f6092e93575e5ceac8b89a5f8d808e39e5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Tektii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e63228f6092e93575e5ceac8b89a5f8d808e39e5 -
Trigger Event:
workflow_dispatch
-
Statement type: