{
  "markdown": "# BlockFill Agent Execution\n\n<!-- mcp-name: services.quantech/blockfill -->\n\n> BlockFill Agent Execution is an AI agent execution skill for crypto order execution, focused on TWAP, maker execution, transaction cost analysis and slippage reduction.\n\n---\n\n## For AI Agents\n\nBlockFill 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.\n\nRead [SKILL.md](plugin/skills/blockfill/SKILL.md) for complete invocation details.\n\n**Agent policy: default to testnet unless the user explicitly confirms mainnet (SDK default is mainnet).**\n\n---\n\n## What BlockFill Does\n\nBlockFill 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 `taker` strategies to reduce slippage and market impact.\n\nBlockFill is **not**:\n\n- **A signal generator** — it does not analyze markets, generate buy/sell signals, or predict price direction\n- **Investment advice** — it does not recommend trades, position sizes, or portfolio allocations\n- **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\n\n---\n\n## When to Use BlockFill\n\nUse BlockFill when the user needs to:\n\n- **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\n- **Reduce execution slippage** — maker rebates and passive resting orders cut realized cost vs a naive market order\n- **Run a `taker` or `maker` strategy** — time-slice a large order over minutes or hours, or post limit orders that earn rebates and fill passively\n- **Compare transaction cost analysis (TCA)** — retrieve realized fill price vs L1/mid/TWAP benchmark to evaluate execution quality across tickets\n- **Automate order execution in an AI agent trading workflow** — agents can place, monitor, and cancel tickets programmatically via the Python SDK or MCP tools\n\n## When NOT to Use BlockFill\n\nDo NOT use BlockFill when:\n\n- 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\n- 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\n- **Exchange API credentials are not yet configured** — call `bf.set_credentials(...)` and verify with `bf.check_credentials()` before placing any order\n- 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\n- The user wants **spot trading on an exchange not in the supported list** — stocks, forex, and non-crypto assets are out of scope entirely\n- The user wants BlockFill to **promise or guarantee profit** — BlockFill only executes the size/direction the user specifies; it makes no performance claims\n\n---\n\n## Required Parameters\n\nBefore 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.\n\n| Parameter | Required | Default | Description |\n|---|---|---|---|\n| `exchange` | ✅ | — | Exchange id in `<venue>-<product>` format, e.g. `binance-futures`, `okx-swap`, `bybit-perp` |\n| `symbol` | ✅ | — | Native symbol format per exchange, e.g. `btcusdt` for Binance, `BTC-USDT-SWAP` for OKX |\n| `target_position` | ✅ | — | Target position in base asset units; positive = long, negative = short (perp); absolute holding for spot |\n| `side` | implied | — | Implied from the sign of `target_position` — do not pass separately; positive means buy/long, negative means sell/short |\n| `strategy` | no | `maker` | Execution strategy: `maker` (PostOnly resting + IOC fallback) or `taker` (IOC slices on a TWAP schedule). `twap` is accepted as the former name for `taker`. |\n| `time_constraint_ms` | no | `300000` | Execution window in milliseconds (5 minutes). Range: 60,000–86,400,000 (1 min to 24 h) |\n| `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 |\n\n---\n\n## Safety Checklist Before Trading\n\nRun 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.\n\n- [ ] **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.\n- [ ] **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)\n- [ ] **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)\n- [ ] **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)\n- [ ] **Exchange ready** — the venue appears in `bf.status().ready_exchanges` before `place`. Prevents \"not ready — warming up\" rejects.\n- [ ] **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.\n- [ ] **Sufficient margin** — `bf.nav()` shows enough margin for the target position. Avoids `cancel_reason: insufficient_margin`.\n\nBlockFill 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).\n\n---\n\n## Quick Example Prompts\n\nCopy these prompts to test BlockFill with an AI agent:\n\n```\nBuy 0.01 BTC perpetual on Binance futures testnet. Use default maker strategy over 5 minutes.\n```\n\n```\nPlace a maker order: sell 500 USDT worth of ETH on OKX swap testnet, target position -0.15 ETH, 10-minute window.\n```\n\n```\nExecute a time-sliced order on binance-futures testnet: buy 0.1 BTCUSDT perpetual over 15 minutes using taker strategy.\n```\n\n```\nShow me the TCA for my last 5 completed tickets on binance-futures — compare realized fill price vs TWAP and mid benchmarks.\n```\n\n```\nCancel all active orders on binance-futures testnet.\n```\n\n```\nPlace a live (mainnet) maker order for 0.005 BTC on binance-futures — I've already confirmed this is not a test.\n```\n\n---\n\n## Capabilities\n\n| Capability | What it does | Docs |\n|---|---|---|\n| `place_order` | Places an execution ticket (`maker` or `taker`) for a given exchange, symbol, target position, and time window | [api-reference.md](docs/api-reference.md) |\n| `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) |\n| `cancel_ticket` | Cancels an active `NEW` or `OPEN` ticket; outstanding exchange orders are pulled automatically | [api-reference.md](docs/api-reference.md) |\n| `compare_tca` | Retrieves transaction cost analysis for completed tickets — realized price vs L1/mid/TWAP benchmark, bps saved, maker/taker breakdown | [api-reference.md](docs/api-reference.md) |\n| `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) |\n| `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) |\n\n---\n\nBlockFill Agent Execution is an AI-agent-ready Python SDK and local execution\nengine for crypto **perpetual-futures and spot** order execution. It enables AI\nagents, trading systems, and developers to execute large orders through TWAP and\nmaker-style strategies across **11 documented CEX and DEX venues** (Binance, OKX,\nBybit, Bitget, Gate.io, KuCoin, Kraken, Deribit, Hyperliquid, Aster, Orderly),\nwhile keeping exchange API keys on the user's own machine.\n\nYou declare a target position; BlockFill works the order over a time window\nusing **maker** (PostOnly resting + IOC fallback) or **twap** (taker-sliced)\nstrategies, cutting execution slippage vs. naïve market orders.\n\nOne Python API, one local daemon, **11 documented venues across spot + perp**\n(see [Supported venues](#supported-venues)), one `bf.place(...)` call per target\nposition.\n\n## Supported venues\n\nThe agent exchange id is `<venue>-<product>` — pass it to `set_credentials(...)`\nand use it everywhere (`bf.place`, `bf.quota`, …).\n\n| Exchange | Exchange id (perp/futures · spot) | Quote (spot · perp) |\n|---|---|---|\n| **Binance** | `binance-futures` · `binance-spot` | usdt, usdc, fdusd, usd · usdt, usdc |\n| **OKX** | `okx-swap` · `okx-spot` | usdt, usdc, usd · usdt |\n| **Bybit** | `bybit-perp` · `bybit-spot` | usdt, usdc, usd · usdt, usdc |\n| **Bitget** | `bitget-futures` · `bitget-spot` | usdt, usdc, usd · usdt, usdc |\n| **Gate.io** | `gateio-futures` · `gateio-spot` | usdt, usdc · usdt |\n| **KuCoin** | `kucoin-futures` · `kucoin-spot` | usdt, usdc · usdt, usdc |\n| **Kraken** | `kraken-futures` · `kraken-spot` | usd, usdt, usdc · usd |\n| **Deribit** | `deribit-perp` · `deribit-spot` | usdc, usdt · usdc |\n| **Hyperliquid** | `hyperliquid-perp` · `hyperliquid-spot` | usdc · usdc |\n| **Aster** | `aster-perp` · `aster-spot` | usdt · usdt |\n| **Orderly** | `orderly-<broker_id>` (WOOFi Pro, …) | — · usdc |\n\n**CEX** (Binance, OKX, Bybit, Bitget, Gate.io, KuCoin, Kraken, Deribit) use\n`api_key` + `api_secret` (+ `api_passphrase` for OKX / KuCoin / Bitget) and are\nbilled by x402 quota. **DEX** (Hyperliquid, Aster, Orderly) sign with a wallet\nand pay builder-code execution fees — no quota. Binance also supports **TradFi\nperpetuals** (gold, oil, US/HK/KR equities, pre-IPO) — see below.\n\n## Binance TradFi Perpetuals\n\n`binance-futures` carries 157 `TRADIFI_PERPETUAL` contracts alongside the usual\ncrypto perps:\n\n| Category | Count | Examples |\n|---|---|---|\n| Equities (US · HK · KR) | 147 | `tslausdt`, `nvdausdt`, `spyusdt`, `tencentusdt`, `skhynixusdt` |\n| Commodities | 8 | `xauusdt` (gold), `xagusdt` (silver), `clusdt` (WTI), `natgasusdt` |\n| Pre-IPO | 2 | `openaiusdt`, `anthropicusdt` |\n\nBinance gates **all** of them behind a one-time, account-level agreement. Until\nit is signed, every order on those symbols is rejected with:\n\n```\n-4411  Please sign TradFi-Perps agreement contract fapi.\n```\n\nThe failure is silent from the ticket's point of view — the ticket simply sits\nat 0% filled while the executor retries a rejected order until the window\nexpires.\n\n**So BlockFill signs it on your behalf.** Whenever credentials for\n`binance-futures` verify successfully — `bf.set_credentials(...)`,\n`bf.check_credentials()`, or `blockfill check credentials` — BlockFill sends\n`POST /fapi/v1/stock/contract` for that account and reports the outcome:\n\n```json\n{\n  \"exchange\": \"binance-futures\",\n  \"ok\": true,\n  \"detail\": \"1 asset balances returned\",\n  \"tradfi_perps\": \"signed\"\n}\n```\n\nYou do not need to do anything else; `bf.place(exchange=\"binance-futures\",\nsymbol=\"xauusdt\", ...)` works straight away.\n\n**Scope — `binance-futures` on mainnet only.** No other venue has such an\nagreement. Portfolio Margin (`/papi/v1/um/stock/contract`) and Options\n(`/eapi/v1/stock/contract`) each carry their **own separate** agreement and\nBlockFill trades neither, so it never signs those. Testnet is skipped. When the\nexchange is out of scope the `tradfi_perps` field is absent entirely.\n\nSigning never fails a credential check: if the call errors, `tradfi_perps`\ncarries the reason and `ok` is unaffected — crypto perps keep working, only\nTradFi symbols would reject.\n\n> **What you are agreeing to.** TradFi Perps are offered by Nest Exchange\n> Limited, a Binance entity regulated by the FSRA of Abu Dhabi Global Market\n> (ADGM) — a different legal entity from the one behind Binance's crypto perps.\n> The agreement covers the standard derivatives risk disclosures: these\n> contracts **do not represent ownership** of the underlying asset (no shares,\n> no dividends, no voting rights), they trade 24/7 including when the\n> underlying cash market is closed, and positions can be margin-called or fully\n> liquidated. Funding differs materially from crypto perps too — equity and\n> pre-IPO contracts cap funding at ±2.00% versus ±0.30% for `btcusdt`.\n> **Binance exposes no API to query or revoke this agreement — `POST` is the\n> only verb** — so signing is one-way. If you would rather accept it yourself\n> after reading Binance's full terms, sign it in the Binance interface before\n> configuring credentials with BlockFill; re-signing is idempotent and\n> BlockFill's call will simply succeed again.\n\nNote that `paxgusdt` and `xautusdt` are **not** TradFi contracts despite\ntracking gold — they are ordinary gold-backed tokens with contract type\n`PERPETUAL`, need no agreement, and trade on spot as well.\n\n## Hyperliquid & Aster — the account wallet key\n\nBoth DEX venues take one credential — your **account wallet's private key**:\n\n```toml\n[exchanges.hyperliquid-perp]\nprivate_key    = \"0x...\"   # required\nwallet_address = \"0x...\"   # optional — derived from the key when omitted\n```\n\n`wallet_address` is optional because the address is a function of the key. If you\nsupply both, `check_credentials()` cross-checks them and fails on a mismatch —\nthat would mean the config names one account while signing as another.\n\n### Why the owner's key, not a delegated agent wallet\n\nBlockFill's execution fee on these venues is collected through the exchange's own\n**builder-code** mechanism, and both exchanges reject any order carrying a builder\nthe account has not approved — on Hyperliquid with `Builder fee has not been\napproved`, leaving the ticket at 0% filled until its window expires.\n\nThat approval is a *user-signed* action, and both exchanges deliberately refuse it\nfrom an agent (API) wallet: a delegated trading key must not be able to decide who\ngets paid. So with an agent key BlockFill could never grant it, and every order\nwould fail until you authorized it by hand, out of band — with no way for a\nheadless engine to even prompt you.\n\nWith the owner's key, `check_credentials()` signs that one approval for you:\n\n```json\n{\n  \"exchange\": \"hyperliquid-perp\",\n  \"ok\": true,\n  \"detail\": \"agent signature accepted; 3 asset balances\",\n  \"builder_fee\": \"approved just now at 0.015% (was 0 tenths bp)\"\n}\n```\n\nIt reads the current approval first and only signs when ours is missing or below\nour rate, so re-running the check is cheap and idempotent. If signing fails the\ncheck fails, with the reason.\n\n| Venue | Builder address | Rate approved |\n|---|---|---|\n| Hyperliquid (perp **and** spot) | `0xB972e5151b20863380A3E7354dd93F1b888E3352` | 0.015% (1.5 bp) |\n| Aster (perp only) | `0xB972e5151b20863380A3E7354dd93F1b888E3352` | 0.015% (1.5 bp) |\n\nAster **spot** needs no approval: Aster Code exists only on perpetuals (its sole\norder endpoint is `POST /fapi/v3/order`; Aster spot is a separate `sapi` host that\ntakes no builder parameters), so no execution fee is charged there.\n\n> **What you are accepting.** This key can withdraw — the exchange no longer\n> stops that, only the absence of withdrawal code in the engine does. The\n> complete set of actions the engine can sign is listed in\n> [trade-only permissions](docs/security/trade-only-permissions.md): five\n> Hyperliquid trade actions plus `approveBuilderFee`, or on Aster the\n> order/balance/position endpoints plus `approveBuilder`. Hyperliquid's\n> `withdraw3` / `usdSend` / `spotSend` / transfer actions are not implemented at\n> all. If that trade is not acceptable for your funds, use the CEX venues, or\n> fund the DEX account with only what you are willing to expose.\n\n> **Testnet does not need the approval, and that is the trap.** BlockFill attaches\n> no builder code on testnet, so the precondition is invisible there. Since agent\n> policy is to validate on testnet first, re-run `check_credentials()` after\n> switching to mainnet.\n\n## Designed for AI Agents\n\nBlockFill Agent Execution is designed to be called by AI agents, trading\ncopilots, MCP servers, strategy systems, and execution workflows.\n\nTypical agent instruction:\n\n> Buy 100,000 USDT worth of BTC perpetuals over 30 minutes using TWAP, with\n> local API-key signing and no third-party custody.\n\nBlockFill provides the execution layer behind that instruction:\n\n1. The agent decides the trading intent.\n2. BlockFill converts the intent into an executable ticket.\n3. The local daemon signs and places orders through the user's own exchange\n   API keys.\n4. The agent can query ticket status, positions, and execution results.\n\n## Security Model\n\nBlockFill Agent Execution is **self-custodial by design**.\n\nExchange API keys stay on the user's own machine (`~/.blockfill/config.toml`,\nchmod 0600). The local daemon signs orders locally and sends them directly to\nthe exchange. BlockFill does not custody user funds, store exchange API keys,\nor route orders through a third-party trading server — no key custody, no\norder routing, no order-flow visibility by a third party. The package only\nships the execution engine.\n\n**Trade-only operations.** BlockFill never calls a withdrawal or transfer\nendpoint — verified: none exist in the engine source. Only a non-reversible\nSHA-256 hash of your account id leaves the machine; the raw `api_key`,\n`api_secret`, and wallet `private_key` never do.\n\nTwo different guarantees stand behind \"trade-only\", and it matters which one\napplies to you:\n\n- **CEX venues** — create the API key with trade permission only, and the\n  *exchange* refuses a withdrawal regardless of what any code asks for. Holds\n  even if the software were compromised.\n- **DEX venues (Hyperliquid, Aster)** — BlockFill holds your **account wallet's\n  private key**, which the chain permits to withdraw. Nothing at the exchange\n  blocks that; what does is that the engine contains no withdrawal or transfer\n  code. That is auditable, and audited, but it is a weaker guarantee. The\n  owner's key is required because authorizing the builder fee is a user-signed\n  action both exchanges refuse from a delegated agent wallet — see\n  [Hyperliquid & Aster](#hyperliquid--aster--the-account-wallet-key) and\n  [trade-only permissions](docs/security/trade-only-permissions.md) before\n  funding a DEX account.\n\n**Full security & compliance reference** — [`SECURITY.md`](SECURITY.md) and\n[`docs/security/`](docs/security/index.md): [data flow](docs/security/data-flow-statement.md),\n[network flow](docs/security/network-flow.md),\n[binary/daemon security](docs/security/binary-daemon-security.md),\n[trade-only permissions](docs/security/trade-only-permissions.md),\n[EIP-712 authorization](docs/security/eip-712-authorization.md),\n[risk disclosure](docs/security/risk-disclosure.md).\n\n### Risk disclosure (summary)\n\nBlockFill is **execution-only**: not an exchange, not a custodian, not an\nadviser. It does **not** guarantee fills, prices, or returns; maker orders may\nnot fully fill and TWAP may incur slippage. You (or your agent) are responsible\nfor symbol, direction, size, leverage, and mainnet-vs-testnet selection — the\nSDK defaults to **mainnet**, so pass `testnet=True` unless mainnet is intended.\nFull text: [Risk Disclosure](docs/security/risk-disclosure.md).\n\n## Requirements\n\n- Python 3.10+\n- **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.\n\n## Install\n\n```bash\npython3 -m venv .venv && source .venv/bin/activate\npip install -U blockfill\n```\n\n`-U` is intentional: it installs the latest release if you don't have it, and\nupgrades in place if you do (without it, `pip install blockfill` is a no-op\nwhen any version is already installed).\n\n## Verify the install\n\n```python\nfrom blockfill import Blockfill\nprint(Blockfill().version())   # → \"blockfill 1.0.X\"\n```\n\n## Quickstart\n\n> ⚠️ **Order matters.** `bf.start()` will refuse to launch if no exchange\n> credentials are configured. Always call `set_credentials()` first on a\n> fresh machine — see `DaemonStartTimeout: no config.toml at ...` below.\n\n```python\nfrom blockfill import Blockfill\n\nbf = Blockfill()\n\n# 1) Write exchange credentials to ~/.blockfill/config.toml (chmod 0600).\n#    SDK auto-runs `check_credentials` (a signed REST round-trip) — proves\n#    auth works AND the host can reach the exchange. If you're behind a\n#    geo block (US → binance), set a proxy first via bf.set_proxy(...).\nbf.set_credentials(\"binance-futures\", api_key=\"...\", api_secret=\"...\", testnet=True)\n\n# 2) Start daemon. ~50s warmup while it fetches market data.\nbf.start()\nbf.status()  # returns DaemonStatus(running=False, ...) if anything is wrong\n\n# Place a ticket\nticket = bf.place(\n    exchange=\"binance-futures\",\n    symbol=\"btcusdt\",\n    strategy=\"maker\",\n    target_position=0.1,\n    time_constraint_ms=300_000,\n)\nprint(ticket.ticket_id, ticket.status)  # tkt_xxx NEW\n\n# Query active session (in-memory)\ntickets = bf.query(status=\"NEW\")\n\n# Cancel\nbf.cancel(ticket.ticket_id)\n\n# Shut down daemon\nbf.stop()\n```\n\n---\n\n## MCP (Claude Code / Claude Desktop)\n\nBlockFill ships an MCP server that exposes the local daemon as tools an LLM can\ncall directly — `status`, `instruments`, `place`, `query`, `cancel`,\n`positions`, `nav`, `tca`, `quota`, `open_orders`. Same security model: the\nserver runs on your machine and your API keys never leave the host.\n\n### Install (one command)\n\n**Claude Code — no prior install needed** (`uvx` fetches `blockfill[mcp]` from\nPyPI, bundled binary included):\n\n```bash\nclaude mcp add blockfill -- uvx --from 'blockfill[mcp]' blockfill-mcp\n```\n\n**Already `pip install`ed?** Register the installed server in one step:\n\n```bash\npip install -U \"blockfill[mcp]\"\nblockfill-mcp --install        # auto-configures detected clients + prints config for the rest\n```\n\n`--install` detects and configures **Claude Code** (`claude mcp add`), **Codex**\n(`~/.codex/config.toml`), and **Cursor** (`~/.cursor/mcp.json`) without clobbering\nexisting entries, and prints the generic block below for any other client. The\nlaunch command is identical everywhere — only the config location/format differs.\n\n**Claude Desktop / any MCP client** — add to the client config (e.g.\n`claude_desktop_config.json`), then restart the client:\n\n```jsonc\n{\n  \"mcpServers\": {\n    \"blockfill\": { \"command\": \"uvx\", \"args\": [\"--from\", \"blockfill[mcp]\", \"blockfill-mcp\"] }\n  }\n}\n```\n\nThen set exchange credentials once (credential entry is intentionally **not** an\nMCP tool):\n\n```bash\nblockfill set-credentials --exchange binance-futures --api-key ... --api-secret ...\n```\n\n### Plugin marketplace (optional — also bundles the skill)\n\nFor discovery / to ship the BlockFill skill alongside the tools:\n\n```\n/plugin marketplace add <this repo url>\n/plugin install blockfill@blockfill\n/reload-plugins\n```\n\n(The plugin's MCP server still uses the same `uvx --from blockfill[mcp]\nblockfill-mcp` command under the hood.)\n\n---\n\n## API Reference\n\n### Version\n\n```python\nbf.version() -> str\n# Returns \"blockfill 1.0.X\"\n```\n\n---\n\n### Credentials\n\n```python\nbf.set_credentials(\n    exchange: str,            # \"binance-futures\" | \"okx-swap\"\n    api_key: str,\n    api_secret: str,\n    api_passphrase: str | None = None,  # OKX, KuCoin, Bitget — required for these three; None for all others\n    testnet: bool = False,  # SDK default is MAINNET — agents MUST pass True explicitly for testnet; default to True unless the user has confirmed mainnet\n) -> None\n# Writes the [exchanges.<name>] block of {data_dir}/config.toml (chmod 0600),\n# runs check_credentials() to print verification, then stops + starts the\n# daemon (when running) so the new user_id/creds load immediately — avoids\n# identity confusion where `place` / history queries would otherwise keep\n# operating under the OLD creds cached in the running daemon.\n```\n\nThe blockfill-server endpoint and API key are **compiled into the binary** at release time — you do not configure them.\n\n---\n\n### Payment — x402 quota top-up\n\nQuota applies to **CEX venues only** (Binance, OKX, Bybit, Bitget, Gate.io,\nKuCoin, Kraken, Deribit); DEX venues (Hyperliquid, Aster, Orderly) pay\nbuilder-code execution fees instead and have no quota.\n\nQuota is tracked per **(account, exchange)** pair. Your *account* is a\n**de-identified hash** of the exchange api_key (or, for wallet venues, the wallet\naddress) — computed locally, so the raw key/address never leaves your machine.\nEach exchange credential therefore has its own quota.\n\nEvery pair starts with a **free tier**. When it runs out you buy more quota by\npaying **USDC on Base** over [x402](https://github.com/coinbase/x402) — a\n**gasless** EIP-3009 `transferWithAuthorization`. The daemon holds the wallet key\nand signs locally; only the signature + authorization leave the machine.\n\n```python\nbf.set_payment(private_key: str | None) -> None\n# Store (or clear, with None) the EVM wallet key that pays for top-ups, in\n# [payment] of {data_dir}/config.toml (chmod 0600). Restarts the daemon so it\n# picks the key up. The wallet must hold USDC on the quota network (Base /\n# Base-Sepolia); gas is paid by the facilitator, so no ETH is needed.\n\nbf.topup(exchange: str, usdc: float = 1.0) -> dict | None\n# Manually buy quota for `exchange` by paying `usdc` USDC. The daemon fetches\n# the 402 challenge, signs the EIP-3009 authorization with the set_payment\n# wallet, and settles via the facilitator. Quota is per exchange account, so\n# top up each exchange you trade. Returns {exchange, usdc, quota_balance,\n# tx_hash} on success, or None on failure (a one-line error is printed).\n```\n\n```python\nbf.set_payment(\"0x<64-hex private key>\")\nbf.topup(\"okx-swap\", 1)   # → {'exchange': 'okx-swap', 'quota_balance': ..., 'tx_hash': '0x…'}\n```\n\nThe key is never sent anywhere — the server only ever sees the signature and a\nde-identified (hashed) account id, never your api_key or wallet key.\n\n---\n\n### Daemon\n\n```python\nbf.start(wait_timeout_s=10.0, env=None) -> None\n# Spawns the daemon in the background, returns once the UDS socket is bound.\n# Idempotent — no-op if already running.\n\nbf.stop(wait_timeout_s=5.0) -> None\n# Graceful shutdown.\n\nbf.restart() -> None\n\nbf.status() -> DaemonStatus\n# Always returns a DaemonStatus (`running=False, ...` when the daemon is\n# not reachable — never raises). Single entry point for everything you\n# need to know about the daemon's state.\n```\n\nFor debugging, tail the daemon log directly:\n\n```bash\ntail -F ~/.blockfill/runtime/daemon.startup.log\n```\n\n`DaemonStatus` fields:\n\n| Field             | Type                        | Meaning                                                                                                                                                                                                                                                               |\n| ----------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `running`         | `bool`                      | Daemon process is up and the RPC socket answered.                                                                                                                                                                                                                     |\n| `pid`             | `int`                       | Daemon process id (0 if not running).                                                                                                                                                                                                                                 |\n| `exchange` (prop) | `dict[str, ExchangeStatus]` | Per-exchange status keyed by name — see below. **Primary way** to check what's configured and what's ready.                                                                                                                                                           |\n| `active_tickets`  | `int`                       | NEW/OPEN tickets currently tracked.                                                                                                                                                                                                                                   |\n| `uptime_s`        | `int`                       | Daemon process uptime in seconds.                                                                                                                                                                                                                                     |\n| `version`         | `str`                       | Daemon binary version (e.g. `\"1.0.X\"`).                                                                                                                                                                                                                               |\n| `proxy`           | `str \\| None`               | Active outbound proxy URL (or None for direct).                                                                                                                                                                                                                       |\n| `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. |\n\n`exchanges` (`list[str]`, configured names) and `ready_exchanges`\n(`list[str]`, warmed-up names) are also present on the dataclass as\nflat-list shortcuts — they're just `list(exchange.keys())` and\n`[n for n,e in exchange.items() if e.ready]` respectively. Prefer\n`exchange[name].ready` in code.\n\nPer-exchange readiness (the only way to check ready — there is no\ntop-level `ready` flag because \"all-ready\" is rarely meaningful in a\nmulti-exchange daemon):\n\n```python\ns = bf.status()\ns.exchange\n# → {\n#     \"binance-futures\": ExchangeStatus(ready=True),\n#     \"okx-swap\":        ExchangeStatus(ready=False),\n#   }\n\nif s.exchange[\"binance-futures\"].ready:\n    bf.place(exchange=\"binance-futures\", ...)\n```\n\n`ready=True` means the executor finished warmup (REST symbol/book fetch,\nauthenticated `get_account_balances`, market-stream connect — `ready_cb`\nfired). `ready=False` covers:\n\n- daemon not running\n- warmup in progress (typical 30–60s after `bf.start()`)\n- init failed and supervisor is in its retry backoff (bad creds, IP\n  whitelist, geo block, exchange API down)\n\n`bf.place(exchange=X, ...)` while `X` is not ready is rejected at the RPC\nlayer with `-32000 X not ready — executor still warming up; poll\nsystem.status until ready_exchanges contains it` — no ticket is created.\n\n---\n\n### Tickets\n\n#### Strategies\n\n| `strategy` | Behavior                                                                                                                                                                                                                                                          |\n| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `\"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. |\n| `\"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.                                                                                    |\n\n```python\nbf.place(\n    exchange: str,\n    symbol: str,\n    strategy: str = \"maker\",        # \"maker\" | \"twap\" (see table above)\n    target_position: float,          # positive = long, negative = short\n    time_constraint_ms: int = 300_000,  # 60_000 .. 86_400_000 (1min .. 24h)\n) -> Ticket\n\nbf.query(\n    status: str | None = None,       # \"NEW\" | \"OPEN\" | \"COMPLETE\" | \"CANCEL\"\n    symbol: str | None = None,\n    ticket_id: str | None = None,\n    from_ms: int | None = None,\n    to_ms: int | None = None,\n    limit: int = 100,\n    history: bool = False,           # False=in-memory; True=blockfill-server MongoDB\n) -> list[Ticket]\n\nbf.cancel(ticket_id=None, symbol=None, all=False) -> None | int\n# - cancel(ticket_id=\"tkt_...\") -> None     # prints an error and returns None if not found (never raises)\n# - cancel(symbol=\"btcusdt\")    -> int      # cancel NEW+OPEN for that symbol\n# - cancel(all=True)            -> int      # cancel everything active\n```\n\n**Spot vs perp `target_position`.** For **perp/futures** it is the net\ndirectional position (positive = long, negative = short) — `target=0.1` from\nflat opens 0.1 long. For **spot** it is the absolute base-asset holding you want\nto end up with, and `init_position` is your current base balance — so\n`target=0.001` on a 1.0 BTC balance **sells** 0.999. To add to a spot holding,\nset `target = current_holding + delta`.\n\n`Ticket` fields:\n\n| Field                 | Type            | Notes                                                   |\n| --------------------- | --------------- | ------------------------------------------------------- |\n| `ticket_id`           | `str`           | `tkt_<hex>`                                             |\n| `status`              | `str`           | `NEW` / `OPEN` / `COMPLETE` / `CANCEL`                  |\n| `exchange`            | `str`           | `binance-futures` / `okx-swap`                          |\n| `symbol`              | `str`           | exchange-format symbol                                  |\n| `strategy`            | `str`           | `maker` / `taker`                                       |\n| `target_position`     | `float`         | requested net position                                  |\n| `init_position`       | `float \\| None` | exchange position at activation time                    |\n| `executed_position`   | `float \\| None` | actual delta filled so far                              |\n| `time_constraint_ms`  | `int`           | execution time limit                                    |\n| `start_time_ms`       | `int \\| None`   | set when executor activates the ticket (NEW → OPEN)     |\n| `last_update_time_ms` | `int \\| None`   | refreshed on every state change                         |\n| `is_expired`          | `bool`          | flag-only; status stays OPEN until separately cancelled |\n| `cancel_reason`       | `str \\| None`   | see table below                                         |\n\n**`cancel_reason` values**: `external`, `superseded`, `stale`, `rejected`, `min_notional`, `risk_breach`, `insufficient_margin`, `paused`\n\n**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.\n\n---\n\n### Diagnostics\n\n```python\nbf.check_credentials() -> None\n# Calls a SIGNED REST endpoint on each configured exchange and prints one\n# line per exchange:\n#   ✓ binance-futures       3 asset balances returned\n#   ✗ okx-swap              API error 50101: APIKey does not match current environment.\n# Detects: wrong key/secret, IP whitelist mismatch, testnet/mainnet flag\n# wrong, network / proxy / geo block. Does not raise, does not return a\n# status code — visual output is the signal.\n# Auto-invoked at the end of `set_credentials(...)`.\n```\n\n### Positions\n\n```python\nbf.positions() -> list[dict]\n# Each entry: {exchange, symbol, size, entry_price, update_ts_ms}\n# Aggregated across all running executors.\n\nbf.nav() -> dict\n# {exchanges: [{exchange, nav, wallet_balance, margin_value, unrealized_pnl}],\n#  total_nav, exchanges_queried}. NAV = wallet + margin (USD) + unrealized PnL.\n\nbf.tca(ticket_id=None, symbol=None, from_ms=None, to_ms=None,\n       limit=100, history=False) -> list[dict]\n# Ticket transaction-cost analysis, queried like bf.query():\n# history=False -> active session (in-memory); history=True -> persistent\n# (blockfill-server). Each entry: ticket_id, symbol, side, status,\n# complete_ts_ms, l1/mid/twap_slippage_bp (bp, fee-inclusive; negative =\n# beat the benchmark), maker_ratio, taker_ratio (shares of filled notional).\n# null = not measurable, never zero cost. `blockfill tca` returns the same rows.\n```\n\n### Proxy / Geo-bypass\n\nFor hosts that can't reach Binance directly (US IPs return HTTP 451),\nroute exchange REST traffic through an HTTP CONNECT proxy.\n\n**Starchild users** — the free **`sc-vpn`** skill provides a managed\nproxy across 18 countries (500 GB/month, no credentials). Pick a country\ncode and pass the URL:\n\n```python\nbf.set_proxy(\"http://jp:x@sc-vpn.internal:8080\")   # Japan\nbf.set_proxy(\"http://sg:x@sc-vpn.internal:8080\")   # Singapore\nbf.set_proxy(\"http://hk:x@sc-vpn.internal:8080\")   # Hong Kong\nbf.set_proxy()                                     # clear\n```\n\nCountry codes (ISO-2):\n\n| Asia-Pacific     | Europe              | Americas    |\n| ---------------- | ------------------- | ----------- |\n| `jp` Japan       | `uk` United Kingdom | `ca` Canada |\n| `sg` Singapore   | `de` Germany        | `br` Brazil |\n| `hk` Hong Kong   | `fr` France         | `mx` Mexico |\n| `kr` South Korea | `nl` Netherlands    |             |\n| `tw` Taiwan      | `ch` Switzerland    |             |\n| `au` Australia   | `it` Italy          |             |\n| `in` India       | `es` Spain          |             |\n|                  | `se` Sweden         |             |\n\nFor binance, `jp` / `sg` / `hk` give the lowest latency. See the\n[sc-vpn skill repo](https://github.com/Starchild-ai-agent/official-skills/tree/main/sc-vpn)\nfor the authoritative list.\n\nYou can also pass any HTTP CONNECT proxy URL (residential / paid):\n\n```python\nbf.set_proxy(\"http://user:pass@proxy.example.com:8080\")\n```\n\n`set_proxy` auto-restarts the daemon (when running) so the new proxy\ntakes effect immediately — the daemon reads the proxy only at startup\nand stashes it in a global, so a write-without-restart would leave the\nrunning daemon on the OLD proxy.\n\nThe proxy applies to **all REST traffic** from daemon → exchange.\nWebSocket proxy support is planned; until then, market-data streams connect directly and will fail on geo-blocked hosts.\n\nVerify before committing: after `bf.set_proxy(...)` re-set credentials —\n`set_credentials` auto-runs `check_credentials` which does a signed REST\nround-trip through the proxy. A failure there means the proxy can't reach\nthe exchange, so you find out before starting the daemon.\n\n---\n\n### Context Manager\n\n```python\nwith Blockfill() as bf:\n    bf.start()\n    ticket = bf.place(...)\n# daemon is stopped on exit\n```\n\n---\n\n## Patterns\n\n### Strategy system integration\n\n```python\nfrom blockfill import Blockfill\n\nbf = Blockfill()\n\nif not bf.status().running:\n    bf.start()\n\n# On each signal\nticket = bf.place(\n    exchange=\"binance-futures\",\n    symbol=symbol,\n    strategy=\"maker\",\n    target_position=position,\n    time_constraint_ms=300_000,\n)\n```\n\n---\n\n## Supported Exchanges\n\n| Exchange         | Value                | Credentials                                       |\n| ---------------- | -------------------- | ------------------------------------------------- |\n| Binance Futures  | `\"binance-futures\"`  | api_key + api_secret (HMAC **or** Ed25519)        |\n| OKX Swap         | `\"okx-swap\"`         | api_key + api_secret + api_passphrase (HMAC)      |\n| Hyperliquid Perp | `\"hyperliquid-perp\"` | account wallet `private_key` (EIP-712)            |\n| Aster Perp       | `\"aster-perp\"`       | account wallet `private_key` (EIP-712)            |\n| Orderly (perps)  | `\"orderly\"`          | account_id + ed25519 secret + broker_id           |\n\n(`\"aster-perp-v1\"` also exists — Aster's legacy HMAC api_key/secret flow,\nconfigured like a CEX. Prefer `\"aster-perp\"`.)\n\n### Orderly: broker = the exchange (= a separate vault)\n\nOrderly is a **settlement layer**, not an exchange — the **broker** (WOOFi Pro,\nRaydium, …) is the venue. The same wallet under a different `broker_id` is a\n**separate account/vault** (`account_id = keccak256(wallet, keccak256(broker_id))`).\n\nPass the bare name `\"orderly\"`; the entry is stored under `orderly-<broker_id>`,\nwhich is the name to give `place()`. Configure each broker once:\n\n```python\nbf.set_credentials(\"orderly\",\n    account_id=\"0x...\", orderly_secret=\"...\", broker_id=\"woofi_pro\",\n    testnet=True)                          # → stored as orderly-woofi_pro\nbf.set_credentials(\"orderly\",              # same wallet, different vault\n    account_id=\"0x...\", orderly_secret=\"...\", broker_id=\"raydium\",\n    testnet=True)                          # → stored as orderly-raydium\n# bf.place(exchange=\"orderly-raydium\", ...); bf.nav() reports each separately\n```\n\n`api_key` / `api_secret` are accepted as aliases for `account_id` /\n`orderly_secret`. The secret is the base58 ed25519 seed; Orderly's key generator\nemits it as `ed25519:<base58>` and either form is accepted — the prefix belongs\non the wire, so it is stripped before storage.\n\n`account_id` is bound to `broker_id` at registration — you cannot switch brokers\nby changing `broker_id` alone; register under the new broker and use that\n`account_id`. Passing the (public) `wallet_address` is optional and turns that\nrule into a check: `check_credentials` recomputes the account id and fails when\nthe trio disagrees, which is the only way to catch a `broker_id` that is real but\nbelongs to a different account of the same wallet.\n\nCLI equivalent:\n\n```bash\nblockfill set-credentials --exchange orderly \\\n  --api-key <account_id> --api-secret <base58 ed25519 secret> \\\n  --api-passphrase woofi_pro\n```\n\n#### Starting from just a wallet\n\n`register-orderly` does the whole Orderly onboarding in one command — registers\nthe account under the broker, generates an ed25519 pair, delegates it via\nEIP-712, and writes the credentials:\n\n```bash\nblockfill register-orderly --broker-id woofi_pro --testnet\n```\n\nThe wallet key is prompted for when not passed, and is stored alongside the\ndelegated access key so re-registering under another broker or rotating an\nexpiring access key needs no re-entry — both operations require a wallet EIP-712\nsignature again. Note it **can withdraw**, unlike the ed25519 access key beside\nit; config.toml is 0600, but treat the entry as wallet-grade. Add `--scope` (must\ninclude `trading`) or `--expiration-days` (Orderly's maximum is 365) to override\nthe defaults.\n\n### Binance Ed25519 keys\n\nBinance Futures **testnet** issues self-generated **Ed25519** keys (no HMAC\nsecret). They work with the same call — pass the Ed25519 **API Key id** as\n`api_key` and the **PEM private key** as `api_secret`; the daemon auto-detects\nthe PEM and signs with Ed25519 (REST + WS `session.logon`):\n\n```python\nbf.set_credentials(\n    \"binance-futures\",\n    api_key=\"<Ed25519 API Key id from Binance>\",\n    api_secret=\"\"\"-----BEGIN PRIVATE KEY-----\nMC4CAQAw...your PKCS#8 ed25519 key...\n-----END PRIVATE KEY-----\"\"\",\n    testnet=True,\n)\n```\n\nTo create one: generate an Ed25519 keypair, register the **public** key (PEM)\nat <https://testnet.binancefuture.com> → API Management, and Binance returns the\nAPI Key id. Keep the private key for `api_secret`. HMAC keys keep working\nunchanged.\n\nHyperliquid and Aster are wallet-signed DEXes — there is no api_key/secret.\nConfigure them via the SDK:\n\n```python\n# Hyperliquid / Aster: the account owner's wallet key. It signs orders AND the\n# one-time builder-fee approval, which an agent/API wallet is not allowed to\n# sign — see docs/security/trade-only-permissions.md before using these venues.\nbf.set_credentials(\n    \"hyperliquid-perp\",\n    private_key=\"0x...\",      # required\n    # wallet_address=\"0x...\", # optional — derived from the key when omitted\n)\nbf.set_credentials(\"aster-perp\", private_key=\"0x...\")\n```\n\nor the bundled CLI (`blockfill set-credentials --exchange hyperliquid-perp\n--hl-master-address 0x... --hl-agent-private-key 0x...`; Aster:\n`--aster-user/--aster-signer/--aster-agent-private-key`).\n\n### ⚠️ Symbol format differs per exchange\n\nEach exchange has its own native symbol format. BlockFill does NOT\ncross-translate — `bf.place(symbol=...)` must match exactly what the\nexchange uses:\n\n| Exchange           | Format                         | Example          |\n| ------------------ | ------------------------------ | ---------------- |\n| `binance-futures`  | lowercase, concatenated        | `dogeusdt`       |\n| `okx-swap`         | dash-separated, `-SWAP` suffix | `DOGE-USDT-SWAP` |\n| `hyperliquid-perp` | coin only (perps are vs USDC)  | `BTC`            |\n| `aster-perp`       | UPPERCASE, concatenated        | `BTCUSDT`        |\n\nDon't guess — look it up with `bf.instruments(<substring>)`. It scans\nall configured exchanges and returns native-format matches:\n\n```python\nbf.instruments(\"doge\")\n# [\n#   {\"exchange\": \"binance-futures\", \"symbol\": \"dogeusdt\",        ...},\n#   {\"exchange\": \"okx-swap\",        \"symbol\": \"DOGE-USDT-SWAP\",  ...},\n#   ...\n# ]\n\nbf.place(exchange=\"binance-futures\", symbol=\"dogeusdt\",       target_position=100)\nbf.place(exchange=\"okx-swap\",        symbol=\"DOGE-USDT-SWAP\", target_position=-100)\n```\n\nWrong format → ticket auto-cancelled with `cancel_reason=\"rejected\"`; no\norder ever reaches the exchange.\n\n---\n\n## FAQ\n\n### What is BlockFill Agent Execution?\n\nBlockFill Agent Execution is an AI-agent-ready Python SDK and local execution\nengine for crypto perpetual futures order execution.\n\n### Can AI agents use BlockFill?\n\nYes. AI agents, MCP servers, trading copilots, and strategy systems can call\nthe BlockFill Python SDK to place and manage execution tickets.\n\n### Does BlockFill custody user funds or API keys?\n\nNo. Exchange API keys stay on the user's machine. The local daemon signs\norders locally and sends them directly to the exchange.\n\n### Which exchanges does BlockFill support?\n\nBlockFill 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.\n\n### Which execution strategies are supported?\n\nBlockFill currently supports TWAP execution and maker-style execution.\n\n### Is BlockFill an exchange?\n\nNo. BlockFill is not an exchange. It is an execution SDK and local order\nexecution engine that connects to supported exchanges through user-owned API\nkeys.\n\n---\n\n## Related Concepts\n\nBlockFill Agent Execution is related to AI agent trading, crypto order\nexecution, TWAP execution, maker execution, algorithmic trading, perpetual\nfutures trading, Binance Futures execution, OKX Swap execution, Bybit execution,\nBitget execution, Gate.io execution, KuCoin execution, Kraken execution,\nDeribit execution, Hyperliquid execution, Aster execution, Orderly execution,\nMCP trading tools, and self-custodial trading infrastructure.\n\n---\n\n## Support\n\n- **GitLab Issues**: <https://gitlab.com/quantech-services-group/blockfill-agent-execution/-/issues>\n  — bug reports, feature requests\n- **Telegram**: message [@blockfill_support](https://t.me/blockfill_support)\n  — questions, integration help, incident reports",
  "bytes": 49190,
  "sha": "af65dba4328ee1297c37bd7706f0495238aca1395809c872bed5ca308d112876",
  "repo_slug": "",
  "fonte": "pypi",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_services_quantech_blockfill_753fbf6d/readme"
}