---
title: Compute on Your Own Data — Compute API
description: POST your own OHLCV klines to the engine and get back RSI / MACD / SMC and 60+ other indicators. For backtest, CSV uploads, and custom data sources.
---

# Compute on Your Own Data

`POST /api/indicators/compute` lets you run the engine's 60+ indicator pipelines **against klines you supply yourself** — independent of the engine's in-memory cache, not bound to any 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**: validate indicator values for a specific window without being constrained by what the engine has cached

Not for:
- **Live data** — use [`POST /api/indicators`](#related) for lower latency, it reads from in-memory store
- **Multi-timeframe (MTF) indicators** like SMC's `fvg_timeframe` — those need server-side prefetch of multiple timeframes; only the store-backed path supports them

---

## 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](#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](/indicators/smc).

---

## 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`](https://api.mobiusquant.ai/docs#/indicators/post_indicators_api_indicators_post) |
| `interval` | string | ✗ | Metadata only, echoed back; some MTF indicators ignore it |
| `echo_klines` | bool | ✗ | 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

| 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`](https://api.mobiusquant.ai/agents.md):

```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

| Limit | Value | On violation |
|---|---|---|
| Kline count | ≤ **2000** | `413` |
| `open_time` | strictly **monotonically increasing in ms** | `400` |
| OHLCV | must be finite (no NaN / inf) | `400` |
| `calc` list | non-empty, all `name` in [registry](https://api.mobiusquant.ai/api/indicators/registry) | `400` |
| `calc` invalid params | Pydantic / signature mismatch | `422` |

### Important: no warmup buffer

`/api/indicators` internally adds 1000 bars of warmup to feed accumulating indicators (EMA / RSI / ATR). **`/api/indicators/compute` does not** — it uses exactly the klines you supply.

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.

---

## Auth & rate limits

Same as `/api/indicators`:

- No `Authorization` header → anonymous IP bucket (low quota)
- Valid `Bearer mq_xxx` → token bucket (higher quota)
- Don't have a token yet? [Apply for one](https://www.mobiusquant.ai/zh/apply-token) — 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](https://www.mobiusquant.ai/zh/apply-token) |
| `413` | `klines` exceeds 2000 | Batch, or shorten the window |
| `422` | Pydantic validation (missing field, wrong type, unknown field) | Check `detail.loc` |
| `429` | Rate limited | Honor `Retry-After`; token bucket has higher quota |
| `500` | Indicator computation crashed (indicator bug) | Report with the indicator name in `detail` |

---

## Related

- [`POST /api/indicators`](https://api.mobiusquant.ai/docs#/indicators/post_indicators_api_indicators_post) — Reads klines from engine cache and computes indicators (faster, but limited to subscribed venues/symbols)
- [`GET /api/indicators/registry`](https://api.mobiusquant.ai/api/indicators/registry) — All 60+ indicators with parameter signatures
- [SMC indicator page](/indicators/smc) — Details of the SMC `objects` sidecar
- [AI Agent Manual](/agents) — Engine integration guide for LLMs
- [Skill Runtime Lookup](/lookup-guide) — Endpoint decision tree for Skill integration
