trade-worker Isolate Profile
Comprehensive engineering specification for the Hoox Trade Execution Worker, covering multi-exchange client architectures, HMAC signing, and D1 database ledgers.
This page
The trade-worker is the execution engine of the Hoox trading platform. Deployed as a private compute isolate behind the edge firewall, this worker is responsible for calculating order parameters, executing leverage scaling, performing cryptographic HMAC-SHA256 signature calculations, placing spot trades on Bybit, Binance, and MEXC, routing spot DEX (uniswap-ethereum, uniswap-arbitrum, jupiter-solana) through WEB3_WALLET_SERVICE POST /swap, and offloading transactional metrics. Perps are not supported.
⚡ 1. Declared Wrangler Configurations & Bindings
The trade-worker does not expose a public URL, communicating internally via V8 Service Bindings. Its wrangler.jsonc maps out its critical storage, queue, and database hooks:
{
"name": "trade-worker",
"main": "src/index.ts",
"compatibility_date": "2026-06-08",
"compatibility_flags": ["nodejs_compat"],
"account_id": "<CLOUDFLARE_ACCOUNT_ID>",
"placement": {
"mode": "smart",
},
"kv_namespaces": [
{
"binding": "CONFIG_KV",
"id": "<KV_NAMESPACE_ID>",
},
],
"r2_buckets": [
{ "binding": "REPORTS_BUCKET", "bucket_name": "trade-reports" },
{ "binding": "SYSTEM_LOGS_BUCKET", "bucket_name": "hoox-system-logs" },
],
"queues": {
"consumers": [
{
"queue": "trade-execution",
"max_retries": 3,
"dead_letter_queue": "trade-execution-dlq",
},
],
},
"durable_objects": {
"bindings": [
{
"name": "EXCHANGE_CONNECTION_MANAGER",
"class_name": "ExchangeConnectionManager",
},
],
},
"services": [
{ "binding": "D1_SERVICE", "service": "d1-worker" },
{ "binding": "TELEGRAM_SERVICE", "service": "telegram-worker" },
{ "binding": "ANALYTICS_SERVICE", "service": "analytics-worker" },
{ "binding": "WEB3_WALLET_SERVICE", "service": "web3-wallet-worker" },
],
}
Secrets (INTERNAL_KEY_BINDING, EXCHANGE_KEY_BINDING, EXCHANGE_SECRET_BINDING, optional EXCHANGE_TESTNET_*, WALLET_EXECUTE_KEY_BINDING) are set with wrangler secret put — they are not declared in wrangler. D1 is reached over D1_SERVICE, not a local DB binding.
🔄 Position reconciliation
Fills write D1 positions via non-blocking waitUntil for latency. To recover ledger drift, POST /api/positions/reconcile (trade-execute internal auth) pulls open positions from each exchange with credentials and upserts D1 (OPEN / CLOSED). Agent housekeeping cron invokes this path automatically.
| Body field | Effect |
|---|---|
exchanges | Optional list (binance / bybit / mexc / uniswap-ethereum / uniswap-arbitrum / jupiter-solana) |
testnet | Reconcile testnet namespace + clients |
dryRun | Diff only — no D1 writes |
🧪 Test trading (test: true)
| Topic | Behavior |
|---|---|
| Schema | Optional test?: boolean on WebhookPayload and TradeQueueMessage |
| Hosts | Binance Futures testnet · Bybit testnet · MEXC rejected |
| Credentials | Prefer *_TESTNET_* secrets; fall back to live keys with a warn log |
| Transport | Always REST; live WebSocket Durable Object is never used |
| D1 trades | status = 'TEST_EXECUTED' |
| D1 positions | id = {exchange}-testnet-{symbol}-{side} |
| Telegram | Exchange label includes [TEST] (single notify — no queue double-send) |
| Analytics | Exchange blob encoded as {exchange}:test |
| Live WS perf | When use_websocket=true and live, router skips REST client construction |
Operator guide: Test Trading.
Spot DEX venues
Spot swaps route through WEB3_WALLET_SERVICE (web3-wallet-worker POST /swap). Spot only — perps are not supported.
| Name | Chain | Kind | Toggle | Quantity | Test flag |
|---|---|---|---|---|---|
uniswap-ethereum | ethereum | spot | exchange:uniswap-ethereum:enabled | LONG spends QUOTE (ExactIn) to buy BASE; SHORT sells BASE | Rejected (TEST_TRADING_UNSUPPORTED) — mainnet-only |
uniswap-arbitrum | arbitrum | spot | exchange:uniswap-arbitrum:enabled | same | Rejected — mainnet-only |
jupiter-solana | solana | spot | exchange:jupiter-solana:enabled | same | Rejected — mainnet-only |
Uniswap venues send dexVersion: "v3" (ETH/USDC liquidity is V3; Arbitrum's V2 router in the wallet worker is SushiSwap). Native ETH/SOL map to the wrapped mint on contract paths. Credential source is wallet-binding (mesh key), not CEX API secrets. Symbols must be BASE/QUOTE (slash). Unset venue toggles mean enabled; "false" skips the venue.
Operator guide: DEX spot swaps. hoox setup writes WALLET_EXECUTE_KEY_BINDING automatically.
🔀 2. The Provider-Based ExchangeRouter Pattern
To support multiple centralized exchanges with different API schemas while maintaining a clean code structure, trade-worker implements a Provider Composition Pattern:
A. Generic Exchange Provider Interface
The client abstraction is defined inside the shared monorepo package @hoox-sh/hoox-shared/types:
export interface IExchangeProvider<TClient, TEnv> {
readonly name: string;
createClient(env: TEnv, options?: { testnet?: boolean }): TClient;
hasCredentials(env: TEnv): boolean;
/** False for exchanges with no public REST sandbox (e.g. MEXC). */
readonly supportsTestTrading?: boolean;
}
B. Dynamic Runtime Routing
The ExchangeRouter evaluates the incoming symbol and parses settings in CONFIG_KV in sub-milliseconds:
- Default Path: Routes trades to the default CEX declared in
exchanges:default_routing(typicallybybit). - Dynamic Symbol Redirects: Parses overrides in KV (e.g.
exchanges:routing:SOLUSDT = binance). If present, the router bypasses Bybit and instantiates theBinanceProviderinstantly without redeploying code. - Test trading: When the payload includes
"test": true, the router creates the client withtestnet: true(Binance →testnet.binancefuture.com, Bybit →api-testnet.bybit.com). MEXC and all spot DEX venues reject withTEST_TRADING_UNSUPPORTED. Test mode always uses REST (live WebSocket Durable Objects are skipped). Credentials preferEXCHANGE_TESTNET_*secrets (legacyBINANCE_TESTNET_*/BYBIT_TESTNET_*still resolve as fallbacks), then live bindings. - Live WS perf: when
exchange:{name}:use_websocket=trueand the trade is live, the router skips REST client construction entirely and hands off to the Durable Object.
🔌 3. Internal REST API Specification
Note: For the canonical endpoint directory with full request/response examples across all workers, see
/docs/devops/api/endpoints.
Internal routes (mesh auth; prefer TRADE_EXECUTE_KEY_BINDING, else INTERNAL_KEY_BINDING):
| Method | Path | Purpose |
|---|---|---|
POST | /webhook | Gateway / pyne / email dispatch |
POST | /process | Legacy / queue-shaped body |
GET/POST | /api/signals | Signal list / insert |
GET | /api/system-logs | Operator SSE log feed |
POST | /api/positions/reconcile | Exchange ↔ D1 position sync |
GET | /report | Report payload for report-worker |
GET | /health | Liveness |
A. Process Order Pipeline
Invoked by the hoox gateway (POST /webhook) or the TRADE_QUEUE consumer.
- Endpoint:
/process(legacy alias of the same execution path) - Method:
POST - Headers:
X-Internal-Auth-Key: <INTERNAL_KEY_BINDING> - JSON Payload:
{'{'} "requestId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", "payload": {'{'} "exchange": "bybit", "action": "LONG", "symbol": "BTCUSDT", "quantity": 0.005, "leverage": 10, "test": true {'}'} {'}'} - Success Response (200 OK):
{'{'} "success": true, "result": {'{'} "orderId": "18049284739", "status": "Filled", "price": 68425.5 {'}'}, "error": null {'}'}
🛡️ 4. Standardized Exception Handling
All execution rejects and validation failures are intercepted by the trade-worker error middleware and formatted using the shared Errors factory from @hoox-sh/hoox-shared/errors:
import { Errors } from "@hoox-sh/hoox-shared/errors";
// 1. Parameter Validation Failure
if (quantity <= 0) {
return Errors.badRequest("Quantity parameter must be greater than zero.");
}
// 2. Exchange Signature Timeout
if (timestampExpired) {
return Errors.unauthorized(
"Timestamp verification failed. Check system NTP sync."
);
}
// 3. API Execution Rejects
try {
await exchange.placeOrder(order);
} catch (err: any) {
return Errors.internal(`Exchange API Reject: ${err.message}`);
}
Tip
If the exchange API rejects an order due to account rate-limiting, the queue consumer automatically returns a retry flag. Cloudflare® Queues will back off and re-route the batch at intervals starting at 30 seconds, protecting your strategy from missed fills.
🔗 Next Steps
- hoox Gateway Profile — Review WAF rules and Durable Object idempotency locks.
- D1 Database Operations — Apply schema.sql, run tracking migrations, and query the ledger.