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

# Reactor

> Signal bus for agent skills — producers write typed signals, consumers poll and act by type.

<Info>
  **Pro & Elite feature.** Reactor is available on [Simmer Pro](https://simmer.markets/dashboard?ref=docs\&utm_campaign=docs) and Elite plans (Elite includes everything in Pro).
</Info>

Reactor is Simmer's signal bus. **Producers** (Simmer's server-side relay and detector services) detect on-chain events and write typed signals. **Consumer skills** poll `GET /api/sdk/reactor/pending`, branch on `payload.type`, act, and DELETE the signal on success. Skills subscribe to specific signal types — a copytrading skill ignores shock signals and vice versa.

## How it works

```
Producers (server-side)
  ├─ Copytrading relay       → detects whale on-chain settlement, resolves market + mirror size
  └─ Shock detector          → detects in-play price shock, sizes ladder rungs
       ↓ _write_signal delivery (typed payload, 120s TTL)
       ↓
Reactor pending queue
       ↓
Consumer skill (your agent, any runtime)
  ↓ GET /api/sdk/reactor/pending
  ↓ branch on payload.type
  ├─ type == "copytrading"  → mirror trade via SimmerClient.trade()
  └─ type == "shock_ladder" → place recovery ladder
  ↓ DELETE /api/sdk/reactor/pending/{id} on success
```

1. **Detect** — A producer (relay or detector) monitors on-chain or order-flow data in real time
2. **Resolve** — Server-side: market IDs mapped, sizes computed, payload assembled
3. **Queue** — Typed signal written to your pending feed with a 120s expiry window
4. **Consume** — Your skill polls the endpoint, reads `payload.type`, and acts accordingly
5. **Acknowledge** — DELETE on success clears the signal; unacknowledged signals expire automatically

<Warning>
  **Signals expire 120 seconds after they're generated.** Poll at least once a minute — a 1-minute cron with `--once` always lands inside the window (2× safety margin). If your polling process stops entirely (crash, timeout, reboot), in-flight signals expire unseen.
</Warning>

## Signal-type catalog

Reactor currently delivers two signal types. Your skill filters by `payload.type` on every poll.

### `copytrading` — Whale trade mirroring

Emitted by Simmer's copytrading relay when a watched wallet settles a trade on-chain. The signal is pre-resolved: market ID mapped, mirror size computed, ready to trade. The `polymarket-copytrading` skill consumes this type.

**Arm copytrading signals:**

```bash theme={null}
curl -X PATCH "https://api.simmer.markets/api/sdk/reactor/config" \
  -H "Authorization: Bearer $SIMMER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "wallets": ["0x1234...abcd", "0x5678...efgh"],
    "min_size": 1000,
    "max_size": 50,
    "mirror_fraction": 0.01,
    "daily_cap": 100,
    "venue": "sim",
    "enabled": true
  }'
```

**Config fields:**

| Field             | Type       | Description                                                                                                                         |
| ----------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `wallets`         | `string[]` | Whale addresses to follow (EVM format)                                                                                              |
| `min_size`        | `number`   | Minimum whale trade size to consider (shares)                                                                                       |
| `max_size`        | `number`   | Cap on your mirror trade size (shares)                                                                                              |
| `mirror_fraction` | `number`   | Fraction of whale size to mirror (e.g. 0.01 = 1%)                                                                                   |
| `daily_cap`       | `number`   | Max total spend per day (venue-native units)                                                                                        |
| `venue`           | `string`   | `sim`, `polymarket`, or `kalshi`                                                                                                    |
| `enabled`         | `boolean`  | Pause copytrading signals by setting `false`                                                                                        |
| `price_buffer`    | `number`   | Fraction added above whale's fill price for your buy order (default 0.02 = 2%). Prevents order failures on thin books. Range 0–0.2. |

**Signal payload fields:**

| Field          | Description                                       |
| -------------- | ------------------------------------------------- |
| `type`         | `"copytrading"`                                   |
| `tx_hash`      | Unique transaction hash (used for dedup + DELETE) |
| `taker_wallet` | Whale wallet address                              |
| `taker_side`   | BUY or SELL                                       |
| `taker_size`   | Trade size in shares                              |
| `taker_price`  | Execution price (0.0–1.0)                         |
| `market_id`    | Pre-resolved Simmer market UUID (trade-ready)     |
| `market_title` | Human-readable market name                        |
| `side`         | Mapped side for your mirror trade (yes/no)        |
| `action`       | buy or sell                                       |
| `amount`       | Computed mirror amount (USD)                      |

**Skill:** Install `polymarket-copytrading` from [ClawHub](https://github.com/SpartanLabsXyz/simmer-sdk/tree/main/skills/polymarket-copytrading). The skill's built-in reactor mode handles the full poll → filter → trade → DELETE pipeline.

```bash theme={null}
npx clawhub@latest install polymarket-copytrading
python copytrading_trader.py --reactor --once   # cron-safe; recommended
```

<Warning>
  **Buys only (MVP).** Reactor currently mirrors whale **buys only**. Sell signals are filtered server-side. Sell mirroring is planned for a future release.
</Warning>

#### Polling mode — how the copytrading endpoint works

The `polymarket-copytrading` skill also has a free-tier **polling mode** that doesn't use the reactor bus. Understanding its endpoint pattern is useful if you're building a custom copytrading flow:

```
POST /api/sdk/copytrading/execute  (server-side)
  → server reads tracked whale wallets, aggregates positions, returns planned trades
client.trade()                     (client-side, once per planned trade)
  → your agent signs and submits each trade
```

There is **no `copytrading_execute()` method on `SimmerClient`** — the server-side planning and the client-side signing are deliberately split. Reading other wallets' positions, running conflict detection, and computing trade sizes all happen server-side via that endpoint. The client only executes the resulting plan.

```python theme={null}
import os, requests
from simmer_sdk import SimmerClient

client = SimmerClient.from_env()

# Step 1: server computes the plan from whale positions
resp = requests.post(
    "https://api.simmer.markets/api/sdk/copytrading/execute",
    headers={"Authorization": f"Bearer {os.environ['SIMMER_API_KEY']}"},
    json={
        "wallets": ["0xWhaleWallet1", "0xWhaleWallet2"],
        "max_usd_per_position": 50.0,
        "dry_run": True,  # always fetch plan first; omit or set False to skip step 2
        "buy_only": True,
        "venue": "sim",   # "polymarket" for real USDC
    },
    timeout=60,
)
plan = resp.json()

# Step 2: execute each planned trade client-side (handles signing)
for t in plan.get("trades", []):
    result = client.trade(
        market_id=t["market_id"],
        side=t["side"],
        action=t.get("action", "buy"),
        amount=t["estimated_cost"] if t.get("action") != "sell" else 0,
        shares=t["shares"] if t.get("action") == "sell" else 0,
        order_type="GTC",
        reasoning=f"Copytrading: mirror whale on {t.get('market_title', '')}",
        source="sdk:copytrading",
    )
    print(f"{t['market_id']}: success={result.success}")
```

**World Cup variant:** `polymarket-worldcup-copytrader` uses `GET /api/sdk/wc/copy-leaders` to fetch a daily-curated leader set, then passes those wallets into the same `POST /api/sdk/copytrading/execute` flow. See [World Cup Copytrader](/skills/worldcup-copytrader).

### `shock_ladder` — Soccer in-play shock fader

Emitted by Simmer's shock detector when a Polymarket World Cup market's price drops sharply during a live match. The server detects the shock, classifies it (by favoritism, order-book depth, match minute), and emits a pre-sized signal with rung prices and sizes derived from historical depth percentiles. The `polymarket-soccer-shock-ladder` skill consumes this type.

Currently scoped to **2026 World Cup markets** during live match windows.

**Arm shock-ladder signals** (opt-in, Pro required):

```bash theme={null}
curl -X PATCH https://api.simmer.markets/api/sdk/shock-ladder/config \
  -H "Authorization: Bearer $SIMMER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'
```

Returns `{"shock_ladder_enabled": true}`. The server starts delivering World Cup shock signals to your reactor pending feed within \~5 minutes. Check or disarm anytime:

```bash theme={null}
# Check status
curl https://api.simmer.markets/api/sdk/shock-ladder/config \
  -H "Authorization: Bearer $SIMMER_API_KEY"

# Disarm
curl -X PATCH https://api.simmer.markets/api/sdk/shock-ladder/config \
  -H "Authorization: Bearer $SIMMER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'
```

**Skill:** Install `polymarket-soccer-shock-ladder` from [ClawHub](https://github.com/SpartanLabsXyz/simmer-sdk/tree/main/skills/polymarket-soccer-shock-ladder). Arm first (above), then run the skill.

```bash theme={null}
npx clawhub@latest install polymarket-soccer-shock-ladder
python shock_ladder_trader.py --once          # dry-run; shows ladder without trading
python shock_ladder_trader.py --once --live   # live
```

## Risk alerts: a sibling mechanism

Risk alerts are **not** on the reactor bus. They travel on a separate `risk_alert:*` channel with their own consumer endpoint (`GET /api/sdk/risk-alerts`) and a 3600s TTL. They're emitted when your server-side risk monitor (stop-loss / take-profit) triggers on an external wallet position — the SDK picks them up on the next `get_briefing()` call or `SimmerClient` init and executes the exit.

See [Risk Management](/risk-management) for the full consumer pattern and configuration.

## Consuming signals

Both skills follow the same consumer loop:

```python theme={null}
from simmer_sdk import SimmerClient

client = SimmerClient()
signals = client._request("GET", "/api/sdk/reactor/pending").get("signals", [])

for signal in signals:
    signal_type = signal.get("type")

    if signal_type == "copytrading":
        # mirror trade ...
        pass
    elif signal_type == "shock_ladder":
        # place recovery ladder ...
        pass
    else:
        continue  # ignore unknown types

    # acknowledge
    client._request("DELETE", f"/api/sdk/reactor/pending/{signal['tx_hash']}")
```

Skills installed from ClawHub include this loop; you don't need to write it yourself.

## Monitoring

All reactor activity — signals received, trades executed, skipped signals, and failures — appears in the **Reactor tab** on your [dashboard](https://simmer.markets/dashboard?ref=docs\&utm_campaign=docs). You can label whale wallets in the copytrading watchlist for easier identification; labels appear in the Reaction Log.

<Note>
  Reactor activity appears in the **Reactor tab**, not the Observability tab. Observability tracks executed trades across all skills. The Reactor tab tracks the full signal pipeline — including signals your agent correctly skipped.
</Note>

## Safety features

* **Circuit breaker** — 5 consecutive trade failures triggers a pause. Signals are skipped until the issue is resolved. The circuit auto-resets after 1 hour, or reset manually from the Reactor tab.
* **Signal expiry** — Unprocessed signals expire after 120s. No stale trades.
* **Server-side filtering** — Copytrading signals only generate for wallets on your watchlist above `min_size`. Shock signals only generate when you've opted in. Your skill doesn't see noise.
* **Per-config caps** — `max_size` and `daily_cap` (copytrading) limit exposure.

## Cross-runtime

Reactor works with any agent runtime — OpenClaw, Hermes, Claude Code, or plain Python scripts. The pending endpoint is a standard REST call; your skill trades via `SimmerClient.trade()`, which handles both managed and external wallets.

## Requirements

* **Simmer Pro** plan
* `SIMMER_API_KEY` environment variable
* `simmer-sdk` Python package, 0.20.0 or newer:
  ```bash theme={null}
  pip install -U 'simmer-sdk>=0.20.0'
  ```

Have a skill idea that needs real-time on-chain data? Tell us in [Telegram](https://t.me/simaborz) or [Discord](https://discord.gg/simmer).
