fere-sdk 0.4.0.dev31__tar.gz → 0.5.0.dev35__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.
- {fere_sdk-0.4.0.dev31 → fere_sdk-0.5.0.dev35}/.gitignore +11 -0
- {fere_sdk-0.4.0.dev31 → fere_sdk-0.5.0.dev35}/PKG-INFO +1 -1
- {fere_sdk-0.4.0.dev31 → fere_sdk-0.5.0.dev35}/pyproject.toml +1 -1
- {fere_sdk-0.4.0.dev31 → fere_sdk-0.5.0.dev35}/src/fere_sdk/client.py +119 -0
- {fere_sdk-0.4.0.dev31 → fere_sdk-0.5.0.dev35}/src/fere_sdk/low_level.py +110 -0
- {fere_sdk-0.4.0.dev31 → fere_sdk-0.5.0.dev35}/src/fere_sdk/models.py +14 -0
- fere_sdk-0.5.0.dev35/src/fere_sdk/perp_models.py +115 -0
- {fere_sdk-0.4.0.dev31 → fere_sdk-0.5.0.dev35}/README.md +0 -0
- {fere_sdk-0.4.0.dev31 → fere_sdk-0.5.0.dev35}/src/fere_sdk/__init__.py +0 -0
- {fere_sdk-0.4.0.dev31 → fere_sdk-0.5.0.dev35}/src/fere_sdk/auth.py +0 -0
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
*.pyc
|
|
3
3
|
__pycache__
|
|
4
4
|
|
|
5
|
+
# pnpm local store (when set up at repo root)
|
|
6
|
+
.pnpm-store/
|
|
7
|
+
|
|
5
8
|
# Devcontainer local env (contains secrets)
|
|
6
9
|
.devcontainer/.env.local
|
|
7
10
|
.devcontainer/.env.local.override
|
|
11
|
+
.devcontainer/.env.runtime.local
|
|
12
|
+
.devcontainer/.env.runtime.*.local
|
|
8
13
|
.devcontainer/.env.gitpod
|
|
9
14
|
|
|
10
15
|
# Bruno gateway collection (GATEWAY_AGENT_ID / GATEWAY_PRIVATE_KEY_B64)
|
|
@@ -24,6 +29,9 @@ coverage.xml
|
|
|
24
29
|
# Claude Code per-session scheduler lock (local runtime state)
|
|
25
30
|
.claude/scheduled_tasks.lock
|
|
26
31
|
|
|
32
|
+
# Claude Code temporary task checklist scratchpad (deleted on completion)
|
|
33
|
+
.claude/current-task.md
|
|
34
|
+
|
|
27
35
|
# Ralph/autonomous loop runtime logs
|
|
28
36
|
dev/logs/*
|
|
29
37
|
!dev/logs/.gitignore
|
|
@@ -36,6 +44,8 @@ dev/logs/*
|
|
|
36
44
|
|
|
37
45
|
# Cursor SEO skill — regenerated report JSON (not source)
|
|
38
46
|
.cursor/skills/seo-agent/artifacts/
|
|
47
|
+
.cursor/skills/docker-images-cve-scan/artifacts/
|
|
48
|
+
docs/plans/artifacts/
|
|
39
49
|
|
|
40
50
|
# Skills installed locally by the skills package manager (see skills-lock.json)
|
|
41
51
|
.claude/skills/stripe-best-practices
|
|
@@ -45,6 +55,7 @@ dev/logs/*
|
|
|
45
55
|
|
|
46
56
|
# agentmemory iii state (SQLite + streams under .agentmemory/data/)
|
|
47
57
|
.agentmemory/data/
|
|
58
|
+
.devcontainer/agentmemory-mcp/node_modules/
|
|
48
59
|
|
|
49
60
|
# Local GEO upstream reference (optional clone; not part of the product tree)
|
|
50
61
|
geo-seo/
|
|
@@ -22,6 +22,11 @@ from .models import (
|
|
|
22
22
|
DepositRequest,
|
|
23
23
|
HooksRequest,
|
|
24
24
|
LimitOrderRequest,
|
|
25
|
+
PerpCancelRequest,
|
|
26
|
+
PerpCloseRequest,
|
|
27
|
+
PerpFundRequest,
|
|
28
|
+
PerpOpenRequest,
|
|
29
|
+
PerpWithdrawRequest,
|
|
25
30
|
SecurityCheckResponse,
|
|
26
31
|
SwapRequest,
|
|
27
32
|
TokenToCheck,
|
|
@@ -274,10 +279,124 @@ class FereClient:
|
|
|
274
279
|
req = WithdrawRequest(position_id=position_id, amount_usdc=amount_usdc)
|
|
275
280
|
return await self._api.withdraw(req, wait=True, timeout=timeout)
|
|
276
281
|
|
|
282
|
+
# ----------------------------------------------------------
|
|
283
|
+
# Perp — venue-neutral perpetual futures (blocks until complete)
|
|
284
|
+
# ----------------------------------------------------------
|
|
285
|
+
|
|
286
|
+
async def perp_open(
|
|
287
|
+
self,
|
|
288
|
+
asset: str,
|
|
289
|
+
is_buy: bool,
|
|
290
|
+
size: float,
|
|
291
|
+
leverage: int = 1,
|
|
292
|
+
is_cross: bool = True,
|
|
293
|
+
order_type: str = "market",
|
|
294
|
+
limit_price: float | None = None,
|
|
295
|
+
slippage_pct: float = 0.5,
|
|
296
|
+
reduce_only: bool = False,
|
|
297
|
+
tp_price: float | None = None,
|
|
298
|
+
sl_price: float | None = None,
|
|
299
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
300
|
+
) -> dict:
|
|
301
|
+
"""Open a perpetual position and wait for completion.
|
|
302
|
+
|
|
303
|
+
``size`` is in contracts (not smallest units).
|
|
304
|
+
"""
|
|
305
|
+
req = PerpOpenRequest(
|
|
306
|
+
asset=asset,
|
|
307
|
+
is_buy=is_buy,
|
|
308
|
+
size=size,
|
|
309
|
+
leverage=leverage,
|
|
310
|
+
is_cross=is_cross,
|
|
311
|
+
order_type=order_type,
|
|
312
|
+
limit_price=limit_price,
|
|
313
|
+
slippage_pct=slippage_pct,
|
|
314
|
+
reduce_only=reduce_only,
|
|
315
|
+
tp_price=tp_price,
|
|
316
|
+
sl_price=sl_price,
|
|
317
|
+
)
|
|
318
|
+
return await self._api.perp_open(req, wait=True, timeout=timeout)
|
|
319
|
+
|
|
320
|
+
async def perp_close(
|
|
321
|
+
self,
|
|
322
|
+
asset: str,
|
|
323
|
+
size: float | None = None,
|
|
324
|
+
slippage_pct: float = 0.5,
|
|
325
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
326
|
+
) -> dict:
|
|
327
|
+
"""Close a perpetual position and wait for completion."""
|
|
328
|
+
req = PerpCloseRequest(
|
|
329
|
+
asset=asset, size=size, slippage_pct=slippage_pct
|
|
330
|
+
)
|
|
331
|
+
return await self._api.perp_close(req, wait=True, timeout=timeout)
|
|
332
|
+
|
|
333
|
+
async def perp_cancel_order(
|
|
334
|
+
self,
|
|
335
|
+
asset: str,
|
|
336
|
+
order_id: int,
|
|
337
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
338
|
+
) -> dict:
|
|
339
|
+
"""Cancel an open perp order and wait for completion."""
|
|
340
|
+
req = PerpCancelRequest(asset=asset, order_id=order_id)
|
|
341
|
+
return await self._api.perp_cancel_order(
|
|
342
|
+
req, wait=True, timeout=timeout
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
async def perp_setup(
|
|
346
|
+
self,
|
|
347
|
+
force_rerun: bool = False,
|
|
348
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
349
|
+
) -> dict:
|
|
350
|
+
"""Provision perp trading and wait for completion."""
|
|
351
|
+
return await self._api.perp_setup(
|
|
352
|
+
force_rerun=force_rerun, wait=True, timeout=timeout
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
async def perp_fund(
|
|
356
|
+
self,
|
|
357
|
+
amount: str,
|
|
358
|
+
source_chain_id: int,
|
|
359
|
+
source_token: str | None = None,
|
|
360
|
+
display_amount_usd: float | None = None,
|
|
361
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
362
|
+
) -> dict:
|
|
363
|
+
"""Fund the perp account and wait for completion."""
|
|
364
|
+
req = PerpFundRequest(
|
|
365
|
+
amount=amount,
|
|
366
|
+
source_chain_id=source_chain_id,
|
|
367
|
+
source_token=source_token,
|
|
368
|
+
display_amount_usd=display_amount_usd,
|
|
369
|
+
)
|
|
370
|
+
return await self._api.perp_fund(req, wait=True, timeout=timeout)
|
|
371
|
+
|
|
372
|
+
async def perp_withdraw(
|
|
373
|
+
self,
|
|
374
|
+
amount_usd: float,
|
|
375
|
+
destination_chain_id: int,
|
|
376
|
+
destination_token: str | None = None,
|
|
377
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
378
|
+
) -> dict:
|
|
379
|
+
"""Withdraw perp collateral and wait for completion."""
|
|
380
|
+
req = PerpWithdrawRequest(
|
|
381
|
+
amount_usd=amount_usd,
|
|
382
|
+
destination_chain_id=destination_chain_id,
|
|
383
|
+
destination_token=destination_token,
|
|
384
|
+
)
|
|
385
|
+
return await self._api.perp_withdraw(req, wait=True, timeout=timeout)
|
|
386
|
+
|
|
277
387
|
# ----------------------------------------------------------
|
|
278
388
|
# Read-only (pass-through)
|
|
279
389
|
# ----------------------------------------------------------
|
|
280
390
|
|
|
391
|
+
async def get_perp_markets(self, search_text: str | None = None) -> dict:
|
|
392
|
+
return await self._api.get_perp_markets(search_text=search_text)
|
|
393
|
+
|
|
394
|
+
async def get_perp_setup_status(self) -> dict:
|
|
395
|
+
return await self._api.get_perp_setup_status()
|
|
396
|
+
|
|
397
|
+
async def get_perp_orders(self) -> dict:
|
|
398
|
+
return await self._api.get_perp_orders()
|
|
399
|
+
|
|
281
400
|
async def get_wallets(self) -> dict:
|
|
282
401
|
return await self._api.get_wallets()
|
|
283
402
|
|
|
@@ -16,6 +16,11 @@ from .models import (
|
|
|
16
16
|
DepositRequest,
|
|
17
17
|
HooksRequest,
|
|
18
18
|
LimitOrderRequest,
|
|
19
|
+
PerpCancelRequest,
|
|
20
|
+
PerpCloseRequest,
|
|
21
|
+
PerpFundRequest,
|
|
22
|
+
PerpOpenRequest,
|
|
23
|
+
PerpWithdrawRequest,
|
|
19
24
|
SecurityCheckResponse,
|
|
20
25
|
SecurityCheckSummary,
|
|
21
26
|
SwapRequest,
|
|
@@ -420,6 +425,111 @@ class FereAPI:
|
|
|
420
425
|
"""GET /v1/earn/positions"""
|
|
421
426
|
return await self._authed_get("/v1/earn/positions")
|
|
422
427
|
|
|
428
|
+
# ----------------------------------------------------------
|
|
429
|
+
# Perp (venue-neutral perpetual futures)
|
|
430
|
+
# ----------------------------------------------------------
|
|
431
|
+
|
|
432
|
+
async def get_perp_markets(self, search_text: str | None = None) -> dict:
|
|
433
|
+
"""GET /v1/perp/markets"""
|
|
434
|
+
params = {}
|
|
435
|
+
if search_text:
|
|
436
|
+
params["search_text"] = search_text
|
|
437
|
+
return await self._authed_get("/v1/perp/markets", params=params)
|
|
438
|
+
|
|
439
|
+
async def get_perp_setup_status(self) -> dict:
|
|
440
|
+
"""GET /v1/perp/setup"""
|
|
441
|
+
return await self._authed_get("/v1/perp/setup")
|
|
442
|
+
|
|
443
|
+
async def get_perp_orders(self) -> dict:
|
|
444
|
+
"""GET /v1/perp/orders"""
|
|
445
|
+
return await self._authed_get("/v1/perp/orders")
|
|
446
|
+
|
|
447
|
+
async def perp_setup(
|
|
448
|
+
self,
|
|
449
|
+
force_rerun: bool = False,
|
|
450
|
+
wait: bool = False,
|
|
451
|
+
timeout: int = 120,
|
|
452
|
+
) -> TaskResponse | dict:
|
|
453
|
+
"""POST /v1/perp/setup"""
|
|
454
|
+
return await self._perp_task(
|
|
455
|
+
"/v1/perp/setup", {"force_rerun": force_rerun}, wait, timeout
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
async def perp_open(
|
|
459
|
+
self,
|
|
460
|
+
request: PerpOpenRequest,
|
|
461
|
+
wait: bool = False,
|
|
462
|
+
timeout: int = 120,
|
|
463
|
+
) -> TaskResponse | dict:
|
|
464
|
+
"""POST /v1/perp/open"""
|
|
465
|
+
return await self._perp_task(
|
|
466
|
+
"/v1/perp/open", request.to_dict(), wait, timeout
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
async def perp_close(
|
|
470
|
+
self,
|
|
471
|
+
request: PerpCloseRequest,
|
|
472
|
+
wait: bool = False,
|
|
473
|
+
timeout: int = 120,
|
|
474
|
+
) -> TaskResponse | dict:
|
|
475
|
+
"""POST /v1/perp/close"""
|
|
476
|
+
return await self._perp_task(
|
|
477
|
+
"/v1/perp/close", request.to_dict(), wait, timeout
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
async def perp_cancel_order(
|
|
481
|
+
self,
|
|
482
|
+
request: PerpCancelRequest,
|
|
483
|
+
wait: bool = False,
|
|
484
|
+
timeout: int = 120,
|
|
485
|
+
) -> TaskResponse | dict:
|
|
486
|
+
"""POST /v1/perp/orders/cancel"""
|
|
487
|
+
return await self._perp_task(
|
|
488
|
+
"/v1/perp/orders/cancel", request.to_dict(), wait, timeout
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
async def perp_fund(
|
|
492
|
+
self,
|
|
493
|
+
request: PerpFundRequest,
|
|
494
|
+
wait: bool = False,
|
|
495
|
+
timeout: int = 120,
|
|
496
|
+
) -> TaskResponse | dict:
|
|
497
|
+
"""POST /v1/perp/fund"""
|
|
498
|
+
return await self._perp_task(
|
|
499
|
+
"/v1/perp/fund", request.to_dict(), wait, timeout
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
async def perp_withdraw(
|
|
503
|
+
self,
|
|
504
|
+
request: PerpWithdrawRequest,
|
|
505
|
+
wait: bool = False,
|
|
506
|
+
timeout: int = 120,
|
|
507
|
+
) -> TaskResponse | dict:
|
|
508
|
+
"""POST /v1/perp/withdraw"""
|
|
509
|
+
return await self._perp_task(
|
|
510
|
+
"/v1/perp/withdraw", request.to_dict(), wait, timeout
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
async def _perp_task(
|
|
514
|
+
self,
|
|
515
|
+
path: str,
|
|
516
|
+
body: dict,
|
|
517
|
+
wait: bool,
|
|
518
|
+
timeout: int,
|
|
519
|
+
) -> TaskResponse | dict:
|
|
520
|
+
params = {}
|
|
521
|
+
if wait:
|
|
522
|
+
params = {"wait": "true", "timeout": str(timeout)}
|
|
523
|
+
data = await self._authed_post(path, body, params=params)
|
|
524
|
+
if wait:
|
|
525
|
+
return data
|
|
526
|
+
return TaskResponse(
|
|
527
|
+
task_id=data["task_id"],
|
|
528
|
+
status=data.get("status", "queued"),
|
|
529
|
+
poll_url=data.get("poll_url"),
|
|
530
|
+
notification_stream=data.get("notification_stream"),
|
|
531
|
+
)
|
|
532
|
+
|
|
423
533
|
# ----------------------------------------------------------
|
|
424
534
|
# Tasks
|
|
425
535
|
# ----------------------------------------------------------
|
|
@@ -5,6 +5,15 @@ from __future__ import annotations
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
# Perp models live in their own module; re-exported here for back-compat.
|
|
9
|
+
from .perp_models import ( # noqa: E402,F401
|
|
10
|
+
PerpCancelRequest,
|
|
11
|
+
PerpCloseRequest,
|
|
12
|
+
PerpFundRequest,
|
|
13
|
+
PerpOpenRequest,
|
|
14
|
+
PerpWithdrawRequest,
|
|
15
|
+
)
|
|
16
|
+
|
|
8
17
|
__all__ = [
|
|
9
18
|
"TaskResponse",
|
|
10
19
|
"TaskStatusResponse",
|
|
@@ -18,6 +27,11 @@ __all__ = [
|
|
|
18
27
|
"HooksRequest",
|
|
19
28
|
"DepositRequest",
|
|
20
29
|
"WithdrawRequest",
|
|
30
|
+
"PerpOpenRequest",
|
|
31
|
+
"PerpCloseRequest",
|
|
32
|
+
"PerpCancelRequest",
|
|
33
|
+
"PerpFundRequest",
|
|
34
|
+
"PerpWithdrawRequest",
|
|
21
35
|
"TokenToCheck",
|
|
22
36
|
"TokenSecurityResult",
|
|
23
37
|
"SecurityCheckSummary",
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Venue-neutral perpetual-futures request models.
|
|
2
|
+
|
|
3
|
+
Kept in a dedicated module (rather than appended to ``models.py``) per the
|
|
4
|
+
team convention of giving new model groups their own file. Re-exported from
|
|
5
|
+
``models.py`` for backwards-compatible imports.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"PerpOpenRequest",
|
|
14
|
+
"PerpCloseRequest",
|
|
15
|
+
"PerpCancelRequest",
|
|
16
|
+
"PerpFundRequest",
|
|
17
|
+
"PerpWithdrawRequest",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class PerpOpenRequest:
|
|
23
|
+
"""Open a venue-neutral perpetual position. size is in contracts."""
|
|
24
|
+
|
|
25
|
+
asset: str
|
|
26
|
+
is_buy: bool
|
|
27
|
+
size: float
|
|
28
|
+
leverage: int = 1
|
|
29
|
+
is_cross: bool = True
|
|
30
|
+
order_type: str = "market"
|
|
31
|
+
limit_price: float | None = None
|
|
32
|
+
slippage_pct: float = 0.5
|
|
33
|
+
reduce_only: bool = False
|
|
34
|
+
tp_price: float | None = None
|
|
35
|
+
sl_price: float | None = None
|
|
36
|
+
tp_is_market: bool = True
|
|
37
|
+
sl_is_market: bool = True
|
|
38
|
+
|
|
39
|
+
def to_dict(self) -> dict:
|
|
40
|
+
d: dict = {
|
|
41
|
+
"asset": self.asset,
|
|
42
|
+
"is_buy": self.is_buy,
|
|
43
|
+
"size": self.size,
|
|
44
|
+
"leverage": self.leverage,
|
|
45
|
+
"is_cross": self.is_cross,
|
|
46
|
+
"order_type": self.order_type,
|
|
47
|
+
"slippage_pct": self.slippage_pct,
|
|
48
|
+
"reduce_only": self.reduce_only,
|
|
49
|
+
"tp_is_market": self.tp_is_market,
|
|
50
|
+
"sl_is_market": self.sl_is_market,
|
|
51
|
+
}
|
|
52
|
+
if self.limit_price is not None:
|
|
53
|
+
d["limit_price"] = self.limit_price
|
|
54
|
+
if self.tp_price is not None:
|
|
55
|
+
d["tp_price"] = self.tp_price
|
|
56
|
+
if self.sl_price is not None:
|
|
57
|
+
d["sl_price"] = self.sl_price
|
|
58
|
+
return d
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class PerpCloseRequest:
|
|
63
|
+
asset: str
|
|
64
|
+
size: float | None = None
|
|
65
|
+
slippage_pct: float = 0.5
|
|
66
|
+
|
|
67
|
+
def to_dict(self) -> dict:
|
|
68
|
+
d: dict = {"asset": self.asset, "slippage_pct": self.slippage_pct}
|
|
69
|
+
if self.size is not None:
|
|
70
|
+
d["size"] = self.size
|
|
71
|
+
return d
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class PerpCancelRequest:
|
|
76
|
+
asset: str
|
|
77
|
+
order_id: int
|
|
78
|
+
|
|
79
|
+
def to_dict(self) -> dict:
|
|
80
|
+
return {"asset": self.asset, "order_id": self.order_id}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class PerpFundRequest:
|
|
85
|
+
amount: str
|
|
86
|
+
source_chain_id: int
|
|
87
|
+
source_token: str | None = None
|
|
88
|
+
display_amount_usd: float | None = None
|
|
89
|
+
|
|
90
|
+
def to_dict(self) -> dict:
|
|
91
|
+
d: dict = {
|
|
92
|
+
"amount": self.amount,
|
|
93
|
+
"source_chain_id": self.source_chain_id,
|
|
94
|
+
}
|
|
95
|
+
if self.source_token is not None:
|
|
96
|
+
d["source_token"] = self.source_token
|
|
97
|
+
if self.display_amount_usd is not None:
|
|
98
|
+
d["display_amount_usd"] = self.display_amount_usd
|
|
99
|
+
return d
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass
|
|
103
|
+
class PerpWithdrawRequest:
|
|
104
|
+
amount_usd: float
|
|
105
|
+
destination_chain_id: int
|
|
106
|
+
destination_token: str | None = None
|
|
107
|
+
|
|
108
|
+
def to_dict(self) -> dict:
|
|
109
|
+
d: dict = {
|
|
110
|
+
"amount_usd": self.amount_usd,
|
|
111
|
+
"destination_chain_id": self.destination_chain_id,
|
|
112
|
+
}
|
|
113
|
+
if self.destination_token is not None:
|
|
114
|
+
d["destination_token"] = self.destination_token
|
|
115
|
+
return d
|
|
File without changes
|
|
File without changes
|
|
File without changes
|