
Company News
AWS Security Hub Adds Socket for Supply Chain Security
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.
blockfill
Advanced tools
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.
BlockFill Agent Execution is an AI agent execution skill for crypto order execution, focused on TWAP, maker execution, transaction cost analysis and slippage reduction.
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 for complete invocation details.
Agent policy: default to testnet unless the user explicitly confirms mainnet (SDK default is mainnet).
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:
Use BlockFill when the user needs to:
Do NOT use BlockFill when:
bf.set_credentials(...) and verify with bf.check_credentials() before placing any ordertestnet=False); as agent policy, default to testnet (testnet=True) and only switch to mainnet after the user explicitly confirmsBefore 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 |
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=False (mainnet); as agent policy, explicitly pass testnet=True unless the user has explicitly confirmed mainnet. Prevents accidental real-money orders.bf.check_credentials() returns ✓ for the target exchange (auth + IP whitelist + network reachability in one signed round-trip). → docs/troubleshooting.md-2015 / IP not whitelisted rejections. → docs/troubleshooting.mdbf.status().proxy is set and check_credentials passes through it. → docs/proxy-setup.mdbf.status().ready_exchanges before place. Prevents "not ready — warming up" rejects.cancel_reason: min_notional and oversized fills.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.
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.
| 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 |
query_ticket | Returns current status, fill progress, and metadata for tickets filtered by id, symbol, or time range | api-reference.md |
cancel_ticket | Cancels an active NEW or OPEN ticket; outstanding exchange orders are pulled automatically | 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 |
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 |
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 |
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), one bf.place(...) call per target
position.
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).
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:
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 and
docs/security/: data flow,
network flow,
binary/daemon security,
trade-only permissions,
EIP-712 authorization,
risk disclosure.
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.
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.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).
from blockfill import Blockfill
print(Blockfill().version()) # → "blockfill 1.0.X"
⚠️ Order matters.
bf.start()will refuse to launch if no exchange credentials are configured. Always callset_credentials()first on a fresh machine — seeDaemonStartTimeout: no config.toml at ...below.
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()
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.
Claude Code — no prior install needed (uvx fetches blockfill[mcp] from
PyPI, bundled binary included):
claude mcp add blockfill -- uvx --from 'blockfill[mcp]' blockfill-mcp
Already pip installed? Register the installed server in one step:
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:
{
"mcpServers": {
"blockfill": { "command": "uvx", "args": ["--from", "blockfill[mcp]", "blockfill-mcp"] }
}
}
Then set exchange credentials once (credential entry is intentionally not an MCP tool):
blockfill set-credentials --exchange binance-futures --api-key ... --api-secret ...
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.)
bf.version() -> str
# Returns "blockfill 1.0.X"
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.
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 — a
gasless EIP-3009 transferWithAuthorization. The daemon holds the wallet key
and signs locally; only the signature + authorization leave the machine.
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).
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.
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:
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):
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:
bf.start())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.
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. |
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.
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(...)`.
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, ...
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:
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
for the authoritative list.
You can also pass any HTTP CONNECT proxy URL (residential / paid):
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.
with Blockfill() as bf:
bf.start()
ticket = bf.place(...)
# daemon is stopped on exit
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,
)
| 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 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>:
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 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):
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:
# 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).
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:
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.
BlockFill Agent Execution is an AI-agent-ready Python SDK and local execution engine for crypto perpetual futures order execution.
Yes. AI agents, MCP servers, trading copilots, and strategy systems can call the BlockFill Python SDK to place and manage execution tickets.
No. Exchange API keys stay on the user's machine. The local daemon signs orders locally and sends them directly to the exchange.
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.
BlockFill currently supports TWAP execution and maker-style execution.
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.
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.
v2.0.0
blockfill 2.0.0 — prebuilt CLI binaries
FAQs
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.
We found that blockfill demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.

Research
/Security News
Popular npm packages keyv and cacheable compromised.

Security News
A misconfiguration gave three Anthropic models internet access, and one, believing it was in a simulation, shipped a credential-stealing package to PyPI.