Metadata-Version: 2.4
Name: blockfill
Version: 0.1.2
Summary: Python SDK for the blockfill execution daemon
Project-URL: Homepage, https://github.com/mavri-x/blockfill
Author-email: Mavri-X <robot@mavri-x.ai>
License: Proprietary
Requires-Python: >=3.10
Requires-Dist: toml
Description-Content-Type: text/markdown

# blockfill Python SDK

Python wrapper for the [blockfill](../executor/) execution daemon. Covers credentials, daemon management, and ticket lifecycle (place/query/cancel).

## Requirements

- Python 3.10+
- linux-x86_64 (binary is bundled inside the wheel)

## Install

```bash
pip install blockfill
```

The wheel ships with the executor binary pre-built — no separate download needed.

## Quickstart

```python
from blockfill import Blockfill

bf = Blockfill(data_dir="~/.blockfill")

# First-time setup
bf.set_credentials("binance-futures", api_key="...", api_secret="...")

# Start daemon
bf.start()
bf.health()

# 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 (in-memory; add --history for qtex persistent history)
tickets = bf.query(status="NEW")

# Cancel
bf.cancel(ticket.ticket_id)

# Stop daemon
bf.stop()
```

---

## API Reference

### `Blockfill(data_dir, binary_path, timeout_s)`

| Parameter     | Default                        | Description                                |
| ------------- | ------------------------------ | ------------------------------------------ |
| `data_dir`    | `~/.blockfill`                 | Data directory (config, socket, logs)      |
| `binary_path` | bundled `_bin/blockfill`       | Override the bundled binary (rarely used)  |
| `timeout_s`   | `10.0`                         | RPC call timeout (seconds)                 |

---

### Version

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

---

### Credentials

```python
bf.set_credentials(
    exchange: str,            # "binance-futures" | "okx-swap"
    api_key: str,
    api_secret: str,
    api_passphrase: str | None = None,  # OKX only
) -> None
# Writes to {data_dir}/config.toml (chmod 0600)

bf.set_qtex_endpoint(endpoint: str) -> None
# e.g. bf.set_qtex_endpoint("https://qtex.example.com")
```

---

### Daemon

```python
bf.start(wait_timeout_s=10.0, env=None) -> None
# Starts daemon in background. No-op if already running.
# env: extra environment variables (e.g. {"BLOCKFILL_API_KEY": "..."})

bf.stop(wait_timeout_s=5.0) -> None
# Graceful shutdown. No-op if not running.

bf.restart() -> None

bf.is_running() -> bool

bf.health() -> DaemonStatus
# Raises DaemonNotRunning if daemon is not up.

bf.run_foreground(env=None) -> None
# Blocking. For dev/debug.
```

`DaemonStatus` fields: `running`, `pid`, `exchanges`, `active_tickets`, `uptime_s`, `version`

---

### Tickets

```python
bf.place(
    exchange: str,
    symbol: str,
    strategy: str,
    target_position: float,       # positive = buy, negative = sell
    time_constraint_ms: int,      # 60000..86400000 ms
) -> 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,
) -> list[Ticket]

bf.cancel(ticket_id: str) -> None
# Raises TicketNotFound if ticket doesn't exist.

bf.cancel_all() -> int
# Returns number of cancelled tickets.
```

`Ticket` fields: `ticket_id`, `status`, `exchange`, `symbol`, `strategy`, `target_position`, `executed_position`, `time_constraint_ms`, `start_time_ms`, `last_update_time_ms`, `is_expired`, `cancel_reason`

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

---

### Context Manager

```python
with Blockfill(binary_path=..., data_dir=...) as bf:
    bf.start(env={"BLOCKFILL_API_KEY": "..."})
    ticket = bf.place(...)
# daemon is stopped on exit
```

---

### Exceptions

| Exception                 | When                                          |
| ------------------------- | --------------------------------------------- |
| `BinaryNotFound`          | bundled binary missing — `pip install --force-reinstall blockfill` |
| `DaemonNotRunning`        | daemon socket not found or unreachable        |
| `DaemonStartTimeout`      | `start()` timed out waiting for daemon        |
| `RpcError(code, message)` | daemon returned a JSON-RPC error              |
| `TicketNotFound`          | `cancel()` called with non-existent ticket_id |
| `CredentialsError`        | invalid exchange name in `set_credentials()`  |
| `InvalidApiKey`           | qtex rejected the embedded BLOCKFILL_API_KEY (HTTP 401) |

---

## Patterns

### Strategy system integration

```python
from blockfill import Blockfill, DaemonNotRunning

bf = Blockfill(binary_path="/path/to/blockfill")

# On startup
if not bf.is_running():
    bf.start(env={"BLOCKFILL_API_KEY": "..."})
bf.health()

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


### Check open positions

```python
open_tickets = bf.query(status="NEW") + bf.query(status="OPEN")
for t in open_tickets:
    print(t.symbol, t.target_position, t.executed_position)
```

---

## AI Agent Usage

The SDK is designed for programmatic use by AI agents (Claude, GPT, etc.).

```python
import sys
sys.path.insert(0, "/path/to/python-sdk")

import os
os.environ["BLOCKFILL_API_KEY"] = "your_api_key"

from blockfill import Blockfill, DaemonNotRunning, TicketNotFound

bf = Blockfill(
    binary_path="/path/to/blockfill",
    data_dir="/path/to/data_dir",
)

if not bf.is_running():
    bf.start(env={"BLOCKFILL_API_KEY": os.environ["BLOCKFILL_API_KEY"]})

bf.health()  # raises if daemon not ready
```

Key points for agents:

- Always call `bf.health()` before placing tickets
- `place()` auto-supersedes existing NEW+OPEN tickets for the same exchange+symbol
- `query()` returns in-memory daemon state (fast, no network round-trip to exchange)

---

## Directory Structure

```
~/.blockfill/
├── config.toml              # credentials + qtex endpoint (chmod 0600)
├── bin/
│   └── blockfill            # binary
├── runtime/
│   ├── daemon.sock          # UDS socket (CLI ↔ daemon IPC)
│   ├── daemon.pid           # PID file
│   └── daemon.log           # daemon logs
└── trading-log/             # Channel B upload retry buffer
```

## Supported Exchanges

| Exchange        | Value               |
| --------------- | ------------------- |
| Binance Futures | `"binance-futures"` |
| OKX Swap        | `"okx-swap"`        |
