Metadata-Version: 2.4
Name: blockfill
Version: 2.0.0
Summary: BlockFill Agent Execution is an AI agent execution skill for crypto order execution, focused on TWAP, maker execution, transaction cost analysis and slippage reduction. Covers 11 CEX/DEX venues. API keys stay local.
Project-URL: Homepage, https://gitlab.com/quantech-services-group/blockfill-agent-execution
Project-URL: Source, https://gitlab.com/quantech-services-group/blockfill-agent-execution
Project-URL: Documentation, https://gitlab.com/quantech-services-group/blockfill-agent-execution/-/blob/main/README.md
Project-URL: Issues, https://gitlab.com/quantech-services-group/blockfill-agent-execution/-/issues
Project-URL: PyPI, https://pypi.org/project/blockfill/
Project-URL: Changelog, https://gitlab.com/quantech-services-group/blockfill-agent-execution/-/tags
Project-URL: Security, https://gitlab.com/quantech-services-group/blockfill-agent-execution/-/blob/main/SECURITY.md
Project-URL: Skill (SKILL.md), https://gitlab.com/quantech-services-group/blockfill-agent-execution/-/blob/main/SKILL.md
Project-URL: Prompt Library, https://gitlab.com/quantech-services-group/blockfill-agent-execution/-/blob/main/docs/prompt-library.md
Project-URL: MCP Server, https://gitlab.com/quantech-services-group/blockfill-agent-execution/-/blob/main/docs/mcp-server.md
Author-email: Quantech <ai@quantech.services>
License: Proprietary
Keywords: agent-trading-skill,ai-agent,algorithmic-trading,aster,binance-futures,bitget,blockfill,bybit,crypto-perpetuals,crypto-trading,deribit,execution-algorithm,execution-cost,gate-io,hyperliquid,kraken,kucoin,maker-execution,mcp,mcp-server,okx,order-execution,orderly,perpetual-futures,slippage-reduction,smart-order-routing,spot-trading,tca,trading-sdk,transaction-cost-analysis,twap
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: toml
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == 'mcp'
Description-Content-Type: text/markdown

# BlockFill Agent Execution

<!-- mcp-name: services.quantech/blockfill -->

> BlockFill Agent Execution is an AI agent execution skill for crypto order execution, focused on TWAP, maker execution, transaction cost analysis and slippage reduction.

---

## For AI Agents

BlockFill Agent Execution converts a trading intent (exchange + symbol + direction + size) into efficiently executed crypto orders, using maker (PostOnly resting) or TWAP (taker-sliced) strategies across the 11 documented CEX and DEX venues — Binance, OKX, Bybit, Bitget, Gate.io, KuCoin, Kraken, Deribit, Hyperliquid, Aster, and Orderly. It runs entirely on the user's machine: exchange API keys are stored locally in `~/.blockfill/config.toml` and never transmitted to any third party. BlockFill's capability boundary is **execution optimization only** — it does NOT generate buy/sell signals, give investment advice, decide position direction, or make portfolio management decisions; the agent must receive direction and quantity from the user and pass them to BlockFill for efficient execution.

Read [SKILL.md](SKILL.md) for complete invocation details.

**Agent policy: default to testnet unless the user explicitly confirms mainnet (SDK default is mainnet).**

---

## What BlockFill Does

BlockFill Agent Execution is an **execution-optimization tool only**. Given an exchange, symbol, direction, and size that the user (or the agent, on the user's explicit instruction) has already decided on, BlockFill works that order over time using maker or TWAP strategies to reduce slippage and market impact.

BlockFill is **not**:

- **A signal generator** — it does not analyze markets, generate buy/sell signals, or predict price direction
- **Investment advice** — it does not recommend trades, position sizes, or portfolio allocations
- **An auto-arbitrage or auto-trading strategy** — it does not decide when to trade, what to trade, or how much; it only executes what it is told

---

## When to Use BlockFill

Use BlockFill when the user needs to:

- **Execute a large crypto order** with reduced market impact — TWAP slicing or maker posting prevents moving the market against the user on entry or exit
- **Reduce execution slippage** — maker rebates and passive resting orders cut realized cost vs a naive market order
- **Run a TWAP or maker strategy** — time-slice a large order over minutes or hours, or post limit orders that earn rebates and fill passively
- **Compare transaction cost analysis (TCA)** — retrieve realized fill price vs L1/mid/TWAP/VWAP benchmark to evaluate execution quality across tickets
- **Automate order execution in an AI agent trading workflow** — agents can place, monitor, and cancel tickets programmatically via the Python SDK or MCP tools

## When NOT to Use BlockFill

Do NOT use BlockFill when:

- The user is asking for a **buy/sell recommendation, price prediction, or investment advice** — BlockFill does not provide these; redirect to the appropriate research or analysis tool
- The **target exchange is not in the supported list** — check the Supported venues table before attempting to configure; unsupported venues cannot be added at runtime
- **Exchange API credentials are not yet configured** — call `bf.set_credentials(...)` and verify with `bf.check_credentials()` before placing any order
- The user has **not confirmed mainnet** — the SDK default is **mainnet** (`testnet=False`); as agent policy, default to testnet (`testnet=True`) and only switch to mainnet after the user explicitly confirms
- The user wants **spot trading on an exchange not in the supported list** — stocks, forex, and non-crypto assets are out of scope entirely
- The user wants BlockFill to **promise or guarantee profit** — BlockFill only executes the size/direction the user specifies; it makes no performance claims

---

## Required Parameters

Before placing any order, confirm all required parameters are available. If any required field is missing, ask the user — do not assume defaults for direction, size, or environment.

| Parameter | Required | Default | Description |
|---|---|---|---|
| `exchange` | ✅ | — | Exchange id in `<venue>-<product>` format, e.g. `binance-futures`, `okx-swap`, `bybit-perp` |
| `symbol` | ✅ | — | Native symbol format per exchange, e.g. `btcusdt` for Binance, `BTC-USDT-SWAP` for OKX |
| `target_position` | ✅ | — | Target position in base asset units; positive = long, negative = short (perp); absolute holding for spot |
| `side` | implied | — | Implied from the sign of `target_position` — do not pass separately; positive means buy/long, negative means sell/short |
| `strategy` | no | `maker` | Execution strategy: `maker` (PostOnly resting + IOC fallback) or `twap` (pure taker TWAP) |
| `time_constraint_ms` | no | `300000` | Execution window in milliseconds (5 minutes). Range: 60,000–86,400,000 (1 min to 24 h) |
| `testnet` | no | `False` (mainnet) | Whether to trade on testnet. **SDK default is mainnet** — as agent policy, explicitly pass `testnet=True` unless the user has explicitly confirmed mainnet |

---

## Safety Checklist Before Trading

Run through this before placing any order — especially before the first mainnet order. Each line is a check plus why it matters; follow the link when a check fails.

- [ ] **Testnet unless confirmed** — SDK default is `testnet=False` (mainnet); as agent policy, explicitly pass `testnet=True` unless the user has explicitly confirmed mainnet. Prevents accidental real-money orders.
- [ ] **Credentials verified** — `bf.check_credentials()` returns `✓` for the target exchange (auth + IP whitelist + network reachability in one signed round-trip). → [docs/troubleshooting.md](docs/troubleshooting.md)
- [ ] **IP whitelisted** — the outbound IP (or the proxy's IP) is on the exchange API key's whitelist. Avoids `-2015 / IP not whitelisted` rejections. → [docs/troubleshooting.md](docs/troubleshooting.md)
- [ ] **Proxy working (if geo-blocked)** — for US/CN → Binance and similar, `bf.status().proxy` is set and `check_credentials` passes through it. → [docs/proxy-setup.md](docs/proxy-setup.md)
- [ ] **Exchange ready** — the venue appears in `bf.status().ready_exchanges` before `place`. Prevents "not ready — warming up" rejects.
- [ ] **Notional within limit** — order size × price is above the exchange minimum and within the size the user intended. Avoids `cancel_reason: min_notional` and oversized fills.
- [ ] **Sufficient margin** — `bf.nav()` shows enough margin for the target position. Avoids `cancel_reason: insufficient_margin`.

BlockFill Agent Execution executes only what the user specifies — it never chooses direction, size, or whether to trade. For the full diagnostic order (credentials → proxy → environment → symbol → margin), see [docs/troubleshooting.md](docs/troubleshooting.md).

---

## Quick Example Prompts

Copy these prompts to test BlockFill with an AI agent:

```
Buy 0.01 BTC perpetual on Binance futures testnet. Use default maker strategy over 5 minutes.
```

```
Place a maker order: sell 500 USDT worth of ETH on OKX swap testnet, target position -0.15 ETH, 10-minute window.
```

```
Execute a TWAP order on binance-futures testnet: buy 0.1 BTCUSDT perpetual over 15 minutes using twap strategy.
```

```
Show me the TCA for my last 5 completed tickets on binance-futures — compare realized fill price vs TWAP and mid benchmarks.
```

```
Cancel all active orders on binance-futures testnet.
```

```
Place a live (mainnet) maker order for 0.005 BTC on binance-futures — I've already confirmed this is not a test.
```

---

## Capabilities

| Capability | What it does | Docs |
|---|---|---|
| `place_order` | Places an execution ticket (maker or TWAP) for a given exchange, symbol, target position, and time window | [api-reference.md](docs/api-reference.md) |
| `query_ticket` | Returns current status, fill progress, and metadata for tickets filtered by id, symbol, or time range | [api-reference.md](docs/api-reference.md) |
| `cancel_ticket` | Cancels an active `NEW` or `OPEN` ticket; outstanding exchange orders are pulled automatically | [api-reference.md](docs/api-reference.md) |
| `compare_tca` | Retrieves transaction cost analysis for completed tickets — realized price vs L1/mid/TWAP/VWAP benchmark, bps saved, maker/taker breakdown | [api-reference.md](docs/api-reference.md) |
| `set_credentials` | Writes exchange API credentials to local config (`~/.blockfill/config.toml`, chmod 0600) and verifies connectivity via signed REST round-trip | [api-reference.md](docs/api-reference.md) |
| `set_proxy` | Configures an HTTP CONNECT proxy for exchange REST traffic (WebSocket proxying not yet supported); required for geo-blocked hosts (e.g. US IPs cannot reach Binance directly) | [api-reference.md](docs/api-reference.md) |

---

BlockFill Agent Execution is an AI-agent-ready Python SDK and local execution
engine for crypto **perpetual-futures and spot** order execution. It enables AI
agents, trading systems, and developers to execute large orders through TWAP and
maker-style strategies across **11 documented CEX and DEX venues** (Binance, OKX,
Bybit, Bitget, Gate.io, KuCoin, Kraken, Deribit, Hyperliquid, Aster, Orderly),
while keeping exchange API keys on the user's own machine.

You declare a target position; BlockFill works the order over a time window
using **maker** (PostOnly resting + IOC fallback) or **twap** (taker-sliced)
strategies, cutting execution slippage vs. naïve market orders.

One Python API, one local daemon, **11 documented venues across spot + perp**
(see [Supported venues](#supported-venues)), one `bf.place(...)` call per target
position.

## Supported venues

The agent exchange id is `<venue>-<product>` — pass it to `set_credentials(...)`
and use it everywhere (`bf.place`, `bf.quota`, …).

| Exchange | Exchange id (perp/futures · spot) | Quote (spot · perp) |
|---|---|---|
| **Binance** | `binance-futures` · `binance-spot` | usdt, usdc, fdusd, usd · usdt, usdc |
| **OKX** | `okx-swap` · `okx-spot` | usdt, usdc, usd · usdt |
| **Bybit** | `bybit-perp` · `bybit-spot` | usdt, usdc, usd · usdt, usdc |
| **Bitget** | `bitget-futures` · `bitget-spot` | usdt, usdc, usd · usdt, usdc |
| **Gate.io** | `gateio-futures` · `gateio-spot` | usdt, usdc · usdt |
| **KuCoin** | `kucoin-futures` · `kucoin-spot` | usdt, usdc · usdt, usdc |
| **Kraken** | `kraken-futures` · `kraken-spot` | usd, usdt, usdc · usd |
| **Deribit** | `deribit-perp` · `deribit-spot` | usdc, usdt · usdc |
| **Hyperliquid** | `hyperliquid-perp` · `hyperliquid-spot` | usdc · usdc |
| **Aster** | `aster-perp` · `aster-spot` | usdt · usdt |
| **Orderly** | `orderly-<broker>` (WOOFi Pro, …) | — · usdc |

**CEX** (Binance, OKX, Bybit, Bitget, Gate.io, KuCoin, Kraken, Deribit) use
`api_key` + `api_secret` (+ `api_passphrase` for OKX / KuCoin / Bitget) and are
billed by x402 quota. **DEX** (Hyperliquid, Aster, Orderly) sign with a wallet
and pay builder-code execution fees — no quota. Binance also supports TradFi
perpetuals (e.g. `xauusdt`).

## Designed for AI Agents

BlockFill Agent Execution is designed to be called by AI agents, trading
copilots, MCP servers, strategy systems, and execution workflows.

Typical agent instruction:

> Buy 100,000 USDT worth of BTC perpetuals over 30 minutes using TWAP, with
> local API-key signing and no third-party custody.

BlockFill provides the execution layer behind that instruction:

1. The agent decides the trading intent.
2. BlockFill converts the intent into an executable ticket.
3. The local daemon signs and places orders through the user's own exchange
   API keys.
4. The agent can query ticket status, positions, and execution results.

## Security Model

BlockFill Agent Execution is **self-custodial by design**.

Exchange API keys stay on the user's own machine (`~/.blockfill/config.toml`,
chmod 0600). The local daemon signs orders locally and sends them directly to
the exchange. BlockFill does not custody user funds, store exchange API keys,
or route orders through a third-party trading server — no key custody, no
order routing, no order-flow visibility by a third party. The package only
ships the execution engine.

**Trade-only.** BlockFill needs only trading permission — it never calls a
withdrawal or transfer endpoint (verified: none exist in the engine source).
Only a non-reversible SHA-256 hash of your account id leaves the machine; the
raw `api_key`, `api_secret`, and wallet `private_key` never do.

**Full security & compliance reference** — [`SECURITY.md`](SECURITY.md) and
[`docs/security/`](docs/security/index.md): [data flow](docs/security/data-flow-statement.md),
[network flow](docs/security/network-flow.md),
[binary/daemon security](docs/security/binary-daemon-security.md),
[trade-only permissions](docs/security/trade-only-permissions.md),
[EIP-712 authorization](docs/security/eip-712-authorization.md),
[risk disclosure](docs/security/risk-disclosure.md).

### Risk disclosure (summary)

BlockFill is **execution-only**: not an exchange, not a custodian, not an
adviser. It does **not** guarantee fills, prices, or returns; maker orders may
not fully fill and TWAP may incur slippage. You (or your agent) are responsible
for symbol, direction, size, leverage, and mainnet-vs-testnet selection — the
SDK defaults to **mainnet**, so pass `testnet=True` unless mainnet is intended.
Full text: [Risk Disclosure](docs/security/risk-disclosure.md).

## Requirements

- Python 3.10+
- **macOS (Apple Silicon / arm64)** or **Linux (x86_64)** — the daemon binary is bundled inside the wheel, so `pip` fetches the platform-specific wheel automatically (`macosx_11_0_arm64` or `manylinux2014_x86_64`). Other platforms (Windows, Intel macOS, linux-arm64) are not yet packaged.

## Install

```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -U blockfill
```

`-U` is intentional: it installs the latest release if you don't have it, and
upgrades in place if you do (without it, `pip install blockfill` is a no-op
when any version is already installed).

## Verify the install

```python
from blockfill import Blockfill
print(Blockfill().version())   # → "blockfill 1.0.X"
```

## Quickstart

> ⚠️ **Order matters.** `bf.start()` will refuse to launch if no exchange
> credentials are configured. Always call `set_credentials()` first on a
> fresh machine — see `DaemonStartTimeout: no config.toml at ...` below.

```python
from blockfill import Blockfill

bf = Blockfill()

# 1) Write exchange credentials to ~/.blockfill/config.toml (chmod 0600).
#    SDK auto-runs `check_credentials` (a signed REST round-trip) — proves
#    auth works AND the host can reach the exchange. If you're behind a
#    geo block (US → binance), set a proxy first via bf.set_proxy(...).
bf.set_credentials("binance-futures", api_key="...", api_secret="...", testnet=True)

# 2) Start daemon. ~50s warmup while it fetches market data.
bf.start()
bf.status()  # returns DaemonStatus(running=False, ...) if anything is wrong

# Place a ticket
ticket = bf.place(
    exchange="binance-futures",
    symbol="btcusdt",
    strategy="maker",
    target_position=0.1,
    time_constraint_ms=300_000,
)
print(ticket.ticket_id, ticket.status)  # tkt_xxx NEW

# Query active session (in-memory)
tickets = bf.query(status="NEW")

# Cancel
bf.cancel(ticket.ticket_id)

# Shut down daemon
bf.stop()
```

---

## MCP (Claude Code / Claude Desktop)

BlockFill ships an MCP server that exposes the local daemon as tools an LLM can
call directly — `status`, `instruments`, `place`, `query`, `cancel`,
`positions`, `nav`, `tca`, `quota`, `open_orders`. Same security model: the
server runs on your machine and your API keys never leave the host.

### Install (one command)

**Claude Code — no prior install needed** (`uvx` fetches `blockfill[mcp]` from
PyPI, bundled binary included):

```bash
claude mcp add blockfill -- uvx --from 'blockfill[mcp]' blockfill-mcp
```

**Already `pip install`ed?** Register the installed server in one step:

```bash
pip install -U "blockfill[mcp]"
blockfill-mcp --install        # auto-configures detected clients + prints config for the rest
```

`--install` detects and configures **Claude Code** (`claude mcp add`), **Codex**
(`~/.codex/config.toml`), and **Cursor** (`~/.cursor/mcp.json`) without clobbering
existing entries, and prints the generic block below for any other client. The
launch command is identical everywhere — only the config location/format differs.

**Claude Desktop / any MCP client** — add to the client config (e.g.
`claude_desktop_config.json`), then restart the client:

```jsonc
{
  "mcpServers": {
    "blockfill": { "command": "uvx", "args": ["--from", "blockfill[mcp]", "blockfill-mcp"] }
  }
}
```

Then set exchange credentials once (credential entry is intentionally **not** an
MCP tool):

```bash
blockfill set-credentials --exchange binance-futures --api-key ... --api-secret ...
```

### Plugin marketplace (optional — also bundles the skill)

For discovery / to ship the BlockFill skill alongside the tools:

```
/plugin marketplace add <this repo url>
/plugin install blockfill@blockfill
/reload-plugins
```

(The plugin's MCP server still uses the same `uvx --from blockfill[mcp]
blockfill-mcp` command under the hood.)

---

## API Reference

### Version

```python
bf.version() -> str
# Returns "blockfill 1.0.X"
```

---

### Credentials

```python
bf.set_credentials(
    exchange: str,            # "binance-futures" | "okx-swap"
    api_key: str,
    api_secret: str,
    api_passphrase: str | None = None,  # OKX, KuCoin, Bitget — required for these three; None for all others
    testnet: bool = False,  # SDK default is MAINNET — agents MUST pass True explicitly for testnet; default to True unless the user has confirmed mainnet
) -> None
# Writes the [exchanges.<name>] block of {data_dir}/config.toml (chmod 0600),
# runs check_credentials() to print verification, then stops + starts the
# daemon (when running) so the new user_id/creds load immediately — avoids
# identity confusion where `place` / history queries would otherwise keep
# operating under the OLD creds cached in the running daemon.
```

The blockfill-server endpoint and API key are **compiled into the binary** at release time — you do not configure them.

---

### Payment — x402 quota top-up

Quota applies to **CEX venues only** (Binance, OKX, Bybit, Bitget, Gate.io,
KuCoin, Kraken, Deribit); DEX venues (Hyperliquid, Aster, Orderly) pay
builder-code execution fees instead and have no quota.

Quota is tracked per **(account, exchange)** pair. Your *account* is a
**de-identified hash** of the exchange api_key (or, for wallet venues, the wallet
address) — computed locally, so the raw key/address never leaves your machine.
Each exchange credential therefore has its own quota.

Every pair starts with a **free tier**. When it runs out you buy more quota by
paying **USDC on Base** over [x402](https://github.com/coinbase/x402) — a
**gasless** EIP-3009 `transferWithAuthorization`. The daemon holds the wallet key
and signs locally; only the signature + authorization leave the machine.

```python
bf.set_payment(private_key: str | None) -> None
# Store (or clear, with None) the EVM wallet key that pays for top-ups, in
# [payment] of {data_dir}/config.toml (chmod 0600). Restarts the daemon so it
# picks the key up. The wallet must hold USDC on the quota network (Base /
# Base-Sepolia); gas is paid by the facilitator, so no ETH is needed.

bf.topup(exchange: str, usdc: float = 1.0) -> dict | None
# Manually buy quota for `exchange` by paying `usdc` USDC. The daemon fetches
# the 402 challenge, signs the EIP-3009 authorization with the set_payment
# wallet, and settles via the facilitator. Quota is per exchange account, so
# top up each exchange you trade. Returns {exchange, usdc, quota_balance,
# tx_hash} on success, or None on failure (a one-line error is printed).
```

```python
bf.set_payment("0x<64-hex private key>")
bf.topup("okx-swap", 1)   # → {'exchange': 'okx-swap', 'quota_balance': ..., 'tx_hash': '0x…'}
```

The key is never sent anywhere — the server only ever sees the signature and a
de-identified (hashed) account id, never your api_key or wallet key.

---

### Daemon

```python
bf.start(wait_timeout_s=10.0, env=None) -> None
# Spawns the daemon in the background, returns once the UDS socket is bound.
# Idempotent — no-op if already running.

bf.stop(wait_timeout_s=5.0) -> None
# Graceful shutdown.

bf.restart() -> None

bf.status() -> DaemonStatus
# Always returns a DaemonStatus (`running=False, ...` when the daemon is
# not reachable — never raises). Single entry point for everything you
# need to know about the daemon's state.
```

For debugging, tail the daemon log directly:

```bash
tail -F ~/.blockfill/runtime/daemon.startup.log
```

`DaemonStatus` fields:

| Field             | Type                        | Meaning                                                                                                                                                                                                                                                               |
| ----------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `running`         | `bool`                      | Daemon process is up and the RPC socket answered.                                                                                                                                                                                                                     |
| `pid`             | `int`                       | Daemon process id (0 if not running).                                                                                                                                                                                                                                 |
| `exchange` (prop) | `dict[str, ExchangeStatus]` | Per-exchange status keyed by name — see below. **Primary way** to check what's configured and what's ready.                                                                                                                                                           |
| `active_tickets`  | `int`                       | NEW/OPEN tickets currently tracked.                                                                                                                                                                                                                                   |
| `uptime_s`        | `int`                       | Daemon process uptime in seconds.                                                                                                                                                                                                                                     |
| `version`         | `str`                       | Daemon binary version (e.g. `"1.0.X"`).                                                                                                                                                                                                                               |
| `proxy`           | `str \| None`               | Active outbound proxy URL (or None for direct).                                                                                                                                                                                                                       |
| `blockfill-server`            | `bool`                      | True if blockfill-server is reachable. blockfill-server hosts the ticket-history endpoints (`/public/v1/tickets/*`) used by `bf.query(history=True)`; when down, live trading is unaffected but history queries fail. Updated by a 10s background ping; flips False after 30s of no response. |

`exchanges` (`list[str]`, configured names) and `ready_exchanges`
(`list[str]`, warmed-up names) are also present on the dataclass as
flat-list shortcuts — they're just `list(exchange.keys())` and
`[n for n,e in exchange.items() if e.ready]` respectively. Prefer
`exchange[name].ready` in code.

Per-exchange readiness (the only way to check ready — there is no
top-level `ready` flag because "all-ready" is rarely meaningful in a
multi-exchange daemon):

```python
s = bf.status()
s.exchange
# → {
#     "binance-futures": ExchangeStatus(ready=True),
#     "okx-swap":        ExchangeStatus(ready=False),
#   }

if s.exchange["binance-futures"].ready:
    bf.place(exchange="binance-futures", ...)
```

`ready=True` means the executor finished warmup (REST symbol/book fetch,
authenticated `get_account_balances`, market-stream connect — `ready_cb`
fired). `ready=False` covers:

- daemon not running
- warmup in progress (typical 30–60s after `bf.start()`)
- init failed and supervisor is in its retry backoff (bad creds, IP
  whitelist, geo block, exchange API down)

`bf.place(exchange=X, ...)` while `X` is not ready is rejected at the RPC
layer with `-32000 X not ready — executor still warming up; poll
system.status until ready_exchanges contains it` — no ticket is created.

---

### Tickets

#### Strategies

| `strategy` | Behavior                                                                                                                                                                                                                                                          |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"maker"`  | **Passive maker.** Posts PostOnly limit orders that sit on the book. In the last segment of the time window, falls back to IOC to clean up any unfilled remainder. Lower fees (maker rebate when available), no guarantee of full fill if the book never crosses. |
| `"twap"`   | **Pure-taker TWAP.** Places IOC orders on a TWAP schedule across the time window — no PostOnly phase. Guarantees completion at the cost of crossing the spread on every slice.                                                                                    |

```python
bf.place(
    exchange: str,
    symbol: str,
    strategy: str = "maker",        # "maker" | "twap" (see table above)
    target_position: float,          # positive = long, negative = short
    time_constraint_ms: int = 300_000,  # 60_000 .. 86_400_000 (1min .. 24h)
) -> Ticket

bf.query(
    status: str | None = None,       # "NEW" | "OPEN" | "COMPLETE" | "CANCEL"
    symbol: str | None = None,
    ticket_id: str | None = None,
    from_ms: int | None = None,
    to_ms: int | None = None,
    limit: int = 100,
    history: bool = False,           # False=in-memory; True=blockfill-server MongoDB
) -> list[Ticket]

bf.cancel(ticket_id=None, symbol=None, all=False) -> None | int
# - cancel(ticket_id="tkt_...") -> None     # prints an error and returns None if not found (never raises)
# - cancel(symbol="btcusdt")    -> int      # cancel NEW+OPEN for that symbol
# - cancel(all=True)            -> int      # cancel everything active
```

**Spot vs perp `target_position`.** For **perp/futures** it is the net
directional position (positive = long, negative = short) — `target=0.1` from
flat opens 0.1 long. For **spot** it is the absolute base-asset holding you want
to end up with, and `init_position` is your current base balance — so
`target=0.001` on a 1.0 BTC balance **sells** 0.999. To add to a spot holding,
set `target = current_holding + delta`.

`Ticket` fields:

| Field                 | Type            | Notes                                                   |
| --------------------- | --------------- | ------------------------------------------------------- |
| `ticket_id`           | `str`           | `tkt_<hex>`                                             |
| `status`              | `str`           | `NEW` / `OPEN` / `COMPLETE` / `CANCEL`                  |
| `exchange`            | `str`           | `binance-futures` / `okx-swap`                          |
| `symbol`              | `str`           | exchange-format symbol                                  |
| `strategy`            | `str`           | `maker` / `twap`                                        |
| `target_position`     | `float`         | requested net position                                  |
| `init_position`       | `float \| None` | exchange position at activation time                    |
| `executed_position`   | `float \| None` | actual delta filled so far                              |
| `time_constraint_ms`  | `int`           | execution time limit                                    |
| `start_time_ms`       | `int \| None`   | set when executor activates the ticket (NEW → OPEN)     |
| `last_update_time_ms` | `int \| None`   | refreshed on every state change                         |
| `is_expired`          | `bool`          | flag-only; status stays OPEN until separately cancelled |
| `cancel_reason`       | `str \| None`   | see table below                                         |

**`cancel_reason` values**: `external`, `superseded`, `stale`, `rejected`, `min_notional`, `risk_breach`, `insufficient_margin`, `paused`

**Auto-supersede**: placing a new ticket for the same `exchange+symbol` immediately cancels any existing `NEW`/`OPEN` ticket for that pair (`cancel_reason="superseded"`). The superseded ticket remains in query results.

---

### Diagnostics

```python
bf.check_credentials() -> None
# Calls a SIGNED REST endpoint on each configured exchange and prints one
# line per exchange:
#   ✓ binance-futures       3 asset balances returned
#   ✗ okx-swap              API error 50101: APIKey does not match current environment.
# Detects: wrong key/secret, IP whitelist mismatch, testnet/mainnet flag
# wrong, network / proxy / geo block. Does not raise, does not return a
# status code — visual output is the signal.
# Auto-invoked at the end of `set_credentials(...)`.
```

### Positions

```python
bf.positions() -> list[dict]
# Each entry: {exchange, symbol, size, entry_price, update_ts_ms}
# Aggregated across all running executors.

bf.nav() -> dict
# {exchanges: [{exchange, nav, wallet_balance, margin_value, unrealized_pnl}],
#  total_nav, exchanges_queried}. NAV = wallet + margin (USD) + unrealized PnL.

bf.tca(ticket_id=None, symbol=None, from_ms=None, to_ms=None,
       limit=100, history=False) -> list[dict]
# Ticket transaction-cost analysis, queried like bf.query():
# history=False -> active session (in-memory); history=True -> persistent
# (blockfill-server). Each entry: benchmarks, fills, maker/taker breakdown,
# execution_cost_usd / opportunity_cost_usd, duration_ms, ...
```

### Proxy / Geo-bypass

For hosts that can't reach Binance directly (US IPs return HTTP 451),
route exchange REST traffic through an HTTP CONNECT proxy.

**Starchild users** — the free **`sc-vpn`** skill provides a managed
proxy across 18 countries (500 GB/month, no credentials). Pick a country
code and pass the URL:

```python
bf.set_proxy("http://jp:x@sc-vpn.internal:8080")   # Japan
bf.set_proxy("http://sg:x@sc-vpn.internal:8080")   # Singapore
bf.set_proxy("http://hk:x@sc-vpn.internal:8080")   # Hong Kong
bf.set_proxy()                                     # clear
```

Country codes (ISO-2):

| Asia-Pacific     | Europe              | Americas    |
| ---------------- | ------------------- | ----------- |
| `jp` Japan       | `uk` United Kingdom | `ca` Canada |
| `sg` Singapore   | `de` Germany        | `br` Brazil |
| `hk` Hong Kong   | `fr` France         | `mx` Mexico |
| `kr` South Korea | `nl` Netherlands    |             |
| `tw` Taiwan      | `ch` Switzerland    |             |
| `au` Australia   | `it` Italy          |             |
| `in` India       | `es` Spain          |             |
|                  | `se` Sweden         |             |

For binance, `jp` / `sg` / `hk` give the lowest latency. See the
[sc-vpn skill repo](https://github.com/Starchild-ai-agent/official-skills/tree/main/sc-vpn)
for the authoritative list.

You can also pass any HTTP CONNECT proxy URL (residential / paid):

```python
bf.set_proxy("http://user:pass@proxy.example.com:8080")
```

`set_proxy` auto-restarts the daemon (when running) so the new proxy
takes effect immediately — the daemon reads the proxy only at startup
and stashes it in a global, so a write-without-restart would leave the
running daemon on the OLD proxy.

The proxy applies to **all REST traffic** from daemon → exchange.
WebSocket proxy support is planned; until then, market-data streams connect directly and will fail on geo-blocked hosts.

Verify before committing: after `bf.set_proxy(...)` re-set credentials —
`set_credentials` auto-runs `check_credentials` which does a signed REST
round-trip through the proxy. A failure there means the proxy can't reach
the exchange, so you find out before starting the daemon.

---

### Context Manager

```python
with Blockfill() as bf:
    bf.start()
    ticket = bf.place(...)
# daemon is stopped on exit
```

---

## Patterns

### Strategy system integration

```python
from blockfill import Blockfill

bf = Blockfill()

if not bf.status().running:
    bf.start()

# On each signal
ticket = bf.place(
    exchange="binance-futures",
    symbol=symbol,
    strategy="maker",
    target_position=position,
    time_constraint_ms=300_000,
)
```

---

## Supported Exchanges

| Exchange         | Value                | Credentials                                       |
| ---------------- | -------------------- | ------------------------------------------------- |
| Binance Futures  | `"binance-futures"`  | api_key + api_secret (HMAC **or** Ed25519)        |
| OKX Swap         | `"okx-swap"`         | api_key + api_secret + api_passphrase (HMAC)      |
| Hyperliquid Perp | `"hyperliquid-perp"` | master address + agent wallet key (EIP-712)       |
| Aster Perp       | `"aster-perp"`       | user + signer + agent wallet key (EIP-712)        |
| Orderly (perps)  | `"orderly-<broker>"` | account_id + ed25519 secret + broker_id           |

(`"aster-perp-v1"` also exists — Aster's legacy HMAC api_key/secret flow,
configured like a CEX. Prefer `"aster-perp"`.)

### Orderly: broker = the exchange (= a separate vault)

Orderly is a **settlement layer**, not an exchange — the **broker** (WOOFi Pro,
Raydium, …) is the venue. The same wallet under a different `broker_id` is a
**separate account/vault** (`account_id = keccak256(wallet, keccak256(broker_id))`).
So configure each broker as its own instance, `orderly-<broker>`:

```python
bf.set_credentials("orderly-woofi",
    account_id="0x...", orderly_secret="ed25519:...", broker_id="woofi_pro",
    testnet=True)
bf.set_credentials("orderly-raydium",     # same wallet, different vault
    account_id="0x...", orderly_secret="ed25519:...", broker_id="raydium",
    testnet=True)
# bf.place(exchange="orderly-raydium", ...); bf.nav() reports each separately
```

`account_id` is bound to `broker_id` at registration — you cannot switch brokers
by changing `broker_id` alone; register under the new broker and use that
`account_id`. (CLI: `blockfill set-credentials --exchange orderly-raydium
--api-key <account_id> --api-secret <ed25519 secret> --api-passphrase <broker_id>`.)

### Binance Ed25519 keys

Binance Futures **testnet** issues self-generated **Ed25519** keys (no HMAC
secret). They work with the same call — pass the Ed25519 **API Key id** as
`api_key` and the **PEM private key** as `api_secret`; the daemon auto-detects
the PEM and signs with Ed25519 (REST + WS `session.logon`):

```python
bf.set_credentials(
    "binance-futures",
    api_key="<Ed25519 API Key id from Binance>",
    api_secret="""-----BEGIN PRIVATE KEY-----
MC4CAQAw...your PKCS#8 ed25519 key...
-----END PRIVATE KEY-----""",
    testnet=True,
)
```

To create one: generate an Ed25519 keypair, register the **public** key (PEM)
at <https://testnet.binancefuture.com> → API Management, and Binance returns the
API Key id. Keep the private key for `api_secret`. HMAC keys keep working
unchanged.

Hyperliquid and Aster are wallet-signed DEXes — there is no api_key/secret.
Configure them via the SDK:

```python
# Hyperliquid: agent (API) wallet signs on behalf of your master address
bf.set_credentials(
    "hyperliquid-perp",
    hl_master_address="0x...",
    hl_agent_private_key="0x...",
)

# Aster: API wallet (signer) signs on behalf of your main wallet (user)
bf.set_credentials(
    "aster-perp",
    aster_user="0x...",
    aster_signer="0x...",
    aster_agent_private_key="0x...",
)
```

or the bundled CLI (`blockfill set-credentials --exchange hyperliquid-perp
--hl-master-address 0x... --hl-agent-private-key 0x...`; Aster:
`--aster-user/--aster-signer/--aster-agent-private-key`).

### ⚠️ Symbol format differs per exchange

Each exchange has its own native symbol format. BlockFill does NOT
cross-translate — `bf.place(symbol=...)` must match exactly what the
exchange uses:

| Exchange           | Format                         | Example          |
| ------------------ | ------------------------------ | ---------------- |
| `binance-futures`  | lowercase, concatenated        | `dogeusdt`       |
| `okx-swap`         | dash-separated, `-SWAP` suffix | `DOGE-USDT-SWAP` |
| `hyperliquid-perp` | coin only (perps are vs USDC)  | `BTC`            |
| `aster-perp`       | UPPERCASE, concatenated        | `BTCUSDT`        |

Don't guess — look it up with `bf.instruments(<substring>)`. It scans
all configured exchanges and returns native-format matches:

```python
bf.instruments("doge")
# [
#   {"exchange": "binance-futures", "symbol": "dogeusdt",        ...},
#   {"exchange": "okx-swap",        "symbol": "DOGE-USDT-SWAP",  ...},
#   ...
# ]

bf.place(exchange="binance-futures", symbol="dogeusdt",       target_position=100)
bf.place(exchange="okx-swap",        symbol="DOGE-USDT-SWAP", target_position=-100)
```

Wrong format → ticket auto-cancelled with `cancel_reason="rejected"`; no
order ever reaches the exchange.

---

## FAQ

### What is BlockFill Agent Execution?

BlockFill Agent Execution is an AI-agent-ready Python SDK and local execution
engine for crypto perpetual futures order execution.

### Can AI agents use BlockFill?

Yes. AI agents, MCP servers, trading copilots, and strategy systems can call
the BlockFill Python SDK to place and manage execution tickets.

### Does BlockFill custody user funds or API keys?

No. Exchange API keys stay on the user's machine. The local daemon signs
orders locally and sends them directly to the exchange.

### Which exchanges does BlockFill support?

BlockFill Agent Execution supports Binance, OKX, Bybit, Bitget, Gate.io, KuCoin, Kraken, Deribit, Hyperliquid, Aster, and Orderly — see the Supported Venues table for per-exchange product types and credential formats.

### Which execution strategies are supported?

BlockFill currently supports TWAP execution and maker-style execution.

### Is BlockFill an exchange?

No. BlockFill is not an exchange. It is an execution SDK and local order
execution engine that connects to supported exchanges through user-owned API
keys.

---

## Related Concepts

BlockFill Agent Execution is related to AI agent trading, crypto order
execution, TWAP execution, maker execution, algorithmic trading, perpetual
futures trading, Binance Futures execution, OKX Swap execution, Bybit execution,
Bitget execution, Gate.io execution, KuCoin execution, Kraken execution,
Deribit execution, Hyperliquid execution, Aster execution, Orderly execution,
MCP trading tools, and self-custodial trading infrastructure.

---

## Support

- **GitLab Issues**: <https://gitlab.com/quantech-services-group/blockfill-agent-execution/-/issues>
  — bug reports, feature requests
- **Telegram**: message [@blockfill_support](https://t.me/blockfill_support)
  — questions, integration help, incident reports
