TaifoonTAIFOON
SIGN IN
Customer integration

AlgoTrada Cubicle Trader

AlgoTrada Cubicle is a quantitative algo client built on top of the Taifoon CLOB venue. It runs a proprietary signal generation pipeline in Rust, reads market candles via the venue's public tape, and dispatches orders through POST /book/v1/orders like any other API client. This page documents the integration shape. Use it as a reference for other algorithmic clients wiring into the venue.
Note
AlgoTrada is a third-party customer of the venue. Its internal decision model is proprietary and is described here abstractly. The integration surface is the public CLOB API and nothing else. Treat this page like a wiring diagram for any quant client connecting to Taifoon.

Wire shape

Two processes, one direction of traffic. The client owns its decision model and its own infrastructure; the venue owns the matcher, the tape, and the receipt log.

  AlgoTrada Cubicle (private)         Taifoon CLOB (public venue)
  ┌──────────────────────┐            ┌──────────────────────┐
  │  Rust decision model │  reads     │  /tape/v1/candles    │
  │  (internal)          │ ─────────► │  /tape/v1/sessions   │
  │                      │            │                      │
  │  Decision: BUY/SELL  │  posts     │  /book/v1/orders     │
  │                      │ ─────────► │  /book/v1/fills      │
  │                      │  monitors  │  /tape/v1/sessions   │
  │                      │ ─────────► │  ../replay/receipt   │
  └──────────────────────┘            └──────────────────────┘

1. Reading the tape

The client polls GET /tape/v1/candles for the markets it follows. The response shape is identical in live and warp modes. The mode, speed_x, and t_logicaltriple on each row is how the consumer disambiguates them.

// Pull 1m candles for a given window
#[derive(serde::Deserialize)]
struct Candle { t: i64, o: f64, h: f64, l: f64, c: f64, v: f64 }

#[derive(serde::Deserialize)]
struct CandlesResponse { candles: Vec<Candle> }

let resp: CandlesResponse = reqwest::Client::new()
    .get(format!("https://api.taifoon.dev/tape/v1/candles/{market}/1m"))
    .query(&[("from", from_unix), ("to", to_unix)])
    .send().await?
    .json().await?;

No authentication is required on Phase A. The client uses a stable attribution string when it later posts orders; reads are open.

2. Decision step (abstract)

The client runs its internal strategy on each new candle. The strategy is proprietary and produces aDecision { side, qty, attribution }. From the venue's point of view this is a black box. The matcher only sees the orders it emits.

/// Trait implemented by the client's proprietary strategy.
/// Returns Some(Decision) when the strategy wants to act on this candle,
/// None otherwise.
pub trait Strategy {
    fn decide(&self, candle: &Candle, market: &str) -> Option<Decision>;
}

pub struct Decision {
    pub side: Side,        // Buy | Sell
    pub qty: f64,          // contract qty in base units
    pub attribution: String,
}
The public docs deliberately stop at the trait boundary. How the client decides lives behind that decide() call and is not part of the venue contract. Any third-party algo client can substitute its own Strategy impl and use the same wire shape below.

3. Posting orders

When decide() returns Some(...), the client POSTs to/book/v1/orders. The attribution string is the only identity the matcher sees. AlgoTrada stamps each order with trader-cubicle-<run_id> so operators can filter the venue log.

#[derive(serde::Serialize)]
struct OrderRequest<'a> {
    market: &'a str,
    side: &'a str,           // "buy" | "sell"
    qty: String,             // decimal string
    price: String,           // decimal string (close of latest candle, for limit IOC)
    tif: &'a str,            // "ioc" | "gtc" | "fok"
    attribution: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    session: Option<String>, // None = live; Some(sid) = warp replay
}

#[derive(serde::Deserialize)]
struct OrderResponse { order_id: String, t_placed: i64, speed_x: f64 }

let body = OrderRequest {
    market,
    side: match decision.side { Side::Buy => "buy", Side::Sell => "sell" },
    qty: format!("{:.8}", decision.qty),
    price: format!("{:.2}", candle.c),
    tif: "ioc",
    attribution: format!("trader-cubicle-{}", run_id),
    session: warp_session_id.clone(), // None for live, Some(sid) for replay
};

let res: OrderResponse = client
    .post("https://api.taifoon.dev/book/v1/orders")
    .json(&body)
    .send().await?
    .json().await?;

4. Warp-replay backtest

Before promoting a build to live, the client backtests it inside a warp session. This is the standard backtest flow for any client. AlgoTrada has no special replay path. The samesession_id is then threaded into every order so the matcher routes into the partitioned book.

Mint the session over a historical window:

curl -s -X POST https://api.taifoon.dev/tape/v1/sessions \
  -H 'Content-Type: application/json' \
  -d '{
    "from": "2026-05-01T00:00:00Z",
    "to":   "2026-05-08T00:00:00Z",
    "markets": ["BTC-PERP","ETH-PERP","SOL-PERP"],
    "speed_x": 100.0
  }' | jq

Drive the cursor forward and step the matcher. AlgoTrada points the venue at its own brain endpoint viacubicle_url on the replay-start call; for any other client the same field accepts any HTTP-reachable decision service.

# Begin driving the session; the venue calls back into the client's
# decision service once per logical tick.
curl -s -X POST https://api.taifoon.dev/tape/v1/sessions/<sid>/replay/start \
  -H 'Content-Type: application/json' \
  -d '{
    "cubicle_url": "https://brain.algotrada.ai/decide",
    "attribution": "trader-cubicle-r4271"
  }' | jq

# Poll the causality receipt at any time during or after the run.
curl -s https://api.taifoon.dev/tape/v1/sessions/<sid>/replay/receipt | jq

The receipt is the 3-actor schema documented at /clob/docs/concepts/receipt:decider (the client's prompt + response per tick), matcher (the order / fill stream), trader (the attribution string). The top-level causality_ok bit must be true for the run to be considered usable.

5. Fills and outcomes

After (or during) a session, the client retrieves its fills via GET /book/v1/fills. Thesession query param scopes results to a single warp run; omit it for live fills.

# All fills attributable to this run, in t_placed order.
curl -s 'https://api.taifoon.dev/book/v1/fills?session=<sid>&attribution=trader-cubicle-r4271' | jq

# Live fills (no session) for the same client identity.
curl -s 'https://api.taifoon.dev/book/v1/fills?attribution=trader-cubicle-r4271&limit=200' | jq

Each fill row carries order_id, price, qty,t_filled, the counterparty attribution, and (during a warp) the same(mode, speed_x, t_logical) triple as the candles. The client computes its own outcomes from this stream; the venue does not score strategies.

Operator notes

  • Infrastructure. AlgoTrada runs the Cubicle client on its own infrastructure. The venue does not host the strategy process. It only sees the HTTP traffic.
  • Attribution prefix. Orders show up in venue logs underattribution=trader-cubicle-<run_id>. The trader- prefix buckets the identity into the Directional Trader bracket on the public leaderboard.
  • Counterparty. Public market makers provide liquidity in live mode. In warp replay, the venue's synthetic warp-MM quotes both sides of the seeded book. See/clob/docs/operators/market-maker for the contract.
  • Balance. The client carries its own balance on-chain when on-chain settlement ships; until then, all paper P&L is tracked off-venue by the client and reconciled against the receipt.
  • Promotion gate. Same gate as any other client: 24h paper WR ≥ 85% onn_resolved ≥ 30 before live attribution is unlocked.

Code repo

AlgoTrada team: algotrada.ai. Implementation source is private and available to authorized partners only. The integration shape on this page is the contract any third-party algo client can implement against the same public endpoints.

Note
For a fully public, copy-pasteable reference client, see /clob/docs/quickstart/bot and/clob/docs/concepts/cubicle. The endpoints and attribution mechanics are identical.