# TWAP (/strategies/twap)



TWAP, or Time-Weighted Average Price, divides one parent order into smaller child orders and submits them at a fixed cadence. It is an execution strategy: you choose the side and amount; TWAP controls the schedule.

Market Lab's current TWAP implementation executes on BULK, Hyperliquid core and HIP-3 perpetuals, or HyperLink. It uses market child orders.

> TWAP can place real orders. Market child orders prioritize execution and can experience slippage. Always inspect the dry-run plan first.

## Preview a TWAP [#preview-a-twap]

```bash
mlab strategy run twap BTC \
  --venue bulkf \
  --side buy \
  --margin 100 \
  --leverage 10 \
  --duration 300 \
  --interval 60 \
  --dry-run
```

A dry run:

* obtains the current reference price from the selected venue
* multiplies `--margin` by `--leverage` to create the target exposure
* converts that exposure into a lot-aligned total size
* calculates the number and size of child orders
* validates every child against the market's minimum notional
* prints the complete strategy plan
* does not create a job or submit an order

The estimated margin and exposure can be slightly lower than requested because size is floored to the market's lot size.

## Scheduling Logic [#scheduling-logic]

The child count is:

```txt
ceil(duration / interval)
```

The normalized total size is divided across that many children. Distribution is performed in whole market lots, and any remainder lots are distributed without losing or exceeding the parent size.

For this configuration:

```txt
duration = 300 seconds
interval = 60 seconds
```

TWAP creates five child orders scheduled at approximately:

```txt
0s, 60s, 120s, 180s, 240s
```

The first child is submitted immediately. The remaining children are submitted once per interval. Together, the five scheduled buckets cover the 300-second execution window.

`--duration` defines how long the parent execution is spread across. `--interval` defines how frequently a child is submitted. An interval alone cannot determine when the parent order should finish.

## Submit a Live Job [#submit-a-live-job]

Remove `--dry-run` to submit the strategy:

```bash
mlab strategy run twap BTC \
  --venue bulkf \
  --side buy \
  --margin 100 \
  --leverage 10 \
  --duration 300 \
  --interval 60
```

Terminal mode prints the normalized plan and asks once for confirmation. After confirmation, `mlab` submits the immutable TWAP definition to `mlabd`, prints the job ID, and returns immediately. Closing the terminal does not stop the strategy.

Use `--yes` for non-interactive execution:

```bash
mlab strategy run twap BTC \
  --venue bulkf \
  --side sell \
  --size 0.01 \
  --duration 600 \
  --interval 30 \
  --yes \
  --output json
```

Structured live output requires `--yes` because JSON and JSONL modes cannot display an interactive confirmation prompt.

## Arguments [#arguments]

| Argument        | Required   | Meaning                                                      |
| --------------- | ---------- | ------------------------------------------------------------ |
| `SYMBOL`        | Yes        | Market Lab symbol such as `BTC` or `xyz:TSLA`                |
| `--venue`       | No         | `bulkf`, `hyperliquidf`, or `hyperlinkf`                     |
| `--testnet`     | No         | Use BULK or Hyperliquid testnet; not valid with `hyperlinkf` |
| `--side`        | Yes        | Parent side: `buy` or `sell`                                 |
| `--size`        | One amount | Total base-asset size                                        |
| `--margin`      | One amount | Total quote collateral; multiplied by leverage at deployment |
| `--duration`    | Yes        | Total execution window in seconds                            |
| `--interval`    | No         | Seconds between child orders; defaults to `60`               |
| `--leverage`    | No         | Leverage applied to each child; defaults to `1`              |
| `--reduce-only` | No         | Marks every child as reduce-only                             |
| `--dry-run`     | No         | Print the plan without creating a job                        |
| `--yes`         | No         | Skip the live terminal confirmation                          |
| `--output`      | No         | `terminal`, `json`, or `jsonl`                               |

Set exactly one of `--size` or `--margin`.

When `--margin` is used, Market Lab multiplies it by leverage and resolves the resulting exposure to a fixed, lot-aligned parent size before the job is created. Later price changes affect the realized exposure of each market child, but they do not change the persisted target size.

## Monitor the Job [#monitor-the-job]

The submission response includes a strategy job ID:

```txt
strategy_...
```

Use it to manage the detached worker:

```bash
mlab strategy jobs
mlab strategy status <JOB_ID>
mlab strategy logs <JOB_ID> --follow
mlab strategy stop <JOB_ID>
```

`strategy logs` contains:

* the normalized TWAP plan used by the worker
* one `strategy.child_order` record per submitted child
* the venue order ID and submission status
* a final `strategy.run.finished` summary
* a `strategy.run.failed` record when execution cannot continue

Job statuses are `starting`, `running`, `stopping`, `stopped`, `completed`, and `failed`.

## Stop Behavior [#stop-behavior]

Stopping a TWAP prevents future child orders. It does not reverse filled children or close the resulting position.

```bash
mlab strategy stop <JOB_ID>
```

Every child submission includes its job ID and sequence. `mlabd` atomically verifies that the job is still active, that children arrive in order, and that each child size matches the persisted schedule. A child submitted after the job has stopped is rejected.

TWAP has no restart command. If a stopped job filled only part of the parent amount, inspect the job logs and account fills, calculate the desired remainder, and submit a new TWAP explicitly.

## Market and Limit Execution [#market-and-limit-execution]

TWAP itself does not require market orders; a TWAP trajectory can use either market or limit children. Market Lab currently implements only market-child TWAP.

The following are not currently accepted by `strategy run twap`:

* `--type limit`
* `--price`
* `--tif`

Limit TWAP requires additional lifecycle rules for resting orders, partial fills, cancellation, and the unfilled amount at expiration. Those semantics will be documented when limit-child TWAP is implemented. Market Lab will not silently treat a requested limit price as a market order.

## Failure Behavior [#failure-behavior]

The job stops and becomes `failed` when a child cannot be planned or submitted. Previously filled children remain filled; Market Lab does not roll them back.

Common validation failures include:

* total size is not aligned with the selected market's lot size
* the schedule creates more children than available lots
* an individual child is below the market minimum notional
* requested leverage exceeds the market maximum
* authentication is missing or no longer matches the selected venue account

Inspect both the job and its logs after a failure:

```bash
mlab strategy status <JOB_ID>
mlab strategy logs <JOB_ID>
mlab fills --symbol BTC
```
