Skip to content

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/indicators for 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 ​

bash
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:

jsonc
{
  "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 null for the first 14 bars — this is normal. All cumulative indicators need warmup. See Limits below.


Python example ​

python
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:

bash
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 ​

FieldTypeRequiredDescription
klinesarray✓OHLCV bars; each row auto-detected as either list or object (see below)
calcarray✓Indicators to compute, each {name, params, id?} — same shape as /api/indicators
intervalstring✗Candle interval; required for HMM and used for CVD confirmation times
last_bar_closedbool✗Defaults to true; set false for a forming final candle, for features and HMM/CVD confirmation
signalsbool✗Defaults to false; true adds per-bar features and requires interval
include_formingbool✗Defaults to false; requires signals=true, evaluates the forming final sample and appends signal bar_status
echo_klinesbool✗Default true — echoes input klines; set false to shrink the response

Two klines row formats (auto-detected):

jsonc
// 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 ​

FieldTypeDescription
countintNumber of klines processed
intervalstringEchoes request's interval (only when provided)
klinesarrayEcho of input klines (omitted when echo_klines=false)
indicatorsobjectkey = 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:

jsonc
{
  "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.

LimitValueOn violation
Kline count≤ 2000; 5000 for HMM-only requestsBusiness cap: 413; schema hard cap above 5000: 422
open_timestrictly monotonically increasing in ms400
OHLCVmust be finite (no NaN / inf)400
calc listnon-empty, all name in registry400
Invalid indicator parametersUnknown parameter or invalid parameter value400

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 / usageMinimum 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 analysis500+

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.

json
{
  "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:

json
{
  "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.

ValueMeaning
Feature trueEvent occurred on this bar, or state holds on this bar
Feature falseValid evaluation, but the event did not occur or state does not hold
Feature nullUnknown: forming candle, insufficient history, invalid dependent value or input gap
Entire signals: nullThis indicator has no enabled feature output
No signals fieldFeatures 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 interval and fixed-duration bars. Set last_bar_closed: false for 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.
  • signals accepts JSON booleans only, not strings, objects or expressions. Omitted or false means no feature output.
  • /api/chart also accepts signals, 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; null for indicators without independent features.
  • Main-panel panels[].items[] entries with type: "markers", source: "signals": confirmed event=true markers, distinguished by indicator_key.

Example marker item:

json
{
  "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 Authorization header → 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 ​

CodeMeaningWhat to do
400Validation failed (non-monotonic, NaN, unknown indicator, too few columns)Read detail, fix the request
401Authorization header present but token invalidRe-apply
413klines exceeds the applicable cap (2000, or 5000 for HMM-only)Shorten the window without discarding necessary warmup
422Invalid request structure (missing field, wrong type, unknown field)Check the locations in detail
429Rate limitedHonor Retry-After; authenticated requests have a higher quota
500Indicator computation crashed (indicator bug)Report with the indicator name in detail

AI trading infrastructure