Skip to main content

Trading engine — gateway, task manager, and process SDK

Project description

Trading Engine

Run algorithmic trading strategies as managed tasks — with a live dashboard, per-task logs, restart policies, and a market SDK that abstracts across venues.


Quickstart

1 — Install

pip install tribulnation-engine

2 — Initialize

Initialize the engine in a new directory:

engine init
# creates engine.toml, .env, and strats/ directory

Edit engine.toml and .env to configure accounts and strategies.

3 — Write a strategy

# strats/my_strat.py
from tribulnation.engine import Process, RestartPolicy

class MyStrat(Process):
  name = 'my-strat'
  restart = RestartPolicy(on='error', delay=5.0)

  async def run(self, market: str):
    mkt  = await self.sdk.market(market)
    book = await mkt.depth()
    self.log.info('mark price: %s', book.mark_price)

run() parameters become the task's argument schema — they appear as a form in the dashboard and are coerced to the declared types when a task is started.

4 — Start the engine

engine start          # foreground, logs to stdout
engine start -d       # background — writes .engine.pid and engine.log

5 — Run a strategy

From the dashboard at http://localhost:3121 or via the CLI:

engine task start my-strat bitcoin -a market=mexc:spot:BTCUSDT
# engine task start <process_name> <run_id> -a <arg_name>=<arg_value> ...

6 — Stop a strategy

From the dashboard or:

engine task stop my-strat bitcoin
# engine task stop <process_name> <run_id>

Strategies

The Process base

from tribulnation.engine import Process, RestartPolicy

Implement async def run(self, ...). Anything run() needs is injected via self:

Attribute What it is
self.sdk Market SDK — call await self.sdk.market('venue::symbol')
self.log Logger scoped to this task
self.run_id The run ID this task was started with
self.set_state(s) Publish a named state ('connected', 'quoting', …)
self.require(cls, id) Declare a dependency on another running task
self.ipc Call methods on Service processes

Restart policy

class Hedger(Process):
  restart = RestartPolicy(on='error', delay=5.0)
on= Behaviour
'never' Stop on exit or error
'error' Restart after an unhandled exception, with delay seconds between attempts
'always' Restart after any exit

max_attempts=N caps the number of consecutive restarts.

Dependencies

Use require() to gate a task on another reaching a named state:

class DydxMaker(Process):
  async def run(self, maker: str, hedge: str) -> None:
    async with self.require(Hedger, self.run_id, state='connected')(
      listen=maker, hedge=hedge
    ):
      # runs only while Hedger/{self.run_id} is alive and in state 'connected'
      ...

require() also auto-starts the dependency if it isn't running, and cancels the dependent if the dependency stops or errors.

Logging

Standard logging calls are automatically captured and streamed to the dashboard. No setup needed:

log = logging.getLogger(__name__)

class Hedger(Process):
  async def run(self, ...):
    log.info('listener live: %s%s', listen, hedge)
    # or use the process logger:
    self.log.info('works the same way')

INFO and above are always captured. DEBUG records are captured only from loggers whose name starts with a prefix listed in [daemon] debug_loggers:

[daemon]
debug_loggers = ["strats"]

Config reference (engine.toml)

[engine]
processes = [
  "strats.hedger:Hedger",
  "strats.dydx_maker:DydxMaker",
]

[daemon]
host     = "localhost"
port     = 3121
db       = "engine.db"    # SQLite path; default "engine.db"
max_logs = 500            # in-memory log lines per task; default unlimited

[accounts.dydx-main]
venue    = "dydx"
mnemonic = "$DYDX_MNEMONIC"

[accounts.hyperliquid-main]
venue       = "hyperliquid"
address     = "$HYPERLIQUID_ADDRESS"
private_key = "$HYPERLIQUID_PRIVATE_KEY"

[[tasks]]
process = "hedger"
id      = "btc"
args    = { listen = "dydx:perp:BTC-USD", hedge = "hl::BTC" }

Account credentials are resolved from environment variables when the value starts with $. engine start automatically loads .env from the same directory as engine.toml.

[[tasks]] entries are started automatically when the engine starts. Each entry needs process (the registered process name), id (the run ID), and optionally args (a table of keyword arguments matching the process signature).


CLI

engine start                                          # launch gateway + manager (foreground)
engine start -d                                       # background — writes .engine.pid + engine.log
engine stop                                           # stop a background engine
engine task start hedger btc -a listen=dydx:perp:BTC-USD -a hedge=hyperliquid::BTC
engine task stop  hedger btc
engine register strats.new_strat:NewStrat             # hot-load a process class

HTTP API

Method Path Description
GET /api/tasks Running tasks
GET /api/tasks/history Completed tasks (SQLite)
GET /api/task/{process}/{id} Single task
GET /api/task/{process}/{id}/logs Task logs
GET /api/task/{process}/{id}/attempts Restart attempts
POST /api/task/{process}/{id} Start a task — body: {"args": {...}}
DELETE /api/task/{process}/{id} Stop a task
DELETE /api/task/{process}/{id}?purge=true Stop and remove from history
DELETE /api/task/{process}/{id}/logs Clear logs and attempt history
GET /api/processes Registered process classes
POST /api/processes Register a process at runtime
GET /api/health Health check
GET /api/ws WebSocket event feed

Architecture

engine start
├── engine gateway    owns real venue SDK connections, serves the market RPC protocol
└── engine manager    task control plane
  ├── in-process runtime — strategies run as asyncio tasks
  ├── REST + WebSocket API (FastAPI / uvicorn)
  └── SQLite store — task history, logs, restart attempts

Strategies never hold venue connections directly. self.sdk inside a task is a ProxySDK that routes market calls to the gateway over a local Unix socket. The gateway owns the real credentials and connections; the manager owns task lifecycle and the dashboard.

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

tribulnation_engine-0.1.0.tar.gz (100.2 kB view details)

Uploaded Source

Built Distribution

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

tribulnation_engine-0.1.0-py3-none-any.whl (113.4 kB view details)

Uploaded Python 3

File details

Details for the file tribulnation_engine-0.1.0.tar.gz.

File metadata

  • Download URL: tribulnation_engine-0.1.0.tar.gz
  • Upload date:
  • Size: 100.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for tribulnation_engine-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9b3b6801104f830e31ca3b27a3be489841e3455731e53f243ca5d6911e8866af
MD5 f97e72d9fb6a959f8fbd29723885a53b
BLAKE2b-256 5aa45a9d6435e33bc14a18015ad9f2981524876e876bc6090191e639b53a1de7

See more details on using hashes here.

File details

Details for the file tribulnation_engine-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for tribulnation_engine-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0282421e00e713eb675c109e54138aee893f96003df38d2a449a302e261f9514
MD5 4e1b9ab88c7dc9760bd29d411d00480a
BLAKE2b-256 def41efab174ae5c63cb8979b6bf797d81dd777f83da04d03f648d03ff23a24a

See more details on using hashes here.

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