[Signals & Trade Spec]

Webhook JSON schemas and the mapping from external trade signals to exchange order payloads.

This document details the exact specifications, JSON validation schemas, and internal translation logic that occurs when an external trade signal (such as a TradingView alert or Email parser) is ingested by the Hoox Gateway and mapped to an active exchange order.


1. Webhook Signal Ingestion Schema

Every signal received by the public gateway at /webhook must contain a valid, authenticated JSON payload. Below is the strict TypeScript interface and parameter schema validated by the gateway's validation middleware:

export interface WebhookSignal {
  apiKey: string; // Authentication token matching CONFIG_KV webhooks:api_key
  exchange: "binance" | "bybit" | "mexc"; // Target exchange router
  action: "LONG" | "SHORT" | "CLOSE"; // Position intent
  symbol: string; // Asset symbol (e.g. "BTCUSDT", "ETHUSDT")
  quantity: number; // Order size (amount of asset or contracts)
  leverage?: number; // Optional leverage multiplier (default: 1, max: 125)
  idempotencyKey?: string; // Optional unique signature for dedup (auto-generated if omitted)
  price?: number; // Optional limit price (default: market price)
  takeProfit?: number; // Optional take-profit price target
  stopLoss?: number; // Optional stop-loss price target
}

Parameter Rules & Type Constraints

ParameterTypeRequiredConstraints
apiKeystringYesMust match the encrypted webhooks:api_key hash in your CONFIG_KV namespace
exchangestringYesOne of: binance, bybit, mexc — must match a configured exchange router
actionstringYesOne of: LONG (buy/open), SHORT (sell/open), CLOSE (flatten position)
symbolstringYesParsed and standardized — BTC-USDT, BTC_USDT, and btc/usdt all become BTCUSDT
quantitynumberYesMust be greater than zero; checked against exchange minimum order size
leveragenumberNoRange: 1 to 125 depending on exchange margin profiles; defaults to 1 (spot)
idempotencyKeystringNoIf omitted, auto-generated from SHA256(symbol + exchange + action + timestamp)
pricenumberNoLimit price; defaults to current market price if omitted
takeProfitnumberNoTarget exit price for automatic partial close
stopLossnumberNoStop-loss trigger price; overrides agent-worker trailing stop if specified

2. Dynamic Payload Translation & Side Mapping

Exchanges do not understand actions like LONG, SHORT, or CLOSE. They process order instructions in terms of Side (BUY or SELL) and PositionSide (LONG or SHORT for multi-margin hedge modes).

The trade-worker parses the incoming signal and translates the business logic into exchange-specific API calls:

A. Translation Table (One-Way Margin / Spot)

ActionPosition StateTranslated Exchange SideOperation Type
LONGClosed / No PositionBUYOpens a long position (spot or margin)
SHORTClosed / No PositionSELLOpens a short position (margin/futures)
CLOSELong Position ActiveSELLCloses and flattens an existing long position
CLOSEShort Position ActiveBUYCloses and flattens an existing short position

B. Hedge-Mode PositionSide Mapping

When running in hedge mode (supported on Bybit and Binance futures), both LONG and SHORT positions can coexist:

Hedge Mode:
  LONG PositionSide = LONG  → BUY opens LONG, SELL closes LONG
  SHORT PositionSide = SHORT → SELL opens SHORT, BUY closes SHORT

One-Way Mode:
  BUY always opens/increases, SELL always closes/reduces

C. Dynamic Position Resolution

When the action is CLOSE, the trade-worker automatically performs a sub-millisecond edge check:

  1. Queries your local D1 transaction ledger to resolve the active position direction for the symbol.
  2. If no position is tracked locally, it performs a real-time portfolio balance check against the exchange's private position endpoint.
  3. Automatically sets the order quantity to match your current open exposure, ensuring a perfect, slippage-free execution that flattens the position without leaving residual micro-contracts.

3. Leverage Scaling & Order Math

If the signal includes a leverage parameter greater than 1 (e.g., "leverage": 10), the trade-worker automatically executes a secure margin transition sequence:

  1. Set Margin Mode: Configures the symbol's margin structure (Isolated vs. Cross) via the exchange API, matching your manifest defaults.
  2. Set Leverage Coefficient: Submits a leverage update payload to the exchange prior to routing the order.
  3. Calculate Collateral: If the order size is defined in USDT terms, the worker scales the execution quantity mathematically:
    Contract Quantity = (Order Size (USDT) × Leverage) / Current Market Price
    
  4. Precision Rounding: Automatically rounds the calculated quantity down to match the exchange's strict asset decimal precision requirements, preventing API rejects.

Precision Table by Exchange

ExchangePrice PrecisionQuantity PrecisionMin Order Size
BinanceVaries by symbolVaries by symbol~$10 equivalent
Bybit0.1 – 0.0013–6 decimal placesVaries by tier
MEXC0.1 – 0.000000012–8 decimal places~$5 equivalent

4. D1 Database Transaction Ledger

Every filled order is persistently written to your globally distributed edge SQLite table. The schema ensures a clean audit log:

CREATE TABLE trades (
  id TEXT PRIMARY KEY,          -- UUID v4 generated by trade-worker
  request_id TEXT NOT NULL,     -- Distributed trace ID from gateway
  exchange TEXT NOT NULL,       -- 'bybit', 'binance', or 'mexc'
  symbol TEXT NOT NULL,         -- e.g. 'BTCUSDT'
  action TEXT NOT NULL,         -- 'LONG', 'SHORT', or 'CLOSE'
  side TEXT NOT NULL,           -- 'BUY' or 'SELL' (exchange-side)
  quantity REAL NOT NULL,       -- Filled asset quantity
  price REAL NOT NULL,          -- Execution entry price (USDT)
  leverage INTEGER DEFAULT 1,   -- Applied leverage multiplier
  fee REAL NOT NULL,            -- Exchange transaction fees (USDT)
  order_id TEXT NOT NULL,       -- Exchange-provided order hash
  status TEXT NOT NULL,         -- 'Filled', 'Rejected', 'Failed', 'Partial'
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE positions (
  id TEXT PRIMARY KEY,
  symbol TEXT NOT NULL UNIQUE,
  side TEXT NOT NULL,           -- 'LONG' or 'SHORT'
  quantity REAL NOT NULL,
  entry_price REAL NOT NULL,
  mark_price REAL,              -- Last known market price
  leverage INTEGER DEFAULT 1,
  unrealized_pnl REAL DEFAULT 0,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

You can query the ledger at any time from your local terminal:

# Query the 10 most recent trades in your D1 database
hoox db query "SELECT created_at, exchange, symbol, action, price, quantity FROM trades ORDER BY created_at DESC LIMIT 10"

# Check open positions
hoox db query "SELECT symbol, side, quantity, entry_price FROM positions WHERE quantity > 0"

5. Queue Failover & Guaranteed Delivery

When the exchange API returns an error (timeout, rate limit, HTTP 5xx), the gateway automatically enqueues the trade payload into Cloudflare Queues for guaranteed delivery:

Retry AttemptDelayTrigger Condition
1ImmediateFirst failure
230 secondsNetwork timeout
31 minuteRate limit (429)
45 minutesExchange error (502/503)
515 minutesExtended maintenance
630 minutesFinal retry before logging as failed

After max retries, the trade is logged to D1 with status = 'Failed' — not lost, but visibly flagged for manual review.


Tip

By utilizing time-series logging via Cloudflare Analytics Engine, Hoox tracks the exact execution duration of each pipeline step. You can audit the latency breakdown (e.g., Gateway auth took 1.2ms, Trade-worker signing took 0.8ms, Bybit API roundtrip took 18.5ms) directly in your Next.js dashboard!

Next Steps