blockfill 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- blockfill-0.1.0/PKG-INFO +299 -0
- blockfill-0.1.0/README.md +288 -0
- blockfill-0.1.0/blockfill/__init__.py +24 -0
- blockfill-0.1.0/blockfill/client.py +218 -0
- blockfill-0.1.0/blockfill/config.py +48 -0
- blockfill-0.1.0/blockfill/daemon.py +90 -0
- blockfill-0.1.0/blockfill/exceptions.py +29 -0
- blockfill-0.1.0/blockfill/installer.py +42 -0
- blockfill-0.1.0/blockfill/models.py +38 -0
- blockfill-0.1.0/blockfill/rpc.py +23 -0
- blockfill-0.1.0/blockfill.egg-info/PKG-INFO +299 -0
- blockfill-0.1.0/blockfill.egg-info/SOURCES.txt +15 -0
- blockfill-0.1.0/blockfill.egg-info/dependency_links.txt +1 -0
- blockfill-0.1.0/blockfill.egg-info/requires.txt +1 -0
- blockfill-0.1.0/blockfill.egg-info/top_level.txt +1 -0
- blockfill-0.1.0/pyproject.toml +16 -0
- blockfill-0.1.0/setup.cfg +4 -0
blockfill-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blockfill
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the blockfill execution daemon
|
|
5
|
+
Author-email: Mavri-X <robot@mavri-x.ai>
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://github.com/mavri-x/blockfill
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: toml
|
|
11
|
+
|
|
12
|
+
# blockfill Python SDK
|
|
13
|
+
|
|
14
|
+
Python wrapper for the [blockfill](../executor/) execution daemon. Covers the full lifecycle: install binary, configure credentials, manage daemon, place/query/cancel tickets.
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
- Python 3.10+
|
|
19
|
+
- blockfill binary (see [Install](#install))
|
|
20
|
+
- blockfill daemon running (see [Daemon](#daemon))
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install blockfill
|
|
26
|
+
# or from source
|
|
27
|
+
pip install -e /path/to/python-sdk
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quickstart
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from blockfill import Blockfill
|
|
34
|
+
|
|
35
|
+
bf = Blockfill(
|
|
36
|
+
binary_path="/path/to/blockfill", # or ~/.blockfill/bin/blockfill
|
|
37
|
+
data_dir="~/.blockfill",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# First-time setup
|
|
41
|
+
bf.set_credentials("binance-futures", api_key="...", api_secret="...")
|
|
42
|
+
bf.set_qtex_endpoint("https://qtex.example.com")
|
|
43
|
+
|
|
44
|
+
# Start daemon
|
|
45
|
+
bf.start(env={"BLOCKFILL_API_KEY": "..."})
|
|
46
|
+
bf.health()
|
|
47
|
+
|
|
48
|
+
# Place a ticket
|
|
49
|
+
ticket = bf.place(
|
|
50
|
+
exchange="binance-futures",
|
|
51
|
+
symbol="btcusdt",
|
|
52
|
+
strategy="maker_l2",
|
|
53
|
+
target_position=0.1,
|
|
54
|
+
time_constraint_ms=300_000,
|
|
55
|
+
)
|
|
56
|
+
print(ticket.ticket_id, ticket.status) # tkt_xxx NEW
|
|
57
|
+
|
|
58
|
+
# Query
|
|
59
|
+
tickets = bf.query(status="NEW")
|
|
60
|
+
|
|
61
|
+
# Cancel
|
|
62
|
+
bf.cancel(ticket.ticket_id)
|
|
63
|
+
|
|
64
|
+
# Stop daemon
|
|
65
|
+
bf.stop()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## API Reference
|
|
71
|
+
|
|
72
|
+
### `Blockfill(binary_path, data_dir, timeout_s)`
|
|
73
|
+
|
|
74
|
+
| Parameter | Default | Description |
|
|
75
|
+
| ------------- | ---------------------------- | ------------------------------------- |
|
|
76
|
+
| `binary_path` | `~/.blockfill/bin/blockfill` | Path to blockfill binary |
|
|
77
|
+
| `data_dir` | `~/.blockfill` | Data directory (config, socket, logs) |
|
|
78
|
+
| `timeout_s` | `10.0` | RPC call timeout (seconds) |
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### Install & Version
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
bf.install(version="latest", force=False) -> str
|
|
86
|
+
# Returns installed version string.
|
|
87
|
+
# If binary already exists and force=False, skips download and returns current version.
|
|
88
|
+
|
|
89
|
+
bf.version() -> str
|
|
90
|
+
# Returns "blockfill 0.1.0"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
### Credentials
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
bf.set_credentials(
|
|
99
|
+
exchange: str, # "binance-futures" | "okx-swap"
|
|
100
|
+
api_key: str,
|
|
101
|
+
api_secret: str,
|
|
102
|
+
api_passphrase: str | None = None, # OKX only
|
|
103
|
+
) -> None
|
|
104
|
+
# Writes to {data_dir}/config.toml (chmod 0600)
|
|
105
|
+
|
|
106
|
+
bf.set_qtex_endpoint(endpoint: str) -> None
|
|
107
|
+
# e.g. bf.set_qtex_endpoint("https://qtex.example.com")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### Daemon
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
bf.start(wait_timeout_s=10.0, env=None) -> None
|
|
116
|
+
# Starts daemon in background. No-op if already running.
|
|
117
|
+
# env: extra environment variables (e.g. {"BLOCKFILL_API_KEY": "..."})
|
|
118
|
+
|
|
119
|
+
bf.stop(wait_timeout_s=5.0) -> None
|
|
120
|
+
# Graceful shutdown. No-op if not running.
|
|
121
|
+
|
|
122
|
+
bf.restart() -> None
|
|
123
|
+
|
|
124
|
+
bf.is_running() -> bool
|
|
125
|
+
|
|
126
|
+
bf.health() -> DaemonStatus
|
|
127
|
+
# Raises DaemonNotRunning if daemon is not up.
|
|
128
|
+
|
|
129
|
+
bf.run_foreground(env=None) -> None
|
|
130
|
+
# Blocking. For dev/debug.
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`DaemonStatus` fields: `running`, `pid`, `exchanges`, `active_tickets`, `uptime_s`, `version`
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### Tickets
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
bf.place(
|
|
141
|
+
exchange: str,
|
|
142
|
+
symbol: str,
|
|
143
|
+
strategy: str,
|
|
144
|
+
target_position: float, # positive = buy, negative = sell
|
|
145
|
+
time_constraint_ms: int,
|
|
146
|
+
client_ticket_id: str | None = None, # idempotency key
|
|
147
|
+
) -> Ticket
|
|
148
|
+
|
|
149
|
+
bf.query(
|
|
150
|
+
status: str | None = None, # "NEW" | "OPEN" | "COMPLETE" | "CANCEL"
|
|
151
|
+
symbol: str | None = None,
|
|
152
|
+
ticket_id: str | None = None,
|
|
153
|
+
from_ms: int | None = None,
|
|
154
|
+
to_ms: int | None = None,
|
|
155
|
+
limit: int = 100,
|
|
156
|
+
) -> list[Ticket]
|
|
157
|
+
|
|
158
|
+
bf.cancel(ticket_id: str) -> None
|
|
159
|
+
# Raises TicketNotFound if ticket doesn't exist.
|
|
160
|
+
|
|
161
|
+
bf.cancel_all() -> int
|
|
162
|
+
# Returns number of cancelled tickets.
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`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`
|
|
166
|
+
|
|
167
|
+
**Auto-cancel**: placing a new ticket for the same `exchange+symbol` automatically cancels existing `NEW` tickets for that pair.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### Context Manager
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
with Blockfill(binary_path=..., data_dir=...) as bf:
|
|
175
|
+
bf.start(env={"BLOCKFILL_API_KEY": "..."})
|
|
176
|
+
ticket = bf.place(...)
|
|
177
|
+
# daemon is stopped on exit
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### Exceptions
|
|
183
|
+
|
|
184
|
+
| Exception | When |
|
|
185
|
+
| ------------------------- | --------------------------------------------- |
|
|
186
|
+
| `BinaryNotFound` | binary not found, call `install()` first |
|
|
187
|
+
| `DaemonNotRunning` | daemon socket not found or unreachable |
|
|
188
|
+
| `DaemonStartTimeout` | `start()` timed out waiting for daemon |
|
|
189
|
+
| `RpcError(code, message)` | daemon returned a JSON-RPC error |
|
|
190
|
+
| `TicketNotFound` | `cancel()` called with non-existent ticket_id |
|
|
191
|
+
| `CredentialsError` | invalid exchange name in `set_credentials()` |
|
|
192
|
+
| `InstallError` | binary download failed |
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Patterns
|
|
197
|
+
|
|
198
|
+
### Strategy system integration
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from blockfill import Blockfill, DaemonNotRunning
|
|
202
|
+
|
|
203
|
+
bf = Blockfill(binary_path="/path/to/blockfill")
|
|
204
|
+
|
|
205
|
+
# On startup
|
|
206
|
+
if not bf.is_running():
|
|
207
|
+
bf.start(env={"BLOCKFILL_API_KEY": "..."})
|
|
208
|
+
bf.health()
|
|
209
|
+
|
|
210
|
+
# On each signal
|
|
211
|
+
ticket = bf.place(
|
|
212
|
+
exchange="binance-futures",
|
|
213
|
+
symbol=symbol,
|
|
214
|
+
strategy="maker_l2",
|
|
215
|
+
target_position=position,
|
|
216
|
+
time_constraint_ms=300_000,
|
|
217
|
+
)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Idempotent placement (safe to retry)
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
import uuid
|
|
224
|
+
|
|
225
|
+
key = str(uuid.uuid4()) # generate once, reuse on retry
|
|
226
|
+
|
|
227
|
+
ticket = bf.place(
|
|
228
|
+
exchange="binance-futures",
|
|
229
|
+
symbol="btcusdt",
|
|
230
|
+
strategy="maker_l2",
|
|
231
|
+
target_position=0.1,
|
|
232
|
+
time_constraint_ms=300_000,
|
|
233
|
+
client_ticket_id=key,
|
|
234
|
+
)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Check open positions
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
open_tickets = bf.query(status="NEW") + bf.query(status="OPEN")
|
|
241
|
+
for t in open_tickets:
|
|
242
|
+
print(t.symbol, t.target_position, t.executed_position)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## AI Agent Usage
|
|
248
|
+
|
|
249
|
+
The SDK is designed for programmatic use by AI agents (Claude, GPT, etc.).
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
import sys
|
|
253
|
+
sys.path.insert(0, "/path/to/python-sdk")
|
|
254
|
+
|
|
255
|
+
import os
|
|
256
|
+
os.environ["BLOCKFILL_API_KEY"] = "your_api_key"
|
|
257
|
+
|
|
258
|
+
from blockfill import Blockfill, DaemonNotRunning, TicketNotFound
|
|
259
|
+
|
|
260
|
+
bf = Blockfill(
|
|
261
|
+
binary_path="/path/to/blockfill",
|
|
262
|
+
data_dir="/path/to/data_dir",
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
if not bf.is_running():
|
|
266
|
+
bf.start(env={"BLOCKFILL_API_KEY": os.environ["BLOCKFILL_API_KEY"]})
|
|
267
|
+
|
|
268
|
+
bf.health() # raises if daemon not ready
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Key points for agents:
|
|
272
|
+
|
|
273
|
+
- Always call `bf.health()` before placing tickets
|
|
274
|
+
- Use `client_ticket_id=uuid.uuid4()` for idempotent retries
|
|
275
|
+
- `place()` auto-cancels existing NEW tickets for the same exchange+symbol
|
|
276
|
+
- `query()` returns data from qtex MongoDB (not exchange realtime)
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Directory Structure
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
~/.blockfill/
|
|
284
|
+
├── config.toml # credentials + qtex endpoint (chmod 0600)
|
|
285
|
+
├── bin/
|
|
286
|
+
│ └── blockfill # binary
|
|
287
|
+
├── runtime/
|
|
288
|
+
│ ├── daemon.sock # UDS socket (CLI ↔ daemon IPC)
|
|
289
|
+
│ ├── daemon.pid # PID file
|
|
290
|
+
│ └── daemon.log # daemon logs
|
|
291
|
+
└── trading-log/ # Channel B upload retry buffer
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Supported Exchanges
|
|
295
|
+
|
|
296
|
+
| Exchange | Value |
|
|
297
|
+
| --------------- | ------------------- |
|
|
298
|
+
| Binance Futures | `"binance-futures"` |
|
|
299
|
+
| OKX Swap | `"okx-swap"` |
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# blockfill Python SDK
|
|
2
|
+
|
|
3
|
+
Python wrapper for the [blockfill](../executor/) execution daemon. Covers the full lifecycle: install binary, configure credentials, manage daemon, place/query/cancel tickets.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Python 3.10+
|
|
8
|
+
- blockfill binary (see [Install](#install))
|
|
9
|
+
- blockfill daemon running (see [Daemon](#daemon))
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install blockfill
|
|
15
|
+
# or from source
|
|
16
|
+
pip install -e /path/to/python-sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quickstart
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from blockfill import Blockfill
|
|
23
|
+
|
|
24
|
+
bf = Blockfill(
|
|
25
|
+
binary_path="/path/to/blockfill", # or ~/.blockfill/bin/blockfill
|
|
26
|
+
data_dir="~/.blockfill",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# First-time setup
|
|
30
|
+
bf.set_credentials("binance-futures", api_key="...", api_secret="...")
|
|
31
|
+
bf.set_qtex_endpoint("https://qtex.example.com")
|
|
32
|
+
|
|
33
|
+
# Start daemon
|
|
34
|
+
bf.start(env={"BLOCKFILL_API_KEY": "..."})
|
|
35
|
+
bf.health()
|
|
36
|
+
|
|
37
|
+
# Place a ticket
|
|
38
|
+
ticket = bf.place(
|
|
39
|
+
exchange="binance-futures",
|
|
40
|
+
symbol="btcusdt",
|
|
41
|
+
strategy="maker_l2",
|
|
42
|
+
target_position=0.1,
|
|
43
|
+
time_constraint_ms=300_000,
|
|
44
|
+
)
|
|
45
|
+
print(ticket.ticket_id, ticket.status) # tkt_xxx NEW
|
|
46
|
+
|
|
47
|
+
# Query
|
|
48
|
+
tickets = bf.query(status="NEW")
|
|
49
|
+
|
|
50
|
+
# Cancel
|
|
51
|
+
bf.cancel(ticket.ticket_id)
|
|
52
|
+
|
|
53
|
+
# Stop daemon
|
|
54
|
+
bf.stop()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## API Reference
|
|
60
|
+
|
|
61
|
+
### `Blockfill(binary_path, data_dir, timeout_s)`
|
|
62
|
+
|
|
63
|
+
| Parameter | Default | Description |
|
|
64
|
+
| ------------- | ---------------------------- | ------------------------------------- |
|
|
65
|
+
| `binary_path` | `~/.blockfill/bin/blockfill` | Path to blockfill binary |
|
|
66
|
+
| `data_dir` | `~/.blockfill` | Data directory (config, socket, logs) |
|
|
67
|
+
| `timeout_s` | `10.0` | RPC call timeout (seconds) |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### Install & Version
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
bf.install(version="latest", force=False) -> str
|
|
75
|
+
# Returns installed version string.
|
|
76
|
+
# If binary already exists and force=False, skips download and returns current version.
|
|
77
|
+
|
|
78
|
+
bf.version() -> str
|
|
79
|
+
# Returns "blockfill 0.1.0"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### Credentials
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
bf.set_credentials(
|
|
88
|
+
exchange: str, # "binance-futures" | "okx-swap"
|
|
89
|
+
api_key: str,
|
|
90
|
+
api_secret: str,
|
|
91
|
+
api_passphrase: str | None = None, # OKX only
|
|
92
|
+
) -> None
|
|
93
|
+
# Writes to {data_dir}/config.toml (chmod 0600)
|
|
94
|
+
|
|
95
|
+
bf.set_qtex_endpoint(endpoint: str) -> None
|
|
96
|
+
# e.g. bf.set_qtex_endpoint("https://qtex.example.com")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### Daemon
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
bf.start(wait_timeout_s=10.0, env=None) -> None
|
|
105
|
+
# Starts daemon in background. No-op if already running.
|
|
106
|
+
# env: extra environment variables (e.g. {"BLOCKFILL_API_KEY": "..."})
|
|
107
|
+
|
|
108
|
+
bf.stop(wait_timeout_s=5.0) -> None
|
|
109
|
+
# Graceful shutdown. No-op if not running.
|
|
110
|
+
|
|
111
|
+
bf.restart() -> None
|
|
112
|
+
|
|
113
|
+
bf.is_running() -> bool
|
|
114
|
+
|
|
115
|
+
bf.health() -> DaemonStatus
|
|
116
|
+
# Raises DaemonNotRunning if daemon is not up.
|
|
117
|
+
|
|
118
|
+
bf.run_foreground(env=None) -> None
|
|
119
|
+
# Blocking. For dev/debug.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`DaemonStatus` fields: `running`, `pid`, `exchanges`, `active_tickets`, `uptime_s`, `version`
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Tickets
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
bf.place(
|
|
130
|
+
exchange: str,
|
|
131
|
+
symbol: str,
|
|
132
|
+
strategy: str,
|
|
133
|
+
target_position: float, # positive = buy, negative = sell
|
|
134
|
+
time_constraint_ms: int,
|
|
135
|
+
client_ticket_id: str | None = None, # idempotency key
|
|
136
|
+
) -> Ticket
|
|
137
|
+
|
|
138
|
+
bf.query(
|
|
139
|
+
status: str | None = None, # "NEW" | "OPEN" | "COMPLETE" | "CANCEL"
|
|
140
|
+
symbol: str | None = None,
|
|
141
|
+
ticket_id: str | None = None,
|
|
142
|
+
from_ms: int | None = None,
|
|
143
|
+
to_ms: int | None = None,
|
|
144
|
+
limit: int = 100,
|
|
145
|
+
) -> list[Ticket]
|
|
146
|
+
|
|
147
|
+
bf.cancel(ticket_id: str) -> None
|
|
148
|
+
# Raises TicketNotFound if ticket doesn't exist.
|
|
149
|
+
|
|
150
|
+
bf.cancel_all() -> int
|
|
151
|
+
# Returns number of cancelled tickets.
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`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`
|
|
155
|
+
|
|
156
|
+
**Auto-cancel**: placing a new ticket for the same `exchange+symbol` automatically cancels existing `NEW` tickets for that pair.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### Context Manager
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
with Blockfill(binary_path=..., data_dir=...) as bf:
|
|
164
|
+
bf.start(env={"BLOCKFILL_API_KEY": "..."})
|
|
165
|
+
ticket = bf.place(...)
|
|
166
|
+
# daemon is stopped on exit
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### Exceptions
|
|
172
|
+
|
|
173
|
+
| Exception | When |
|
|
174
|
+
| ------------------------- | --------------------------------------------- |
|
|
175
|
+
| `BinaryNotFound` | binary not found, call `install()` first |
|
|
176
|
+
| `DaemonNotRunning` | daemon socket not found or unreachable |
|
|
177
|
+
| `DaemonStartTimeout` | `start()` timed out waiting for daemon |
|
|
178
|
+
| `RpcError(code, message)` | daemon returned a JSON-RPC error |
|
|
179
|
+
| `TicketNotFound` | `cancel()` called with non-existent ticket_id |
|
|
180
|
+
| `CredentialsError` | invalid exchange name in `set_credentials()` |
|
|
181
|
+
| `InstallError` | binary download failed |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Patterns
|
|
186
|
+
|
|
187
|
+
### Strategy system integration
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from blockfill import Blockfill, DaemonNotRunning
|
|
191
|
+
|
|
192
|
+
bf = Blockfill(binary_path="/path/to/blockfill")
|
|
193
|
+
|
|
194
|
+
# On startup
|
|
195
|
+
if not bf.is_running():
|
|
196
|
+
bf.start(env={"BLOCKFILL_API_KEY": "..."})
|
|
197
|
+
bf.health()
|
|
198
|
+
|
|
199
|
+
# On each signal
|
|
200
|
+
ticket = bf.place(
|
|
201
|
+
exchange="binance-futures",
|
|
202
|
+
symbol=symbol,
|
|
203
|
+
strategy="maker_l2",
|
|
204
|
+
target_position=position,
|
|
205
|
+
time_constraint_ms=300_000,
|
|
206
|
+
)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Idempotent placement (safe to retry)
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
import uuid
|
|
213
|
+
|
|
214
|
+
key = str(uuid.uuid4()) # generate once, reuse on retry
|
|
215
|
+
|
|
216
|
+
ticket = bf.place(
|
|
217
|
+
exchange="binance-futures",
|
|
218
|
+
symbol="btcusdt",
|
|
219
|
+
strategy="maker_l2",
|
|
220
|
+
target_position=0.1,
|
|
221
|
+
time_constraint_ms=300_000,
|
|
222
|
+
client_ticket_id=key,
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Check open positions
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
open_tickets = bf.query(status="NEW") + bf.query(status="OPEN")
|
|
230
|
+
for t in open_tickets:
|
|
231
|
+
print(t.symbol, t.target_position, t.executed_position)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## AI Agent Usage
|
|
237
|
+
|
|
238
|
+
The SDK is designed for programmatic use by AI agents (Claude, GPT, etc.).
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
import sys
|
|
242
|
+
sys.path.insert(0, "/path/to/python-sdk")
|
|
243
|
+
|
|
244
|
+
import os
|
|
245
|
+
os.environ["BLOCKFILL_API_KEY"] = "your_api_key"
|
|
246
|
+
|
|
247
|
+
from blockfill import Blockfill, DaemonNotRunning, TicketNotFound
|
|
248
|
+
|
|
249
|
+
bf = Blockfill(
|
|
250
|
+
binary_path="/path/to/blockfill",
|
|
251
|
+
data_dir="/path/to/data_dir",
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
if not bf.is_running():
|
|
255
|
+
bf.start(env={"BLOCKFILL_API_KEY": os.environ["BLOCKFILL_API_KEY"]})
|
|
256
|
+
|
|
257
|
+
bf.health() # raises if daemon not ready
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Key points for agents:
|
|
261
|
+
|
|
262
|
+
- Always call `bf.health()` before placing tickets
|
|
263
|
+
- Use `client_ticket_id=uuid.uuid4()` for idempotent retries
|
|
264
|
+
- `place()` auto-cancels existing NEW tickets for the same exchange+symbol
|
|
265
|
+
- `query()` returns data from qtex MongoDB (not exchange realtime)
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Directory Structure
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
~/.blockfill/
|
|
273
|
+
├── config.toml # credentials + qtex endpoint (chmod 0600)
|
|
274
|
+
├── bin/
|
|
275
|
+
│ └── blockfill # binary
|
|
276
|
+
├── runtime/
|
|
277
|
+
│ ├── daemon.sock # UDS socket (CLI ↔ daemon IPC)
|
|
278
|
+
│ ├── daemon.pid # PID file
|
|
279
|
+
│ └── daemon.log # daemon logs
|
|
280
|
+
└── trading-log/ # Channel B upload retry buffer
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Supported Exchanges
|
|
284
|
+
|
|
285
|
+
| Exchange | Value |
|
|
286
|
+
| --------------- | ------------------- |
|
|
287
|
+
| Binance Futures | `"binance-futures"` |
|
|
288
|
+
| OKX Swap | `"okx-swap"` |
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from .client import Blockfill
|
|
2
|
+
from .exceptions import (
|
|
3
|
+
BinaryNotFound,
|
|
4
|
+
CredentialsError,
|
|
5
|
+
DaemonNotRunning,
|
|
6
|
+
DaemonStartTimeout,
|
|
7
|
+
InstallError,
|
|
8
|
+
RpcError,
|
|
9
|
+
TicketNotFound,
|
|
10
|
+
)
|
|
11
|
+
from .models import DaemonStatus, Ticket
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Blockfill",
|
|
15
|
+
"Ticket",
|
|
16
|
+
"DaemonStatus",
|
|
17
|
+
"BinaryNotFound",
|
|
18
|
+
"CredentialsError",
|
|
19
|
+
"DaemonNotRunning",
|
|
20
|
+
"DaemonStartTimeout",
|
|
21
|
+
"InstallError",
|
|
22
|
+
"RpcError",
|
|
23
|
+
"TicketNotFound",
|
|
24
|
+
]
|