probable-trader 1.0.0
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.
- package/README.md +118 -0
- package/bin/probable-trader.js +64 -0
- package/lib/setup.js +125 -0
- package/package.json +30 -0
- package/requirements.txt +6 -0
- package/schemas/action-output.schema.json +50 -0
- package/schemas/config.schema.json +49 -0
- package/schemas/order-intent.schema.json +47 -0
- package/scripts/lib/__init__.py +1 -0
- package/scripts/lib/client_wrapper.py +241 -0
- package/scripts/lib/config.py +150 -0
- package/scripts/lib/db.py +176 -0
- package/scripts/lib/onboard.py +177 -0
- package/scripts/lib/report.py +105 -0
- package/scripts/lib/safety.py +76 -0
- package/scripts/lib/ws_client.py +109 -0
- package/scripts/prob.py +474 -0
package/scripts/prob.py
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Probable Markets CLI — Agent-native trading skill for BSC prediction markets.
|
|
3
|
+
|
|
4
|
+
Usage: python3 scripts/prob.py <command> [options]
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
# Ensure lib is importable
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
15
|
+
|
|
16
|
+
from lib.config import ProbableConfig, load_config, init_config_dir
|
|
17
|
+
from lib.safety import ActionResult, require_confirm, output_result
|
|
18
|
+
from lib.db import ProbableDB
|
|
19
|
+
from lib.client_wrapper import ProbableClient
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_client(args) -> ProbableClient:
|
|
23
|
+
cfg = load_config()
|
|
24
|
+
db = ProbableDB(cfg.db_path)
|
|
25
|
+
return ProbableClient(cfg=cfg, db=db)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ── Config commands ──────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
def cmd_config_show(args):
|
|
31
|
+
cfg = load_config()
|
|
32
|
+
result = ActionResult(success=True, action="config.show", data=cfg.to_dict(mask_secrets=True))
|
|
33
|
+
output_result(result, args.json)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def cmd_config_init(args):
|
|
37
|
+
path = init_config_dir()
|
|
38
|
+
result = ActionResult(success=True, action="config.init", data={"path": str(path)})
|
|
39
|
+
output_result(result, args.json)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ── Doctor ───────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
def cmd_doctor(args):
|
|
45
|
+
client = get_client(args)
|
|
46
|
+
result = client.doctor()
|
|
47
|
+
output_result(result, args.json)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ── Setup (onboard) ─────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
def cmd_setup(args):
|
|
53
|
+
client = get_client(args)
|
|
54
|
+
|
|
55
|
+
# Doctor first
|
|
56
|
+
doc = client.doctor()
|
|
57
|
+
if not doc.success:
|
|
58
|
+
output_result(doc, args.json)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
blocked = require_confirm("setup", {"action": "enable_trading (approve tokens)"}, args.confirm, args.dry_run)
|
|
62
|
+
if blocked:
|
|
63
|
+
output_result(blocked, args.json)
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
result = client.enable_trading()
|
|
67
|
+
output_result(result, args.json)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ── Onboard ──────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
def cmd_onboard(args):
|
|
73
|
+
cfg = load_config()
|
|
74
|
+
details = {"action": "Generate API key via L1 auth (EIP-712 signing)"}
|
|
75
|
+
blocked = require_confirm("onboard", details, args.confirm, args.dry_run)
|
|
76
|
+
if blocked:
|
|
77
|
+
output_result(blocked, args.json)
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
from lib.onboard import run_onboard
|
|
81
|
+
result = run_onboard(cfg)
|
|
82
|
+
output_result(result, args.json)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ── Market commands ──────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
def cmd_market_list(args):
|
|
88
|
+
client = get_client(args)
|
|
89
|
+
result = client.get_markets(
|
|
90
|
+
topic_type=args.type,
|
|
91
|
+
page=args.page,
|
|
92
|
+
limit=args.limit,
|
|
93
|
+
status=args.status,
|
|
94
|
+
sort_by=args.sort_by,
|
|
95
|
+
)
|
|
96
|
+
output_result(result, args.json)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def cmd_market_get(args):
|
|
100
|
+
client = get_client(args)
|
|
101
|
+
result = client.get_market(int(args.market_id))
|
|
102
|
+
output_result(result, args.json)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def cmd_market_search(args):
|
|
106
|
+
client = get_client(args)
|
|
107
|
+
# Search is implemented as filtered list — get all and filter client-side
|
|
108
|
+
result = client.get_markets(page=1, limit=20)
|
|
109
|
+
if result.success and result.data:
|
|
110
|
+
query = args.query.lower()
|
|
111
|
+
items = result.data if isinstance(result.data, list) else result.data.get("items", result.data.get("data", []))
|
|
112
|
+
if isinstance(items, list):
|
|
113
|
+
filtered = [m for m in items if query in json.dumps(m, default=str).lower()]
|
|
114
|
+
result = ActionResult(success=True, action="market.search",
|
|
115
|
+
data={"query": args.query, "results": filtered, "count": len(filtered)})
|
|
116
|
+
output_result(result, args.json)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# ── Book / Price / History / Fee ─────────────────────────────────
|
|
120
|
+
|
|
121
|
+
def cmd_book(args):
|
|
122
|
+
client = get_client(args)
|
|
123
|
+
result = client.get_orderbook(args.token_id)
|
|
124
|
+
output_result(result, args.json)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def cmd_price(args):
|
|
128
|
+
client = get_client(args)
|
|
129
|
+
result = client.get_latest_price(args.token_id)
|
|
130
|
+
output_result(result, args.json)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def cmd_history(args):
|
|
134
|
+
client = get_client(args)
|
|
135
|
+
result = client.get_price_history(args.token_id, interval=args.interval)
|
|
136
|
+
output_result(result, args.json)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def cmd_fee_rates(args):
|
|
140
|
+
client = get_client(args)
|
|
141
|
+
result = client.get_fee_rates(args.token_id)
|
|
142
|
+
output_result(result, args.json)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ── Order commands ───────────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
def cmd_order_place(args):
|
|
148
|
+
client = get_client(args)
|
|
149
|
+
details = {
|
|
150
|
+
"market_id": args.market_id, "token_id": args.token_id,
|
|
151
|
+
"side": args.side, "price": args.price, "amount": args.amount,
|
|
152
|
+
"order_type": args.type or "limit",
|
|
153
|
+
}
|
|
154
|
+
blocked = require_confirm("order.place", details, args.confirm, args.dry_run)
|
|
155
|
+
if blocked:
|
|
156
|
+
output_result(blocked, args.json)
|
|
157
|
+
return
|
|
158
|
+
result = client.place_order(
|
|
159
|
+
market_id=int(args.market_id), token_id=args.token_id,
|
|
160
|
+
side=args.side, price=args.price, amount=args.amount,
|
|
161
|
+
order_type=args.type or "limit",
|
|
162
|
+
)
|
|
163
|
+
output_result(result, args.json)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def cmd_order_get(args):
|
|
167
|
+
client = get_client(args)
|
|
168
|
+
result = client.get_order_by_id(args.order_id)
|
|
169
|
+
output_result(result, args.json)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def cmd_order_list(args):
|
|
173
|
+
client = get_client(args)
|
|
174
|
+
result = client.get_my_orders(
|
|
175
|
+
market_id=int(args.market_id) if args.market_id else 0,
|
|
176
|
+
status=args.status or "",
|
|
177
|
+
limit=10, page=1,
|
|
178
|
+
)
|
|
179
|
+
output_result(result, args.json)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def cmd_order_cancel(args):
|
|
183
|
+
client = get_client(args)
|
|
184
|
+
blocked = require_confirm("order.cancel", {"order_id": args.order_id}, args.confirm, args.dry_run)
|
|
185
|
+
if blocked:
|
|
186
|
+
output_result(blocked, args.json)
|
|
187
|
+
return
|
|
188
|
+
result = client.cancel_order(args.order_id)
|
|
189
|
+
output_result(result, args.json)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def cmd_order_cancel_all(args):
|
|
193
|
+
client = get_client(args)
|
|
194
|
+
details = {"market_id": args.market_id}
|
|
195
|
+
blocked = require_confirm("order.cancel_all", details, args.confirm, args.dry_run)
|
|
196
|
+
if blocked:
|
|
197
|
+
output_result(blocked, args.json)
|
|
198
|
+
return
|
|
199
|
+
mid = int(args.market_id) if args.market_id else None
|
|
200
|
+
result = client.cancel_all_orders(market_id=mid)
|
|
201
|
+
output_result(result, args.json)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# ── Positions / Trades / Balance ─────────────────────────────────
|
|
205
|
+
|
|
206
|
+
def cmd_positions(args):
|
|
207
|
+
client = get_client(args)
|
|
208
|
+
mid = int(args.market_id) if args.market_id else 0
|
|
209
|
+
result = client.get_my_positions(market_id=mid)
|
|
210
|
+
output_result(result, args.json)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def cmd_trades(args):
|
|
214
|
+
client = get_client(args)
|
|
215
|
+
mid = int(args.market_id) if args.market_id else None
|
|
216
|
+
result = client.get_my_trades(market_id=mid, page=args.page)
|
|
217
|
+
output_result(result, args.json)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def cmd_balance(args):
|
|
221
|
+
client = get_client(args)
|
|
222
|
+
result = client.get_my_balances()
|
|
223
|
+
output_result(result, args.json)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# ── Split / Merge / Redeem ───────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
def cmd_split(args):
|
|
229
|
+
client = get_client(args)
|
|
230
|
+
details = {"market_id": args.market_id, "amount": args.amount}
|
|
231
|
+
blocked = require_confirm("split", details, args.confirm, args.dry_run)
|
|
232
|
+
if blocked:
|
|
233
|
+
output_result(blocked, args.json)
|
|
234
|
+
return
|
|
235
|
+
result = client.split(int(args.market_id), int(args.amount))
|
|
236
|
+
output_result(result, args.json)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def cmd_merge(args):
|
|
240
|
+
client = get_client(args)
|
|
241
|
+
details = {"market_id": args.market_id, "amount": args.amount}
|
|
242
|
+
blocked = require_confirm("merge", details, args.confirm, args.dry_run)
|
|
243
|
+
if blocked:
|
|
244
|
+
output_result(blocked, args.json)
|
|
245
|
+
return
|
|
246
|
+
result = client.merge(int(args.market_id), int(args.amount))
|
|
247
|
+
output_result(result, args.json)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def cmd_redeem(args):
|
|
251
|
+
client = get_client(args)
|
|
252
|
+
details = {"market_id": args.market_id}
|
|
253
|
+
blocked = require_confirm("redeem", details, args.confirm, args.dry_run)
|
|
254
|
+
if blocked:
|
|
255
|
+
output_result(blocked, args.json)
|
|
256
|
+
return
|
|
257
|
+
result = client.redeem(int(args.market_id))
|
|
258
|
+
output_result(result, args.json)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# ── WebSocket commands ───────────────────────────────────────────
|
|
262
|
+
|
|
263
|
+
def cmd_ws_book(args):
|
|
264
|
+
from lib.ws_client import subscribe_book
|
|
265
|
+
asyncio.run(subscribe_book(args.token_id, duration=args.duration, json_mode=args.json))
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def cmd_ws_user(args):
|
|
269
|
+
cfg = load_config()
|
|
270
|
+
from lib.ws_client import subscribe_user
|
|
271
|
+
asyncio.run(subscribe_user(cfg, duration=args.duration, json_mode=args.json))
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# ── Report command ───────────────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
def cmd_report_daily(args):
|
|
277
|
+
cfg = load_config()
|
|
278
|
+
db = ProbableDB(cfg.db_path)
|
|
279
|
+
from lib.report import generate_daily_report
|
|
280
|
+
result = generate_daily_report(db, fmt=args.format)
|
|
281
|
+
if args.output:
|
|
282
|
+
Path(args.output).write_text(result.to_json() if args.json else json.dumps(result.data, indent=2, default=str))
|
|
283
|
+
print(f"Report written to {args.output}")
|
|
284
|
+
else:
|
|
285
|
+
output_result(result, args.json)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
# ── Argument parser ──────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
291
|
+
# Shared parent with global flags — allows --json/--confirm/--dry-run anywhere
|
|
292
|
+
parent = argparse.ArgumentParser(add_help=False)
|
|
293
|
+
parent.add_argument("--json", action="store_true", help="JSON-only output")
|
|
294
|
+
parent.add_argument("--confirm", action="store_true", help="Confirm destructive actions")
|
|
295
|
+
parent.add_argument("--dry-run", action="store_true", help="Preview without executing")
|
|
296
|
+
|
|
297
|
+
p = argparse.ArgumentParser(prog="prob.py", description="Probable Markets CLI", parents=[parent])
|
|
298
|
+
|
|
299
|
+
sub = p.add_subparsers(dest="command", help="Available commands")
|
|
300
|
+
|
|
301
|
+
# config
|
|
302
|
+
cfg_p = sub.add_parser("config", help="Configuration", parents=[parent])
|
|
303
|
+
cfg_sub = cfg_p.add_subparsers(dest="config_cmd")
|
|
304
|
+
cfg_sub.add_parser("show", help="Show current config", parents=[parent])
|
|
305
|
+
cfg_sub.add_parser("init", help="Create .probable/ directory", parents=[parent])
|
|
306
|
+
|
|
307
|
+
# doctor / onboard / setup
|
|
308
|
+
sub.add_parser("doctor", help="Health check", parents=[parent])
|
|
309
|
+
sub.add_parser("onboard", help="Generate API key (L1 auth, only needs private key)", parents=[parent])
|
|
310
|
+
sub.add_parser("setup", help="Enable trading approvals (on-chain)", parents=[parent])
|
|
311
|
+
|
|
312
|
+
# market
|
|
313
|
+
mkt_p = sub.add_parser("market", help="Market discovery", parents=[parent])
|
|
314
|
+
mkt_sub = mkt_p.add_subparsers(dest="market_cmd")
|
|
315
|
+
|
|
316
|
+
mkt_list = mkt_sub.add_parser("list", help="List markets", parents=[parent])
|
|
317
|
+
mkt_list.add_argument("--type", choices=["binary", "categorical", "all"], default=None)
|
|
318
|
+
mkt_list.add_argument("--status", choices=["activated", "resolved"], default=None)
|
|
319
|
+
mkt_list.add_argument("--sort-by", dest="sort_by",
|
|
320
|
+
choices=["time", "volume", "volume_24h", "volume_7d", "cutoff"], default=None)
|
|
321
|
+
mkt_list.add_argument("--page", type=int, default=1)
|
|
322
|
+
mkt_list.add_argument("--limit", type=int, default=20)
|
|
323
|
+
|
|
324
|
+
mkt_get = mkt_sub.add_parser("get", help="Get market details", parents=[parent])
|
|
325
|
+
mkt_get.add_argument("market_id", help="Market ID")
|
|
326
|
+
|
|
327
|
+
mkt_search = mkt_sub.add_parser("search", help="Search markets", parents=[parent])
|
|
328
|
+
mkt_search.add_argument("query", help="Search query")
|
|
329
|
+
|
|
330
|
+
# book
|
|
331
|
+
book_p = sub.add_parser("book", help="Orderbook snapshot", parents=[parent])
|
|
332
|
+
book_p.add_argument("token_id", help="Token ID")
|
|
333
|
+
|
|
334
|
+
# price
|
|
335
|
+
price_p = sub.add_parser("price", help="Latest price", parents=[parent])
|
|
336
|
+
price_p.add_argument("token_id", help="Token ID")
|
|
337
|
+
|
|
338
|
+
# history
|
|
339
|
+
hist_p = sub.add_parser("history", help="Price history", parents=[parent])
|
|
340
|
+
hist_p.add_argument("token_id", help="Token ID")
|
|
341
|
+
hist_p.add_argument("--interval", choices=["1m", "1h", "1d", "1w", "max"], default="1h")
|
|
342
|
+
|
|
343
|
+
# fee-rates
|
|
344
|
+
fee_p = sub.add_parser("fee-rates", help="Fee rates from chain", parents=[parent])
|
|
345
|
+
fee_p.add_argument("token_id", help="Token ID")
|
|
346
|
+
|
|
347
|
+
# order
|
|
348
|
+
ord_p = sub.add_parser("order", help="Order management", parents=[parent])
|
|
349
|
+
ord_sub = ord_p.add_subparsers(dest="order_cmd")
|
|
350
|
+
|
|
351
|
+
ord_place = ord_sub.add_parser("place", help="Place order", parents=[parent])
|
|
352
|
+
ord_place.add_argument("--market-id", required=True, dest="market_id")
|
|
353
|
+
ord_place.add_argument("--token-id", required=True, dest="token_id")
|
|
354
|
+
ord_place.add_argument("--side", required=True, choices=["BUY", "SELL"])
|
|
355
|
+
ord_place.add_argument("--price", required=True)
|
|
356
|
+
ord_place.add_argument("--amount", required=True)
|
|
357
|
+
ord_place.add_argument("--type", choices=["limit", "market"], default="limit")
|
|
358
|
+
|
|
359
|
+
ord_get = ord_sub.add_parser("get", help="Get order details", parents=[parent])
|
|
360
|
+
ord_get.add_argument("order_id", help="Order ID")
|
|
361
|
+
|
|
362
|
+
ord_list = ord_sub.add_parser("list", help="List my orders", parents=[parent])
|
|
363
|
+
ord_list.add_argument("--market-id", dest="market_id", default=None)
|
|
364
|
+
ord_list.add_argument("--status", choices=["pending", "filled", "cancelled"], default=None)
|
|
365
|
+
|
|
366
|
+
ord_cancel = ord_sub.add_parser("cancel", help="Cancel order", parents=[parent])
|
|
367
|
+
ord_cancel.add_argument("order_id", help="Order ID")
|
|
368
|
+
|
|
369
|
+
ord_cancel_all = ord_sub.add_parser("cancel-all", help="Cancel all orders", parents=[parent])
|
|
370
|
+
ord_cancel_all.add_argument("--market-id", dest="market_id", default=None)
|
|
371
|
+
|
|
372
|
+
# positions / trades / balance
|
|
373
|
+
pos_p = sub.add_parser("positions", help="My positions", parents=[parent])
|
|
374
|
+
pos_p.add_argument("--market-id", dest="market_id", default=None)
|
|
375
|
+
|
|
376
|
+
trades_p = sub.add_parser("trades", help="My trades", parents=[parent])
|
|
377
|
+
trades_p.add_argument("--market-id", dest="market_id", default=None)
|
|
378
|
+
trades_p.add_argument("--page", type=int, default=1)
|
|
379
|
+
|
|
380
|
+
sub.add_parser("balance", help="My balances", parents=[parent])
|
|
381
|
+
|
|
382
|
+
# split / merge / redeem
|
|
383
|
+
split_p = sub.add_parser("split", help="Split collateral into outcome tokens", parents=[parent])
|
|
384
|
+
split_p.add_argument("--market-id", required=True, dest="market_id")
|
|
385
|
+
split_p.add_argument("--amount", required=True)
|
|
386
|
+
|
|
387
|
+
merge_p = sub.add_parser("merge", help="Merge outcome tokens into collateral", parents=[parent])
|
|
388
|
+
merge_p.add_argument("--market-id", required=True, dest="market_id")
|
|
389
|
+
merge_p.add_argument("--amount", required=True)
|
|
390
|
+
|
|
391
|
+
redeem_p = sub.add_parser("redeem", help="Redeem resolved market", parents=[parent])
|
|
392
|
+
redeem_p.add_argument("--market-id", required=True, dest="market_id")
|
|
393
|
+
|
|
394
|
+
# ws
|
|
395
|
+
ws_p = sub.add_parser("ws", help="WebSocket streams", parents=[parent])
|
|
396
|
+
ws_sub = ws_p.add_subparsers(dest="ws_cmd")
|
|
397
|
+
|
|
398
|
+
ws_book = ws_sub.add_parser("book", help="Stream orderbook updates", parents=[parent])
|
|
399
|
+
ws_book.add_argument("token_id", help="Token ID")
|
|
400
|
+
ws_book.add_argument("--duration", type=int, default=60, help="Duration in seconds")
|
|
401
|
+
|
|
402
|
+
ws_user = ws_sub.add_parser("user", help="Stream execution reports", parents=[parent])
|
|
403
|
+
ws_user.add_argument("--duration", type=int, default=60, help="Duration in seconds")
|
|
404
|
+
|
|
405
|
+
# report
|
|
406
|
+
rpt_p = sub.add_parser("report", help="Trading reports", parents=[parent])
|
|
407
|
+
rpt_sub = rpt_p.add_subparsers(dest="report_cmd")
|
|
408
|
+
|
|
409
|
+
rpt_daily = rpt_sub.add_parser("daily", help="Daily trading report", parents=[parent])
|
|
410
|
+
rpt_daily.add_argument("--format", choices=["markdown", "json", "both"], default="markdown")
|
|
411
|
+
rpt_daily.add_argument("--output", default=None, help="Output file path")
|
|
412
|
+
|
|
413
|
+
return p
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
DISPATCH = {
|
|
417
|
+
("config", "show"): cmd_config_show,
|
|
418
|
+
("config", "init"): cmd_config_init,
|
|
419
|
+
("doctor", None): cmd_doctor,
|
|
420
|
+
("onboard", None): cmd_onboard,
|
|
421
|
+
("setup", None): cmd_setup,
|
|
422
|
+
("market", "list"): cmd_market_list,
|
|
423
|
+
("market", "get"): cmd_market_get,
|
|
424
|
+
("market", "search"): cmd_market_search,
|
|
425
|
+
("book", None): cmd_book,
|
|
426
|
+
("price", None): cmd_price,
|
|
427
|
+
("history", None): cmd_history,
|
|
428
|
+
("fee-rates", None): cmd_fee_rates,
|
|
429
|
+
("order", "place"): cmd_order_place,
|
|
430
|
+
("order", "get"): cmd_order_get,
|
|
431
|
+
("order", "list"): cmd_order_list,
|
|
432
|
+
("order", "cancel"): cmd_order_cancel,
|
|
433
|
+
("order", "cancel-all"): cmd_order_cancel_all,
|
|
434
|
+
("positions", None): cmd_positions,
|
|
435
|
+
("trades", None): cmd_trades,
|
|
436
|
+
("balance", None): cmd_balance,
|
|
437
|
+
("split", None): cmd_split,
|
|
438
|
+
("merge", None): cmd_merge,
|
|
439
|
+
("redeem", None): cmd_redeem,
|
|
440
|
+
("ws", "book"): cmd_ws_book,
|
|
441
|
+
("ws", "user"): cmd_ws_user,
|
|
442
|
+
("report", "daily"): cmd_report_daily,
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def main():
|
|
447
|
+
parser = build_parser()
|
|
448
|
+
args = parser.parse_args()
|
|
449
|
+
|
|
450
|
+
if not args.command:
|
|
451
|
+
parser.print_help()
|
|
452
|
+
sys.exit(1)
|
|
453
|
+
|
|
454
|
+
# Determine sub-command
|
|
455
|
+
sub_cmd = None
|
|
456
|
+
for attr in ("config_cmd", "market_cmd", "order_cmd", "ws_cmd", "report_cmd"):
|
|
457
|
+
if hasattr(args, attr):
|
|
458
|
+
sub_cmd = getattr(args, attr)
|
|
459
|
+
break
|
|
460
|
+
|
|
461
|
+
key = (args.command, sub_cmd)
|
|
462
|
+
fn = DISPATCH.get(key)
|
|
463
|
+
if fn is None:
|
|
464
|
+
# Try command-only dispatch
|
|
465
|
+
fn = DISPATCH.get((args.command, None))
|
|
466
|
+
if fn is None:
|
|
467
|
+
parser.print_help()
|
|
468
|
+
sys.exit(1)
|
|
469
|
+
|
|
470
|
+
fn(args)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
if __name__ == "__main__":
|
|
474
|
+
main()
|