Compute on Your Own Data
POST /api/indicators/compute calculates indicators from candles you supply, without requiring a supported venue or symbol.
Good for:
- Backtest: run indicators over OHLCV history pulled from your DB / CSV
- Custom data sources: counterparty exchange, aggregated contracts, your own tick resamples
- Offline analysis: calculate indicator values for a specific historical window
Not for:
- Live data — use
POST /api/indicatorsfor lower latency, it reads from in-memory store - Multi-timeframe requests such as SMC's
fvg_timeframe— use the market-data indicator endpoint for supported multi-timeframe queries
30-second quickstart
curl -X POST 'https://api.mobiusquant.ai/api/indicators/compute' \
-H 'authorization: Bearer mq_xxx' \
-H 'content-type: application/json' \
-d '{
"klines": [
[1735689600000, 95000.0, 95500.0, 94800.0, 95200.0, 1234.5],
[1735693200000, 95200.0, 95800.0, 95100.0, 95650.0, 1500.2]
],
"interval": "1h",
"calc": [{"name": "rsi", "params": {"period": 14}}]
}'Returns:
{
"count": 2,
"interval": "1h",
"klines": [[1735689600000, 95000.0, ...], ...], // echoed
"indicators": {
"rsi:period=14": {
"columns": ["open_time", "rsi"],
"data": [[1735689600000, null], [1735693200000, null]],
"explain": { /* knowledge: summary_focus / signals / caveats */ }
}
}
}RSI returns
nullfor the first 14 bars — this is normal. All cumulative indicators need warmup. See Limits below.
Python example
import requests
import pandas as pd
# Load historical OHLCV from your own data source
df = pd.read_csv('btc_1h_history.csv') # columns: open_time(ms), open, high, low, close, volume
klines = df[['open_time', 'open', 'high', 'low', 'close', 'volume']].values.tolist()
resp = requests.post(
'https://api.mobiusquant.ai/api/indicators/compute',
headers={'Authorization': 'Bearer mq_xxx'},
json={
'klines': klines,
'interval': '1h',
'calc': [
{'name': 'ema', 'params': {'period': 20}, 'id': 'ema20'},
{'name': 'ema', 'params': {'period': 200}, 'id': 'ema200'},
{'name': 'rsi', 'params': {'period': 14}},
],
},
)
result = resp.json()
print(result['indicators']['ema20']['data'][-1]) # latest EMA20 value
print(result['indicators']['rsi:period=14']['data'][-1])SMC and other "composite" indicators work too
Indicators like SMC (Smart Money Concepts) that produce non-numeric output also return per-bar columns + an objects sidecar:
curl -X POST 'https://api.mobiusquant.ai/api/indicators/compute?explain=false' \
-H 'content-type: application/json' \
-d '{
"klines": [/* ≥500 historical bars */],
"calc": [{"id": "smc", "name": "smc",
"params": {"show_swing_points": true, "show_premium_discount_zones": true}}]
}'The response includes indicators.smc.objects with structured shapes — swing_structures, order_blocks_swing, fair_value_gaps, premium_zone, etc. Feed them straight into a chart renderer or an LLM. See SMC indicator page.
Request schema
| Field | Type | Required | Description |
|---|---|---|---|
klines | array | ✓ | OHLCV bars; each row auto-detected as either list or object (see below) |
calc | array | ✓ | Indicators to compute, each {name, params, id?} — same shape as /api/indicators |
interval | string | ✗ | Candle interval; required for HMM and used for CVD confirmation times |
last_bar_closed | bool | ✗ | Defaults to true; set false for a forming final candle, for features and HMM/CVD confirmation |
signals | bool | ✗ | Defaults to false; true adds per-bar features and requires interval |
include_forming | bool | ✗ | Defaults to false; requires signals=true, evaluates the forming final sample and appends signal bar_status |
echo_klines | bool | ✗ | Default true — echoes input klines; set false to shrink the response |
Two klines row formats (auto-detected):
// Compact (Binance nested-list style): [open_time_ms, open, high, low, close, volume, quote_volume?]
[1735689600000, 95000.0, 95500.0, 94800.0, 95200.0, 1234.5, 117526200.0]
// Object (more explicit)
{
"open_time": 1735689600000,
"open": 95000.0,
"high": 95500.0,
"low": 94800.0,
"close": 95200.0,
"volume": 1234.5,
"quote_volume": 117526200.0 // optional, used by some indicators (VWAP / CVD); defaults to 0
}Response schema
| Field | Type | Description |
|---|---|---|
count | int | Number of klines processed |
interval | string | Echoes request's interval (only when provided) |
klines | array | Echo of input klines (omitted when echo_klines=false) |
indicators | object | key = id or auto-generated name:p1=v1,..., value = single indicator's columns/data/objects/explain |
Each indicators.<key> has the same shape as in POST /api/indicators:
{
"columns": ["open_time", "rsi"],
"data": [[1735689600000, null], [1735693200000, 67.4], ...],
"objects": { /* present only for SMC and other non-numeric indicators */ },
"explain": { /* knowledge: only when ?explain=true */ }
}Limits
HMM requires an explicit interval: 5m/15m/1h/4h/12h/1d; 12h is BYO-only, not live data. HMM returns eight business fields, including hmm_supported (true/false/null), plus open_time and per-result meta. Default normalization=auto selects D1 for 5m/15m/1h and Legacy for 4h/12h/1d; explicit modes override it. At horizon20, input warmup is D1 20/60 or Legacy 153/461 bars; at horizon100, 100/300 or 769/2309. Candidate confirmation is additional. Support=false does not confirm reversal; null includes pending first confirmation. At most eight HMM instances per request. If the final candle is forming, set top-level last_bar_closed: false; otherwise all input candles are assumed closed.
| Limit | Value | On violation |
|---|---|---|
| Kline count | ≤ 2000; 5000 for HMM-only requests | Business cap: 413; schema hard cap above 5000: 422 |
open_time | strictly monotonically increasing in ms | 400 |
| OHLCV | must be finite (no NaN / inf) | 400 |
calc list | non-empty, all name in registry | 400 |
| Invalid indicator parameters | Unknown parameter or invalid parameter value | 400 |
Supply enough input history
This endpoint uses only the candles you supply. Include sufficient history before the period you want to analyze.
So make sure you include enough history:
| Indicator / usage | Minimum bars |
|---|---|
| RSI(14) to stabilize | ≥ 50 |
| EMA(200) / ATR(200) to stabilize | ≥ 200 |
| Bollinger / Keltner / Donchian (default params) | ≥ 30 |
| SMC internal structure + FVG | ≥ 50 |
| SMC swing structure / OB / Strong-Weak / P/D zones | ≥ 100 (default swing_size=50, plus buffer) |
| SMC complete + EQH/EQL stable | ≥ 200 (ATR threshold converges) |
| Production-grade analysis | 500+ |
The first N bars returning null is normal — discard them and use the stable tail.
Native features
Add top-level "signals": true to POST /api/indicators or POST /api/indicators/compute to request native feature results. Of 59 indicators, 35 provide 106 features (101 events and 5 states); the other 24 provide descriptive values without independent feature signals. Each indicator reference documents rules, meanings and validity requirements, or explains why no signals are provided. These describe indicator events or states, not trade instructions.
{
"exchange": "binance",
"market": "perp",
"symbol": "BTCUSDT",
"interval": "5m",
"limit": 100,
"calc": [
{"name": "macd", "id": "macd"},
{"name": "rsi", "id": "rsi"},
{"name": "cvd", "id": "cvd"}
],
"signals": true
}Send this body to /api/indicators?explain=false. For BYO, use /api/indicators/compute?explain=false, replace market, symbol and limit fields with klines, and retain interval, calc and signals.
Each indicator adds a signals object with columns and data, aligned one-to-one with its numeric output rows. The first columns are open_time and confirmed_at_ms in milliseconds, followed by feature columns. confirmed_at_ms is the closed candle's interval-based close time; its presence does not imply that any feature triggered.
For example, indicators.macd.signals:
{
"columns": ["open_time", "confirmed_at_ms", "macd.cross_up", "macd.cross_down", "macd.zero_cross_up", "macd.zero_cross_down", "macd.above_signal", "macd.below_signal"],
"data": [[1735689600000, 1735689900000, true, false, false, false, true, false]]
}This bar has a cross-up and MACD is above Signal, without a zero-axis crossing.
| Value | Meaning |
|---|---|
Feature true | Event occurred on this bar, or state holds on this bar |
Feature false | Valid evaluation, but the event did not occur or state does not hold |
Feature null | Unknown: forming candle, insufficient history, invalid dependent value or input gap |
Entire signals: null | This indicator has no enabled feature output |
No signals field | Features were not requested |
Events are true only on their occurrence bar; states can remain true for multiple bars. Read by column name, not a hard-coded index.
Use GET /api/indicators/signals to query full definitions by indicator, kind or feature ID, including display names, rules, meanings and boundary conditions. Results stay grouped by indicator. Definitions do not indicate that a feature has occurred in current market data.
Compact definitions remain available with GET /api/indicators/registry?include_signals=true. Each indicator adds has_signals and a signals list containing id, kind=event|state, description, required_history, and version. The default registry response does not include these fields.
Constraints:
- Live features support connected Binance, Bybit, OKX and Hyperliquid feeds, including Binance stock perpetuals, but not stock spot venues. Other markets return 400 when features are requested; numeric-only queries remain available.
- BYO requires
intervaland fixed-duration bars. Setlast_bar_closed: falsefor a forming last bar; all earlier bars must be closed. - Forming bars have null confirmation times and feature values. Features use the numeric output window, not an independent window.
- From the first interval gap onward, features remain null for the rest of that input. Supply contiguous data with sufficient warmup; do not turn unknown into false.
- Revised data or a different history start may change results. This API does not provide monitoring progress, catch-up or deduplication guarantees.
signalsaccepts JSON booleans only, not strings, objects or expressions. Omitted or false means no feature output./api/chartalso acceptssignals, as described below.
Feature charts
For intrabar warnings, set signals: true and include_forming: true and read the same signal table. Forming rows have bar_status=forming and confirmed_at_ms=null; features may disappear at close. Chart event markers carry the same classification; provisional CVD objects have status=forming. Default requests are unchanged. See forming trials for live and BYO usage.
For unknown-value reasons, native links, effective parameters and actual input ranges, use the optional top-level include options. Omitting them preserves the existing response.
Send the same example request to POST /api/chart, keeping "signals": true. Alongside ordinary candles, indicator plots and structural objects, the response adds:
indicator_signals[responseKey]: the same{columns, data}feature format as the calculation endpoint;nullfor indicators without independent features.- Main-panel
panels[].items[]entries withtype: "markers", source: "signals": confirmedevent=truemarkers, distinguished byindicator_key.
Example marker item:
{
"type": "markers",
"source": "signals",
"indicator_key": "macd",
"data": [{
"time": 1735689600,
"confirmed_at_ms": 1735689900000,
"position": "aboveBar",
"shape": "circle",
"role": "neutral",
"indicator_key": "macd",
"signal_ids": ["macd.cross_up"],
"text": "MACD 金叉"
}]
}time is the confirmation candle's open time in seconds; confirmed_at_ms is its close time in milliseconds. Markers are never backdated to a pivot and never represent forming bars, null or false. Multiple events on one candle for one indicator instance share a marker, retaining all signal_ids. No empty marker item is emitted. States remain readable but are not plotted on every candle; candles are not recolored.
CVD retains divergence segments and endpoints; with this option, unified markers replace its confirmation arrows to avoid duplicates. SMC objects remain available for structural drawings; clients generating their own confirmation annotations should deduplicate against signal_ids. Omit signals or set it to false to keep the original chart response, without indicator_signals or unified event markers.
Auth & rate limits
Same as /api/indicators:
- No
Authorizationheader → anonymous quota per IP - Valid
Bearer mq_xxx→ higher token quota - Don't have a token yet? Apply for one — it's free.
Error codes
| Code | Meaning | What to do |
|---|---|---|
400 | Validation failed (non-monotonic, NaN, unknown indicator, too few columns) | Read detail, fix the request |
401 | Authorization header present but token invalid | Re-apply |
413 | klines exceeds the applicable cap (2000, or 5000 for HMM-only) | Shorten the window without discarding necessary warmup |
422 | Invalid request structure (missing field, wrong type, unknown field) | Check the locations in detail |
429 | Rate limited | Honor Retry-After; authenticated requests have a higher quota |
500 | Indicator computation crashed (indicator bug) | Report with the indicator name in detail |
Related
POST /api/indicators— Calculate indicators for supported markets and symbolsGET /api/indicators/registry— Available indicators and their parameters- SMC indicator page — Details of the SMC
objectssidecar - AI Agent Manual — Engine integration guide for LLMs
- Skill Runtime Lookup — Endpoint decision tree for Skill integration