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 | flatconst 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 | Endpoint | Access | What it returns |
|---|---|---|
GET /v1/algos | public | The catalog: every algo with its ID, name and market. |
GET /v1/algos/{id}/signal | rented | What the algo wants right now: bias or side, regime, open position, protective levels. |
GET /v1/algos/{id}/events | rented | Its live log, newest first: entries, exits, scale-outs, daily marks. ?limit= up to 200 (default 50). |
GET /v1/algos/{id}/track | public | Backtest 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
} bias | long, short or flat for date. Flat means no trade today. |
execution | How the day's trade is taken: entry window in New York time, stop and target in points, where half the position is taken off. |
position | null while flat; otherwise the open trade with side and entry. |
protect | The live stop and target of the open trade ({ stop, target }), null while flat. |
delta_k, estimate_k | The 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
} side | long, short or flat — the position the algo holds now. |
asof | Session date the signal belongs to. |
regime | Present on regime-gated algos, e.g. short-gamma (armed). The algo only trades while its regime allows it. |
in_position, position | Intraday algos: whether a trade is open, and its dir, entry, stop, target. |
armed, gate_open | The setup is live and waiting for its trigger. |
contracts | Size per 1 unit of risk. Volatility-targeted algos return fractions (0.79) — scale it to your account and round. |
protect | Live stop / target of the open trade; a null leg means that algo has none by design (e.g. exit at the close). |
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 }
]
} | type | Meaning |
|---|---|
bias | New daily bias (Bellwether). |
entry | Trade opened — side or dir (1 long, −1 short), price (price or entry), stop (sl/stop), target (tp/target). |
scale_out | Part of the position taken off; sl_moved_to is the new stop. |
exit / close | Trade closed — reason (SL, TP, BE, moc…) and the result in points (pnl_pts) or dollars (pnl_usd). |
mark, reanchor | Daily mark-to-market of a position held overnight. |
skip | The algo stood aside today, with the reason. |
cot_anchor | A 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
401 | Missing or wrong key — send Authorization: Bearer sk_live_…. |
403 algo_required | Your account doesn't rent this algo (or the subscription ended). The message links to its page. |
400 bad_id | The ID has characters other than letters, digits and dashes. |
502 algos_unavailable | Our algo service is restarting — retry in a minute; the algos keep their state. |
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.