> ## Documentation Index
> Fetch the complete documentation index at: https://docs.renesis.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Algorithms API

> Execution algorithms for institutional-grade order execution (TWAP, Ladder, Sweep, TimePace, Spread)

# Algorithms API

The Algorithms API provides **execution algorithms** that break large orders into smaller slices to minimize market impact and achieve better average prices.

## Key Features

* **Time-sliced execution**: TWAP and TimePace spread orders evenly over time
* **Multi-level orders**: Ladder places limit orders at multiple price levels
* **Aggressive fills**: Sweep uses IOC orders for immediate execution
* **Spread trading**: Two-leg execution for basis/arb trades with risk management
* **Anti-gaming**: Randomization of size and timing to avoid detection
* **Limit price guardrails**: Won't execute beyond your price limit
* **TCA metrics**: Arrival price, VWAP, slippage tracking
* **Full lifecycle**: Start, pause, resume, cancel algorithms

***

## Algorithm Types

| Algorithm     | Status    | Description                                                    |
| ------------- | --------- | -------------------------------------------------------------- |
| `twap`        | Available | Time-Weighted Average Price - executes evenly over time        |
| `ladder`      | Available | Places limit orders at multiple price levels                   |
| `sweep`       | Available | Aggressive fill - hits all available liquidity with IOC orders |
| `timepace`    | Available | Fixed quantity per interval, no catch-up                       |
| `spread`      | Available | Two-leg spread/basis trade execution                           |
| `funding_arb` | Available | Delta-neutral funding-rate carry (long spot + short perp)      |

***

## POST /order-execution/algorithms/start

Start a new execution algorithm.

### Request Body (JSON)

| Parameter              | Type   | Required | Description                                                     |
| ---------------------- | ------ | -------- | --------------------------------------------------------------- |
| `algorithm_type`       | string | Yes      | Algorithm type: `twap`, `ladder`, `sweep`, `timepace`, `spread` |
| `symbol`               | string | Yes      | Trading pair (e.g., "BTC/USDC")                                 |
| `side`                 | string | Yes      | `buy` or `sell`                                                 |
| `quantity`             | number | Yes      | Total quantity to execute                                       |
| `exchange_account_ids` | array  | Yes      | List of exchange account IDs                                    |
| `limit_price`          | number | No       | Won't execute beyond this price                                 |
| `params`               | object | Yes      | Algorithm-specific parameters (see below)                       |

***

### TWAP Parameters

| Parameter          | Type   | Default     | Description                                |
| ------------------ | ------ | ----------- | ------------------------------------------ |
| `duration_seconds` | int    | required    | Total execution time in seconds            |
| `interval_seconds` | int    | required    | Time between slices                        |
| `aggression`       | string | `"neutral"` | `passive`, `neutral`, `aggressive`, `auto` |
| `randomize_size`   | bool   | `true`      | +/-20% size randomization                  |
| `randomize_timing` | bool   | `true`      | +/-30% timing randomization                |
| `catch_up_enabled` | bool   | `true`      | Catch up if falling behind                 |
| `max_catch_up_pct` | int    | `200`       | Max catch-up as % of interval size         |

**Request Example**

```bash theme={null}
curl -X POST "https://api.renesis.fi/order-execution/algorithms/start" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "algorithm_type": "twap",
    "symbol": "BTC/USDC",
    "side": "buy",
    "quantity": 0.001,
    "exchange_account_ids": ["binance-001"],
    "params": {
      "duration_seconds": 60,
      "interval_seconds": 10,
      "aggression": "neutral"
    }
  }'
```

**Response Example**

```json theme={null}
{
  "isError": false,
  "message": "Algorithm started successfully.",
  "statusCode": 200,
  "data": {
    "order_id": "893dc6ba-3b13-407a-a06c-41dcdd39d1ee",
    "algorithm_type": "twap",
    "symbol": "BTC/USDC",
    "side": "buy",
    "total_quantity": 0.001,
    "status": "RUNNING",
    "arrival_price": 78836.00,
    "arrival_spread_bps": 10,
    "estimated_completion": "2026-02-02T23:42:58.862514",
    "progress": {
      "filled_quantity": 0.0,
      "remaining_quantity": 0.001,
      "fill_percentage": 0.0,
      "vwap": null,
      "child_orders_filled": 0,
      "total_fees": 0.0
    },
    "state": {
      "current_interval": 1,
      "total_intervals": 6,
      "base_quantity_per_interval": 0.000166
    }
  }
}
```

**How TWAP executes**

TWAP splits the order into `total_intervals = duration_seconds / interval_seconds`
slices and works one interval at a time:

1. **Interval start**, compute the interval's target size
   (`base = total_quantity / total_intervals`, jittered ±20% when
   `randomize_size` is on) and post a **passive limit** at the chosen
   aggression level.
2. **Mid-interval reprice**, if the passive order is still unfilled about
   halfway through the interval, it is **cancelled and reposted as an
   aggressive IOC** so the slice doesn't sit idle on a thin book. The original
   passive is always cancelled first, so an interval is never bought twice.
3. **Catch-up** (when `catch_up_enabled`), if the algo has fallen behind the
   schedule, it sends an extra IOC of up to `max_catch_up_pct%` of the interval
   size to close the gap.
4. **Fill crediting**, every child-order fill (passive or IOC) is credited to
   `progress.filled_quantity` and `child_orders_filled`. The reported fill
   percentage always reflects quantity **actually filled on the exchange**.

<Note>
  **Final interval mops up the remainder.** Because `randomize_size` perturbs
  each interval independently it does not conserve the order total, so the
  randomized targets can sum to slightly less than `quantity`. To guarantee the
  order attempts 100%, the **last interval ignores randomization and targets the
  entire remaining quantity**. Per-order exchange lot-size rounding may still
  leave a tiny remainder; if that remainder is below the venue's minimum order
  size/notional it is unfillable and the algorithm completes at `100%`
  (dust snapped). A larger genuine shortfall is reported honestly (e.g. `94%`),
  not masked.
</Note>

<Note>
  **Completion.** After the last interval the algo waits briefly for any
  in-flight child orders to settle, then transitions to `COMPLETED`. It does not
  hang waiting on reserved-but-unfilled rounding dust.
</Note>

**Best Practices**

* Use `passive` aggression for large orders to minimize market impact
* Keep `catch_up_enabled` on (default) so the order fills its full quantity
* Set `limit_price` as a safety guardrail
* Longer durations reduce market impact but increase timing risk
* Expect the **final slice to be larger** than the others when earlier
  randomized slices came in light, this is the mop-up that drives the order to
  \~100%

***

### Ladder Parameters

| Parameter                  | Type   | Default    | Description                            |
| -------------------------- | ------ | ---------- | -------------------------------------- |
| `num_levels`               | int    | required   | Number of price levels (1-20)          |
| `price_spacing_bps`        | number | `10`       | Spacing between levels in basis points |
| `size_distribution`        | string | `"equal"`  | `equal`, `linear`, `exponential`       |
| `start_price`              | number | market mid | Starting price for the ladder          |
| `end_price`                | number | -          | End price (overrides spacing if set)   |
| `cancel_on_fill_pct`       | number | -          | Cancel remaining levels when X% filled |
| `cancel_on_price_move_bps` | number | -          | Cancel all if mid-price moves X bps    |
| `reprice_on_move_bps`      | number | -          | Recalculate levels if mid moves X bps  |

**Size Distributions**

| Distribution  | Behavior                       | Use Case                           |
| ------------- | ------------------------------ | ---------------------------------- |
| `equal`       | Same quantity at each level    | Standard ladder                    |
| `linear`      | More quantity at better prices | Favor fills near mid-price         |
| `exponential` | Much more at best prices       | Aggressive near-touch accumulation |

**Request Example**

```bash theme={null}
curl -X POST "https://api.renesis.fi/order-execution/algorithms/start" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "algorithm_type": "ladder",
    "symbol": "BTC/USDT",
    "side": "buy",
    "quantity": 0.01,
    "exchange_account_ids": ["binance-sandbox-001"],
    "params": {
      "num_levels": 5,
      "price_spacing_bps": 20,
      "size_distribution": "linear",
      "cancel_on_fill_pct": 80,
      "reprice_on_move_bps": 50
    }
  }'
```

**Response Example**

```json theme={null}
{
  "isError": false,
  "message": "Algorithm started successfully.",
  "statusCode": 200,
  "data": {
    "order_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "algorithm_type": "ladder",
    "symbol": "BTC/USDT",
    "side": "buy",
    "total_quantity": 0.01,
    "status": "RUNNING",
    "arrival_price": 95000.00,
    "state": {
      "levels": [
        {"level_number": 1, "price": 95000.00, "quantity": 0.0033, "status": "active"},
        {"level_number": 2, "price": 94981.00, "quantity": 0.0027, "status": "active"},
        {"level_number": 3, "price": 94962.00, "quantity": 0.0020, "status": "active"},
        {"level_number": 4, "price": 94943.00, "quantity": 0.0013, "status": "active"},
        {"level_number": 5, "price": 94924.00, "quantity": 0.0007, "status": "active"}
      ],
      "total_filled": 0.0,
      "active_orders": 5,
      "initial_mid_price": 95000.00
    }
  }
}
```

**Best Practices**

* Use `linear` distribution to concentrate size near market price
* Set `cancel_on_fill_pct` to 70-80% to capture most fills without overfilling
* Use `reprice_on_move_bps` for volatile markets to keep levels relevant
* Keep `num_levels` at 3-10 for most use cases

***

### Sweep Parameters

| Parameter          | Type   | Default        | Description                                     |
| ------------------ | ------ | -------------- | ----------------------------------------------- |
| `urgency`          | string | `"aggressive"` | Aggression level for pricing                    |
| `limit_price`      | number | -              | Maximum price for buys, minimum for sells       |
| `use_sor`          | bool   | `true`         | Route across multiple venues via SOR            |
| `max_slippage_bps` | number | `50`           | Max slippage from arrival price before blocking |
| `max_retries`      | int    | `3`            | Number of retry rounds if not fully filled      |

**Request Example**

```bash theme={null}
curl -X POST "https://api.renesis.fi/order-execution/algorithms/start" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "algorithm_type": "sweep",
    "symbol": "BTC/USDT",
    "side": "buy",
    "quantity": 0.01,
    "exchange_account_ids": ["binance-sandbox-001"],
    "params": {
      "max_slippage_bps": 30,
      "max_retries": 5
    }
  }'
```

**Response Example**

```json theme={null}
{
  "isError": false,
  "message": "Algorithm started successfully.",
  "statusCode": 200,
  "data": {
    "order_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "algorithm_type": "sweep",
    "symbol": "BTC/USDT",
    "side": "buy",
    "total_quantity": 0.01,
    "status": "RUNNING",
    "arrival_price": 95000.00,
    "state": {
      "sweep_round": 0,
      "total_rounds": 6,
      "remaining_after_round": 0.0,
      "arrival_price": 95000.00
    }
  }
}
```

**Best Practices**

* Sweep is the simplest algorithm - use it when you need immediate fills
* All slices are IOC (Immediate-or-Cancel) - unfilled portions cancel immediately
* Set `max_slippage_bps` to protect against adverse price movement
* Use `max_retries` > 0 for illiquid markets where one pass may not fill

***

### TimePace Parameters

| Parameter               | Type   | Default     | Description                            |
| ----------------------- | ------ | ----------- | -------------------------------------- |
| `quantity_per_interval` | number | required    | Fixed quantity per interval            |
| `interval_seconds`      | int    | required    | Seconds between intervals              |
| `limit_price`           | number | -           | Skip interval if price is beyond limit |
| `aggression`            | string | `"neutral"` | `passive`, `neutral`, `aggressive`     |
| `randomize_size`        | bool   | `true`      | Randomize quantity per interval        |
| `randomize_size_pct`    | number | `10`        | Randomization percentage               |

**Key Difference from TWAP**: TimePace has **no catch-up** logic. If an interval is skipped (due to limit price breach), that quantity is lost. TWAP catches up on missed quantity.

**Request Example**

```bash theme={null}
curl -X POST "https://api.renesis.fi/order-execution/algorithms/start" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "algorithm_type": "timepace",
    "symbol": "BTC/USDT",
    "side": "buy",
    "quantity": 0.01,
    "exchange_account_ids": ["binance-sandbox-001"],
    "params": {
      "quantity_per_interval": 0.002,
      "interval_seconds": 30,
      "aggression": "neutral",
      "randomize_size": true
    }
  }'
```

**Response Example**

```json theme={null}
{
  "isError": false,
  "message": "Algorithm started successfully.",
  "statusCode": 200,
  "data": {
    "order_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "algorithm_type": "timepace",
    "symbol": "BTC/USDT",
    "side": "buy",
    "total_quantity": 0.01,
    "status": "RUNNING",
    "arrival_price": 95000.00,
    "state": {
      "current_interval": 1,
      "intervals_completed": 0,
      "intervals_skipped": 0,
      "quantity_per_interval_actual": 0.002
    }
  }
}
```

**Best Practices**

* Use TimePace when you want predictable, fixed-size executions
* Set `limit_price` to skip intervals during adverse price moves
* Unlike TWAP, skipped intervals do NOT accumulate - choose TimePace for strict pacing
* Runs until total\_quantity is filled or the algorithm is cancelled

***

### Spread Parameters

The anchor leg trades `anchor_symbol` on the order's side; the contra leg
trades `contra_symbol` on the OPPOSITE side (it is a hedge). The contra
symbol may be a derivative (e.g. `BTC/USDT:USDT`); each leg's orders route
to the market type implied by its own symbol.

| Parameter                     | Type   | Default       | Description                                         |
| ----------------------------- | ------ | ------------- | --------------------------------------------------- |
| `anchor_symbol`               | string | required      | Primary leg symbol                                  |
| `contra_symbol`               | string | required      | Hedge leg symbol                                    |
| `anchor_exchange_account_ids` | array  | required      | Exchange accounts for anchor leg                    |
| `contra_exchange_account_ids` | array  | required      | Exchange accounts for contra leg                    |
| `risk_quantity`               | number | required      | Max unhedged exposure                               |
| `lead_mode`                   | string | `"anchor"`    | Which leg posts first: `anchor` or `contra`         |
| `aggression`                  | string | `"neutral"`   | Aggression for posting leg                          |
| `sweep_trigger_mode`          | string | `"immediate"` | Hedge trigger: `immediate`, `disabled`, `threshold` |
| `sweep_threshold_pct`         | number | `0`           | Spread % threshold for `threshold` mode             |
| `pause_offset_pct`            | number | -             | Pause if spread exceeds this %                      |
| `reprice_increment_bps`       | number | `10`          | Reprice increment in bps                            |
| `spread_offset_pct`           | number | `0`           | Target spread offset                                |

**Sweep Trigger Modes**

| Mode        | Behavior                                               |
| ----------- | ------------------------------------------------------ |
| `immediate` | Hedge with IOC order as soon as lead leg fills         |
| `disabled`  | Hedge with limit order (passive)                       |
| `threshold` | Use IOC only when spread exceeds `sweep_threshold_pct` |

**Request Example (BTC spot vs perp basis trade)**

```bash theme={null}
curl -X POST "https://api.renesis.fi/order-execution/algorithms/start" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "algorithm_type": "spread",
    "symbol": "BTC/USDT",
    "side": "buy",
    "quantity": 0.01,
    "exchange_account_ids": ["binance-sandbox-001"],
    "params": {
      "anchor_symbol": "BTC/USDT",
      "contra_symbol": "BTC/USDT:USDT",
      "anchor_exchange_account_ids": ["binance-sandbox-001"],
      "contra_exchange_account_ids": ["bybit-001"],
      "risk_quantity": 0.005,
      "lead_mode": "anchor",
      "aggression": "neutral",
      "sweep_trigger_mode": "immediate",
      "pause_offset_pct": 2.0
    }
  }'
```

**Response Example**

```json theme={null}
{
  "isError": false,
  "message": "Algorithm started successfully.",
  "statusCode": 200,
  "data": {
    "order_id": "d4e5f6a7-b8c9-0123-def0-234567890123",
    "algorithm_type": "spread",
    "symbol": "BTC/USDT",
    "side": "buy",
    "total_quantity": 0.01,
    "status": "RUNNING",
    "arrival_price": 95000.00,
    "state": {
      "anchor_filled": 0.0,
      "contra_filled": 0.0,
      "current_spread_pct": 0.05,
      "unhedged_quantity": 0.0,
      "is_spread_paused": false
    }
  }
}
```

**Best Practices**

* Set `risk_quantity` conservatively - it limits max unhedged exposure
* Use `immediate` sweep trigger for tight risk management
* Set `pause_offset_pct` to stop trading when the spread widens beyond tolerance
* The algorithm completes when BOTH legs are fully filled
* Use different exchange accounts for each leg for cross-venue basis trades

***

### FundingArb Parameters

Delta-neutral funding-rate carry: go long spot and short the perpetual on the same underlying, collect the funding that longs pay shorts while the carry is favourable, and unwind when it no longer is. The two legs cancel each other's price exposure (delta-neutral by construction), so the P\&L is the funding collected minus fees and basis drift, not a directional bet. v1 supports positive funding only (long spot + short perp).

This is a long-running position algorithm, not a fill-and-finish execution algo. One algorithm represents one position: it waits for an entry signal, opens both legs, holds across funding windows, then unwinds and completes.

#### How the entry and exit signal works

Funding rates, especially on hourly-funding venues, oscillate around zero and flip sign constantly. Acting on a single funding print would open and close the position several times a day and bleed round-trip fees that dwarf the carry. The strategy is built to avoid that:

* **Smoothed signal.** Every entry and exit decision uses an exponentially-weighted moving average (EWMA) of the predicted funding APR, never the latest raw print. `funding_smoothing_hours` is the EWMA time-constant: larger values give a smoother, slower signal (less churn, more lag).
* **Asymmetric hysteresis band.** You enter when the smoothed APR is at or above `entry_apr_threshold`, and only exit (funding decay) when it falls below the lower `exit_apr_threshold`. The gap between the two is a deadband that stops the position flip-flopping when funding hovers near the entry level.
* **Fee-aware entry gate.** Before opening, the strategy estimates how long the current carry would take to cover the round-trip cost (`round_trip_cost_bps`). If that break-even time exceeds `max_breakeven_hours`, it refuses to open. On near-zero-funding markets this gate simply never fires, which is the correct behavior: the carry cannot beat the fees.
* **Minimum hold.** Once open, the funding-decay exit cannot fire until the position has been held for `min_hold_hours`, preventing a same-day round trip if funding dips right after entry. The hard exits below bypass it.

#### Parameters

**Legs and size**

| Parameter                  | Type   | Default         | Description                                                                                                |
| -------------------------- | ------ | --------------- | ---------------------------------------------------------------------------------------------------------- |
| `spot_symbol`              | string | required        | Spot leg symbol (the long leg). Must equal the top-level `symbol`. Always needs the full notional in cash. |
| `perp_symbol`              | string | required        | Perp leg symbol (the short leg), a derivative such as `BTC/USD:USD`. Same underlying as the spot leg.      |
| `position_quantity`        | number | required        | Per-leg position size N in base units. Also pass it as the top-level `quantity`.                           |
| `spot_exchange_account_id` | string | the one account | Account for the spot leg. Defaults to the single configured account.                                       |
| `perp_exchange_account_id` | string | the one account | Account for the perp leg. On Kraken the same account id serves both legs (one account, two CCXT classes).  |

**Entry / exit signal**

| Parameter                 | Type   | Default | Description                                                                                                                           |
| ------------------------- | ------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `entry_apr_threshold`     | number | `30`    | Enter when the smoothed predicted funding APR (percent, annualised) is at or above this.                                              |
| `exit_apr_threshold`      | number | `5`     | Exit (funding decay) when the smoothed APR falls below this. Must be below `entry_apr_threshold`; the gap is the hysteresis deadband. |
| `funding_smoothing_hours` | number | `8`     | EWMA time-constant applied to the predicted APR. Bigger = smoother and slower.                                                        |
| `max_entry_basis_bps`     | number | `15`    | Skip entry if the spot/perp basis is already wider than this (entering at a bad relative price).                                      |

**Fees and hold**

| Parameter             | Type   | Default  | Description                                                                                           |
| --------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
| `round_trip_cost_bps` | number | `30`     | Estimated all-in round-trip cost (both legs, in and out) used by the fee gate.                        |
| `max_breakeven_hours` | number | `96`     | Reject entry if the carry would take longer than this to cover the round-trip cost.                   |
| `min_hold_hours`      | number | `8`      | The funding-decay exit cannot fire before the position has been held this long. Hard exits bypass it. |
| `risk_quantity`       | number | 10% of N | Max unhedged delta allowed while opening or unwinding, before the hedge leg catches up.               |

**Perp leg risk**

| Parameter             | Type   | Default      | Description                                                                                                                                                                        |
| --------------------- | ------ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `leverage`            | number | `3`          | Perp leg leverage, set at entry. It sizes only the perp margin (notional / leverage) and the perp liquidation distance. It does NOT reduce the spot cash requirement. Keep it low. |
| `margin_mode`         | string | `"isolated"` | `isolated` (per-pair margin) or `cross` (shared futures-wallet margin), set at entry.                                                                                              |
| `stop_loss_basis_bps` | number | `80`         | Hard, instant unwind if the spot/perp basis blows out this far (the legs decoupled). Bypasses the minimum hold.                                                                    |

**Execution**

| Parameter                  | Type   | Default     | Description                                           |
| -------------------------- | ------ | ----------- | ----------------------------------------------------- |
| `aggression`               | string | `"neutral"` | Aggression for the maker spot leg.                    |
| `funding_check_interval_s` | number | `60`        | How often to refresh collected funding while holding. |

**Lifecycle phases** (in `state.phase`): `waiting_entry` then `opening` then `holding` then `unwinding` then `done`. The spot leg posts as a maker; each spot fill is hedged with a taker order on the perp leg. The algorithm unwinds (taker on both legs) on any exit trigger, or immediately if you cancel it.

**Exit triggers** (any one unwinds the position):

* **Funding decay** when the smoothed APR falls below `exit_apr_threshold` and `min_hold_hours` has elapsed (the main hysteresis exit).
* **Funding flip** when the smoothed APR is strongly adverse (below the negative of `entry_apr_threshold`). Instant, bypasses the minimum hold.
* **Basis blow-out** when the basis exceeds `stop_loss_basis_bps`. Instant, bypasses the minimum hold.
* **Kill switch** when you cancel the algorithm; both legs are flattened.

**Request Example (Kraken BTC carry)**

```bash theme={null}
curl -X POST "http://localhost:8082/oems/algorithms/start" \
  -H "Content-Type: application/json" \
  -d '{
    "algorithm_type": "funding_arb",
    "symbol": "BTC/USD",
    "side": "buy",
    "quantity": 0.01,
    "exchange_account_ids": ["kraken-001"],
    "params": {
      "spot_symbol": "BTC/USD",
      "perp_symbol": "BTC/USD:USD",
      "position_quantity": 0.01,
      "entry_apr_threshold": 30,
      "exit_apr_threshold": 5,
      "funding_smoothing_hours": 8,
      "max_entry_basis_bps": 15,
      "round_trip_cost_bps": 30,
      "max_breakeven_hours": 96,
      "min_hold_hours": 8,
      "leverage": 3,
      "margin_mode": "isolated",
      "stop_loss_basis_bps": 80
    }
  }'
```

`side` is always `buy` for v1 (long spot, short perp); it is fixed regardless of what you send.

**State (the position view)**

The `state` block on `GET /oems/algorithms/{order_id}` is the live position:

```json theme={null}
{
  "phase": "holding",
  "legs": {
    "spot": { "filled_quantity": 0.01, "avg_price": 100000.0 },
    "perp": { "filled_quantity": 0.01, "avg_price": 100010.0 },
    "unhedged_quantity": 0.0
  },
  "entry_basis_bps": 1.0,
  "entry_apr_pct": 42.0,
  "current_basis_bps": 1.2,
  "current_apr_pct": 38.5,
  "pnl": {
    "funding_collected": 3.20,
    "fees_paid": 0.85,
    "unrealized_basis_pnl": -0.10,
    "net_pnl": 2.25
  },
  "exit_reason": null
}
```

The two open legs and their entry prices live under `state.legs`. Each leg's child orders are the algorithm's slices grouped by `symbol` - fetch them from `GET /oems/algorithms/{order_id}/slices` (spot leg vs perp leg, with opposite sides). Profitability is the `state.pnl` block: funding collected, fees paid, mark-to-market basis drift, and the net. This strategy P\&L is distinct from the execution `tca` block (which measures slippage and fee savings, not carry).

<Note>
  **Funding accounting depends on the venue.** `funding_collected` only counts realized funding payments the venue reports per fee event; it is never estimated. Kraken Futures has no per-payment funding endpoint, so on Kraken `funding_collected` stays `0` and the net is carried by `unrealized_basis_pnl`. Binance, Bybit, and OKX report per-payment funding and populate it.
</Note>

**Best Practices**

* Pre-fund both wallets (spot cash for the long, perp margin for the short). v1 does not auto-transfer between wallets; it does pre-check balances and refuses to start if either leg is underfunded.
* Keep `leverage` low (2 to 3x). It sets the perp liquidation distance and does not change the spot cash you need.
* Leave a healthy gap between `entry_apr_threshold` and `exit_apr_threshold`. A narrow band re-introduces churn; a wide band holds through normal funding dips.
* Pick markets by their realized funding history, not the instantaneous prediction. On majors whose funding sits near zero, the fee gate will (correctly) refuse to open, so a one-off high print will not trigger a trade.
* Cancel the algorithm to flatten the position immediately (the kill switch).

***

### Aggression Levels

Applies to TWAP, Ladder, TimePace, and Spread algorithms.

| Level        | Description         | Use Case                                  |
| ------------ | ------------------- | ----------------------------------------- |
| `passive`    | 25% into spread     | Minimize market impact, patient execution |
| `neutral`    | Midpoint            | Balanced approach                         |
| `aggressive` | 75% into spread     | Prioritize fills over price               |
| `auto`       | Dynamic (TWAP only) | Adjusts based on fill progress            |

***

## GET /order-execution/algorithms/{order_id}

Get the status and progress of an algorithm.

### Path Parameters

| Parameter  | Type   | Description        |
| ---------- | ------ | ------------------ |
| `order_id` | string | Algorithm order ID |

### Request Example

```bash theme={null}
curl "https://api.renesis.fi/order-execution/algorithms/893dc6ba-3b13-407a-a06c-41dcdd39d1ee" \
  -H "X-API-Key: $API_KEY"
```

### Response Example

```json theme={null}
{
  "isError": false,
  "message": "Success",
  "statusCode": 200,
  "data": {
    "order_id": "893dc6ba-3b13-407a-a06c-41dcdd39d1ee",
    "algorithm_type": "twap",
    "symbol": "BTC/USDC",
    "side": "buy",
    "total_quantity": 0.001,
    "status": "RUNNING",
    "arrival_price": 78836.00,
    "progress": {
      "filled_quantity": 0.0006,
      "remaining_quantity": 0.0004,
      "fill_percentage": 60.0,
      "vwap": 78842.50,
      "child_orders_filled": 3,
      "child_orders_failed": 0,
      "total_fees": 0.000045
    },
    "state": {
      "current_interval": 4,
      "total_intervals": 6,
      "behind_by": 0.0
    },
    "recent_slices": [
      {
        "slice_id": "abc123",
        "status": "filled",
        "quantity": 0.0002,
        "fill_price": 78840.00,
        "arrival_price": 78836.00,
        "order_type": "limit",
        "slippage_bps": 0.51
      }
    ],
    "tca": {
      "vwap": 78842.50,
      "arrival_price": 78836.00,
      "arrival_spread_bps": 3.66,
      "implementation_shortfall_bps": 0.82,
      "filled_quantity": 0.0006,
      "notional": 47.31,
      "fills": { "maker_pct": 71.8, "taker_pct": 28.2, "maker_quantity": 0.00043, "taker_quantity": 0.00017 },
      "fees": { "total": 0.000045, "currency": "USDC", "effective_bps": 0.95 },
      "vs_market_order": {
        "taker_rate_bps": 7.5,
        "market_fee": 0.0000355,
        "fee_saved": 0.0000133,
        "fee_saved_pct": 37.4,
        "price_improvement_bps": 1.01,
        "price_improvement_value": 0.0000048,
        "total_saved": 0.0000181,
        "total_saved_bps": 3.83,
        "estimated": true,
        "currency": "USDC"
      }
    }
  }
}
```

### TCA Summary (`tca`)

Post-trade **Transaction Cost Analysis**, computed from the order's fills. It is
`null` until the order has at least one fill.

| Field                           | Meaning                                                                                                                                                                     |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vwap`                          | Volume-weighted average price actually achieved across all fills                                                                                                            |
| `arrival_price`                 | Benchmark mid-price captured the instant the algo started (the *decision price*)                                                                                            |
| `implementation_shortfall_bps`  | `(vwap − arrival) / arrival × 10000`. **Signed and side-agnostic**, negative means the average fill was *below* arrival (favourable for a buy, slightly adverse for a sell) |
| `fills.maker_pct` / `taker_pct` | Share of filled quantity that **rested** as a maker (`limit`) vs **crossed** as a taker (`market`/IOC). Higher maker % = lower fees                                         |
| `fees.total` / `effective_bps`  | Total fees paid and the blended fee rate in basis points                                                                                                                    |
| `vs_market_order`               | Estimated savings vs sending the whole order as a single market order                                                                                                       |

**`vs_market_order` breakdown**

| Field                             | Meaning                                                                          |
| --------------------------------- | -------------------------------------------------------------------------------- |
| `market_fee`                      | What an all-taker market order would have paid in fees (`taker_rate × notional`) |
| `fee_saved` / `fee_saved_pct`     | Fee saving from filling mostly as a maker                                        |
| `price_improvement_bps`           | Spread you avoided by resting passively instead of crossing the book             |
| `total_saved` / `total_saved_bps` | `fee_saved` + price improvement, the headline number                             |

<Note>
  **`vs_market_order` is an estimate** (`estimated: true`). The price-improvement
  leg assumes a market order crosses *half* the arrival spread and **ignores
  market impact**, so for large orders the real saving is **larger** than shown.
  It needs the venue's maker/taker fee rates; if those aren't available it falls
  back to the observed taker rate of the order's own market fills.
</Note>

<Tip>
  **Reading the sign:** because `implementation_shortfall_bps` isn't flipped by
  side, a value near zero means the algo tracked the arrival price almost exactly
  (excellent execution). For a **buy**, negative is favourable; for a **sell**,
  positive is favourable.
</Tip>

***

## POST /order-execution/algorithms/{order_id}/pause

Pause a running algorithm. Stops generating new slices but keeps open orders.

### Request Example

```bash theme={null}
curl -X POST "https://api.renesis.fi/order-execution/algorithms/893dc6ba-3b13-407a-a06c-41dcdd39d1ee/pause" \
  -H "X-API-Key: $API_KEY"
```

### Response Example

```json theme={null}
{
  "isError": false,
  "message": "Algorithm paused successfully.",
  "statusCode": 200,
  "data": {
    "order_id": "893dc6ba-3b13-407a-a06c-41dcdd39d1ee",
    "status": "PAUSED",
    "paused_at": "2026-02-02T23:45:00Z"
  }
}
```

***

## POST /order-execution/algorithms/{order_id}/resume

Resume a paused algorithm.

### Request Example

```bash theme={null}
curl -X POST "https://api.renesis.fi/order-execution/algorithms/893dc6ba-3b13-407a-a06c-41dcdd39d1ee/resume" \
  -H "X-API-Key: $API_KEY"
```

### Response Example

```json theme={null}
{
  "isError": false,
  "message": "Algorithm resumed successfully.",
  "statusCode": 200,
  "data": {
    "order_id": "893dc6ba-3b13-407a-a06c-41dcdd39d1ee",
    "status": "RUNNING"
  }
}
```

***

## POST /order-execution/algorithms/{order_id}/cancel

Cancel an algorithm. Cancels all open child orders.

### Request Example

```bash theme={null}
curl -X POST "https://api.renesis.fi/order-execution/algorithms/893dc6ba-3b13-407a-a06c-41dcdd39d1ee/cancel" \
  -H "X-API-Key: $API_KEY"
```

### Response Example

```json theme={null}
{
  "isError": false,
  "message": "Algorithm cancelled successfully.",
  "statusCode": 200,
  "data": {
    "order_id": "893dc6ba-3b13-407a-a06c-41dcdd39d1ee",
    "status": "CANCELLED",
    "cancelled_at": "2026-02-02T23:47:00Z",
    "child_orders_cancelled": 2,
    "final_progress": {
      "filled_quantity": 0.0008,
      "fill_percentage": 80.0,
      "vwap": 78855.46,
      "total_fees": 0.000058
    }
  }
}
```

***

## GET /order-execution/algorithms/{order_id}/slices

Get all child orders (slices) for an algorithm.

### Request Example

```bash theme={null}
curl "https://api.renesis.fi/order-execution/algorithms/893dc6ba-3b13-407a-a06c-41dcdd39d1ee/slices" \
  -H "X-API-Key: $API_KEY"
```

### Response Example

```json theme={null}
{
  "isError": false,
  "message": "Success",
  "statusCode": 200,
  "data": {
    "order_id": "893dc6ba-3b13-407a-a06c-41dcdd39d1ee",
    "slices": [
      {
        "slice_id": "slice-001",
        "interval_number": 1,
        "status": "filled",
        "quantity": 0.0002,
        "filled_quantity": 0.0002,
        "price": 78830.00,
        "fill_price": 78832.50,
        "symbol": "BTC/USDT",
        "side": "buy",
        "arrival_price": 78836.00,
        "slippage_bps": -0.44,
        "fees": 0.000015,
        "fee_currency": "BNB",
        "submitted_at": "2026-02-02T23:42:10Z",
        "filled_at": "2026-02-02T23:42:10Z",
        "venue": "binance"
      }
    ]
  }
}
```

Each slice carries its own `symbol` and `side`. For single-symbol
algorithms these match the parent order (or are `null` on older records).
Multi-leg algorithms (Spread) emit slices on different symbols and
opposite sides: the anchor leg trades `anchor_symbol` on the parent side,
the contra leg trades `contra_symbol` on the opposite side as a hedge.
`arrival_price` always refers to the slice's own market and may be `null`
when no snapshot for that market was available at submission.

***

## GET /order-execution/algorithms

List all algorithms.

### Query Parameters

| Parameter        | Type   | Description                                                               |
| ---------------- | ------ | ------------------------------------------------------------------------- |
| `status`         | string | Filter by status: `RUNNING`, `PAUSED`, `COMPLETED`, `CANCELLED`, `FAILED` |
| `algorithm_type` | string | Filter by type: `twap`, `ladder`, `sweep`, `timepace`, `spread`           |
| `symbol`         | string | Filter by trading pair                                                    |
| `side`           | string | Filter by `buy` or `sell`                                                 |
| `limit`          | int    | Max results (default: 50, max: 200)                                       |
| `offset`         | int    | Pagination offset                                                         |

### Request Example

```bash theme={null}
# List all
curl "https://api.renesis.fi/order-execution/algorithms" \
  -H "X-API-Key: $API_KEY"

# List running only
curl "https://api.renesis.fi/order-execution/algorithms?status=RUNNING" \
  -H "X-API-Key: $API_KEY"

# List ladder algorithms
curl "https://api.renesis.fi/order-execution/algorithms?algorithm_type=ladder" \
  -H "X-API-Key: $API_KEY"
```

***

## GET /order-execution/algorithms/by-status/{status}

List algorithms by status.

### Request Example

```bash theme={null}
curl "https://api.renesis.fi/order-execution/algorithms/by-status/RUNNING" \
  -H "X-API-Key: $API_KEY"
curl "https://api.renesis.fi/order-execution/algorithms/by-status/COMPLETED" \
  -H "X-API-Key: $API_KEY"
```

***

## GET /order-execution/algorithms/by-account/{exchange_account_id}

List algorithms for a specific exchange account.

### Request Example

```bash theme={null}
curl "https://api.renesis.fi/order-execution/algorithms/by-account/binance-001" \
  -H "X-API-Key: $API_KEY"
```

***

## Algorithm Status Lifecycle

```
PENDING -> RUNNING -> COMPLETED
               |
            PAUSED -> RUNNING (resume)
               |
           CANCELLED
               |
            FAILED
```

| Status      | Description                 |
| ----------- | --------------------------- |
| `PENDING`   | Created but not started     |
| `RUNNING`   | Actively executing slices   |
| `PAUSED`    | Temporarily stopped by user |
| `COMPLETED` | All quantity filled         |
| `CANCELLED` | Stopped by user             |
| `FAILED`    | Error occurred              |

***

## TCA Metrics (Transaction Cost Analysis)

Each algorithm tracks execution quality metrics:

| Metric            | Description                                              |
| ----------------- | -------------------------------------------------------- |
| `arrival_price`   | Market mid-price when algorithm started                  |
| `vwap`            | Volume-weighted average fill price                       |
| `slippage_bps`    | Difference between arrival and fill price (basis points) |
| `fill_percentage` | Percentage of order filled                               |
| `total_fees`      | Total trading fees incurred                              |

### Calculating Implementation Shortfall

```
Implementation Shortfall = (VWAP - Arrival Price) / Arrival Price * 10000 bps
```

Positive = paid more than arrival (for buys), negative = got better price.

***

## Error Handling

| Error                  | Cause                          | Resolution                                |
| ---------------------- | ------------------------------ | ----------------------------------------- |
| `DAILY_LIMIT_EXCEEDED` | Order would exceed daily limit | Reduce quantity or wait until tomorrow    |
| `MIN_ORDER_SIZE`       | Slice below exchange minimum   | Algorithm skips tiny slices automatically |
| `SYMBOL_NOT_PERMITTED` | Account can't trade this pair  | Check exchange account permissions        |
| `EXCHANGE_ERROR`       | Exchange rejected order        | Check error message for details           |

### Daily Limit Behavior

When an algorithm hits the daily trading limit:

* Remaining slices are rejected
* Algorithm is paused automatically
* Check `/order-execution/execution/daily-limit` for current limit status

***

## Choosing an Algorithm

| Scenario                            | Algorithm  | Why                                                |
| ----------------------------------- | ---------- | -------------------------------------------------- |
| Minimize market impact over time    | `twap`     | Spreads execution evenly, catches up if behind     |
| Fixed-pace execution, no catch-up   | `timepace` | Predictable pacing, skipped intervals stay skipped |
| Fill entire order NOW               | `sweep`    | IOC execution with retry logic                     |
| Accumulate at multiple price levels | `ladder`   | Posts limit orders across a price range            |
| Basis/arb trade across venues       | `spread`   | Two-leg execution with risk management             |

### TWAP vs TimePace

| Feature                      | TWAP                            | TimePace                        |
| ---------------------------- | ------------------------------- | ------------------------------- |
| Catch-up on missed intervals | Yes                             | No                              |
| Fixed end time               | Yes (duration-based)            | No (runs until filled)          |
| Size per interval            | Calculated from total/intervals | User-specified                  |
| Use case                     | Fill X quantity in Y time       | Send Z quantity every N seconds |
