tickstreamdocs

DOCS

Algo signals API

Read the live signal and trade events of the algos you rent — one REST call, three languages, a copy-paste polling bot, every field explained.

requiresOne sleeve or above

Every algo you rent runs live on our servers around the clock. Its current signal and every trade it takes are on two endpoints, on the same API key you use for market data. Your dashboard shows the same data; this page is for pulling it into your own code, alerts or trading setup.

1 · Your first call

Take your key from the dashboard and ask for the signal of an algo you rent. The ID is the last part of the algo's page URL — Bellwether is cot-pulse, so its page is /algos/cot-pulse.

curl "https://api.tick-stream.xyz/v1/algos/cot-pulse/signal" \
  -H "Authorization: Bearer sk_live_…"
import requests

r = requests.get(
    "https://api.tick-stream.xyz/v1/algos/cot-pulse/signal",
    headers={"Authorization": "Bearer sk_live_…"},
    timeout=10,
)
r.raise_for_status()
sig = r.json()
print(sig["bias"])   # long | short | flat
const r = await fetch("https://api.tick-stream.xyz/v1/algos/cot-pulse/signal", {
  headers: { Authorization: "Bearer sk_live_…" },
});
const sig = await r.json();
console.log(sig.bias); // long | short | flat
EndpointAccessWhat it returns
GET /v1/algospublicThe catalog: every algo with its ID, name and market.
GET /v1/algos/{id}/signalrentedWhat the algo wants right now: bias or side, regime, open position, protective levels.
GET /v1/algos/{id}/eventsrentedIts live log, newest first: entries, exits, scale-outs, daily marks. ?limit= up to 200 (default 50).
GET /v1/algos/{id}/trackpublicBacktest and live track record: metrics, equity curve, monthly returns.

2 · Reading a signal

There are two shapes. Bellwether sets one bias per day from the COT positioning and trades it inside a fixed window with a fixed bracket, so its signal carries the plan for the day:

{
  "algo": "cot-pulse",
  "date": "2026-10-29",
  "bias": "long",
  "delta_k": 4.47,
  "estimate_k": 46.86,
  "last_cot_anchor": "2026-10-20",
  "execution": {
    "market": "NQ", "contracts": 1,
    "entry_window_et": "09:30–10:30", "session_close_et": "16:00",
    "sl_pts": 25, "tp_pts": 25, "scale_out_half_at_pts": 12.5,
    "reentry_after_stop": false
  },
  "position": null,
  "protect": null
}
biaslong, short or flat for date. Flat means no trade today.
executionHow the day's trade is taken: entry window in New York time, stop and target in points, where half the position is taken off.
positionnull while flat; otherwise the open trade with side and entry.
protectThe live stop and target of the open trade ({ stop, target }), null while flat.
delta_k, estimate_kThe model's positioning read in thousands of contracts — context, not something to act on by itself.

Every other algo reports a side and, where it has one, a regime:

{
  "algo": "backdraft",
  "asof": "2026-10-29",
  "regime": "short-gamma (armed)",
  "side": "flat",
  "in_position": false,
  "contracts": 0,
  "point_value": 20,
  "protect": null
}
sidelong, short or flat — the position the algo holds now.
asofSession date the signal belongs to.
regimePresent on regime-gated algos, e.g. short-gamma (armed). The algo only trades while its regime allows it.
in_position, positionIntraday algos: whether a trade is open, and its dir, entry, stop, target.
armed, gate_openThe setup is live and waiting for its trigger.
contractsSize per 1 unit of risk. Volatility-targeted algos return fractions (0.79) — scale it to your account and round.
protectLive stop / target of the open trade; a null leg means that algo has none by design (e.g. exit at the close).
tip

One line covers both shapes: state = sig.bias ?? sig.side ?? "flat".

3 · The event log

The signal tells you where the algo stands; the events tell you what it did. They arrive newest first, each with a unix timestamp ts in seconds.

{
  "algo": "cot-pulse",
  "events": [
    { "type": "bias", "ts": 1793232627, "date": "2026-10-29", "bias": "long", "delta_k": 4.47 },
    { "type": "exit", "ts": 1793194209, "reason": "BE", "price": 29905.5, "pnl_pts": 0 },
    { "type": "scale_out", "ts": 1793194205, "price": 29918, "pnl_pts": 12.5, "sl_moved_to": 29905.5 },
    { "type": "entry", "ts": 1793194202, "side": "long", "price": 29905.5, "sl": 29880.5, "tp": 29930.5 }
  ]
}
typeMeaning
biasNew daily bias (Bellwether).
entryTrade opened — side or dir (1 long, −1 short), price (price or entry), stop (sl/stop), target (tp/target).
scale_outPart of the position taken off; sl_moved_to is the new stop.
exit / closeTrade closed — reason (SL, TP, BE, moc…) and the result in points (pnl_pts) or dollars (pnl_usd).
mark, reanchorDaily mark-to-market of a position held overnight.
skipThe algo stood aside today, with the reason.
cot_anchorA new COT report was folded into the model (Bellwether).

4 · A bot that watches for changes

Signals change at most a few times a day, so polling once a minute is plenty and stays far inside the rate limits. This loop prints the signal whenever it changes and every new event once — swap the print for your alert, webhook or order code.

# Poll a signal once a minute and act only when it changes.
import os, time, requests

KEY  = os.environ["TICKSTREAM_KEY"]
ALGO = "cot-pulse"
URL  = f"https://api.tick-stream.xyz/v1/algos/{ALGO}"
H    = {"Authorization": f"Bearer {KEY}"}

def state(sig):
    # Bellwether publishes a daily bias, the others a side
    return sig.get("bias") or sig.get("side") or "flat"

last, seen = None, 0
while True:
    try:
        sig = requests.get(f"{URL}/signal", headers=H, timeout=10).json()
        if state(sig) != last:
            last = state(sig)
            print("signal:", last, sig.get("protect"))   # → your alert / order code
        ev = requests.get(f"{URL}/events?limit=20", headers=H, timeout=10).json()
        for e in reversed(ev["events"]):        # newest first → oldest first
            if e["ts"] > seen:
                seen = e["ts"]
                print(e["type"], e)
    except requests.RequestException as err:
        print("retrying:", err)
    time.sleep(60)
// Node 18+ — poll once a minute, act only on change.
const KEY = process.env.TICKSTREAM_KEY, ALGO = "cot-pulse";
const URL = `https://api.tick-stream.xyz/v1/algos/${ALGO}`;
const H = { Authorization: `Bearer ${KEY}` };
const state = (s) => s.bias ?? s.side ?? "flat";

let last = null, seen = 0;
async function tick() {
  try {
    const sig = await (await fetch(`${URL}/signal`, { headers: H })).json();
    if (state(sig) !== last) { last = state(sig); console.log("signal:", last, sig.protect); }
    const { events } = await (await fetch(`${URL}/events?limit=20`, { headers: H })).json();
    for (const e of events.reverse()) if (e.ts > seen) { seen = e.ts; console.log(e.type, e); }
  } catch (err) { console.log("retrying:", err.message); }
}
tick(); setInterval(tick, 60_000);

5 · Errors

401Missing or wrong key — send Authorization: Bearer sk_live_….
403 algo_requiredYour account doesn't rent this algo (or the subscription ended). The message links to its page.
400 bad_idThe ID has characters other than letters, digits and dashes.
502 algos_unavailableOur algo service is restarting — retry in a minute; the algos keep their state.
auto

Rather not run your own loop? The Account & Execution API can trade a rented algo straight onto your own Rithmic account — switch it on per algo in the dashboard.