fere-cli 0.3.1.dev33__tar.gz → 0.4.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.
Files changed (24) hide show
  1. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/.gitignore +4 -0
  2. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/PKG-INFO +2 -2
  3. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/pyproject.toml +2 -2
  4. fere_cli-0.4.0.dev35/src/fere_cli/commands/perp.py +337 -0
  5. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/main.py +4 -0
  6. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/README.md +0 -0
  7. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/install.sh +0 -0
  8. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/__init__.py +0 -0
  9. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/async_util.py +0 -0
  10. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/banner.py +0 -0
  11. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/commands/__init__.py +0 -0
  12. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/commands/auth.py +0 -0
  13. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/commands/chat.py +0 -0
  14. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/commands/earn.py +0 -0
  15. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/commands/hooks.py +0 -0
  16. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/commands/limit_order.py +0 -0
  17. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/commands/portfolio.py +0 -0
  18. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/commands/swap.py +0 -0
  19. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/commands/utility.py +0 -0
  20. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/config.py +0 -0
  21. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/output.py +0 -0
  22. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/src/fere_cli/update.py +0 -0
  23. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/tests/test_install_sh.py +0 -0
  24. {fere_cli-0.3.1.dev33 → fere_cli-0.4.0.dev35}/tests/test_update.py +0 -0
@@ -8,6 +8,8 @@ __pycache__
8
8
  # Devcontainer local env (contains secrets)
9
9
  .devcontainer/.env.local
10
10
  .devcontainer/.env.local.override
11
+ .devcontainer/.env.runtime.local
12
+ .devcontainer/.env.runtime.*.local
11
13
  .devcontainer/.env.gitpod
12
14
 
13
15
  # Bruno gateway collection (GATEWAY_AGENT_ID / GATEWAY_PRIVATE_KEY_B64)
@@ -43,6 +45,7 @@ dev/logs/*
43
45
  # Cursor SEO skill — regenerated report JSON (not source)
44
46
  .cursor/skills/seo-agent/artifacts/
45
47
  .cursor/skills/docker-images-cve-scan/artifacts/
48
+ docs/plans/artifacts/
46
49
 
47
50
  # Skills installed locally by the skills package manager (see skills-lock.json)
48
51
  .claude/skills/stripe-best-practices
@@ -52,6 +55,7 @@ dev/logs/*
52
55
 
53
56
  # agentmemory iii state (SQLite + streams under .agentmemory/data/)
54
57
  .agentmemory/data/
58
+ .devcontainer/agentmemory-mcp/node_modules/
55
59
 
56
60
  # Local GEO upstream reference (optional clone; not part of the product tree)
57
61
  geo-seo/
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fere-cli
3
- Version: 0.3.1.dev33
3
+ Version: 0.4.0.dev35
4
4
  Summary: Terminal CLI for FereAI crypto trading and research
5
5
  Author-email: Fere AI <info@fere.ai>
6
6
  License-Expression: MIT
7
7
  Requires-Python: >=3.10
8
8
  Requires-Dist: click>=8.1
9
- Requires-Dist: fere-sdk>=0.3.1.dev33
9
+ Requires-Dist: fere-sdk>=0.4.0.dev35
10
10
  Requires-Dist: httpx>=0.27
11
11
  Requires-Dist: rich>=13.0
12
12
  Provides-Extra: dev
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "fere-cli"
3
- version = "0.3.1.dev33"
3
+ version = "0.4.0.dev35"
4
4
  description = "Terminal CLI for FereAI crypto trading and research"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  license = "MIT"
8
8
  authors = [{ name = "Fere AI", email = "info@fere.ai" }]
9
9
  dependencies = [
10
- "fere-sdk>=0.3.1.dev33",
10
+ "fere-sdk>=0.4.0.dev35",
11
11
  "httpx>=0.27",
12
12
  "click>=8.1",
13
13
  "rich>=13.0",
@@ -0,0 +1,337 @@
1
+ """Perp commands: fere perp (markets, status, setup, orders, open, close,
2
+ cancel, fund, withdraw).
3
+
4
+ Venue-neutral perpetual futures. ``size`` is in contracts.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import click
10
+ from rich.console import Console
11
+
12
+ from ..async_util import async_command, get_client, is_tty
13
+ from ..output import print_error, print_success
14
+
15
+ console = Console()
16
+
17
+
18
+ @click.group()
19
+ def perp():
20
+ """Trade perpetual futures (open, close, fund, withdraw)."""
21
+ pass
22
+
23
+
24
+ @perp.command()
25
+ @click.option("--search", default=None, help="Filter the asset universe.")
26
+ @click.pass_context
27
+ @async_command
28
+ async def markets(ctx, search):
29
+ """List the tradable perp market universe."""
30
+ try:
31
+ client = await get_client(ctx)
32
+ data = await client.get_perp_markets(search_text=search)
33
+ await client.close()
34
+ print_success(data)
35
+ except click.ClickException:
36
+ raise
37
+ except Exception as e:
38
+ print_error(str(e))
39
+ raise SystemExit(1)
40
+
41
+
42
+ @perp.command()
43
+ @click.pass_context
44
+ @async_command
45
+ async def status(ctx):
46
+ """Show whether perp trading is provisioned."""
47
+ try:
48
+ client = await get_client(ctx)
49
+ data = await client.get_perp_setup_status()
50
+ await client.close()
51
+ print_success(data)
52
+ except click.ClickException:
53
+ raise
54
+ except Exception as e:
55
+ print_error(str(e))
56
+ raise SystemExit(1)
57
+
58
+
59
+ @perp.command()
60
+ @click.option("--force", is_flag=True, help="Re-run setup even if complete.")
61
+ @click.option(
62
+ "--timeout", default=120, type=int, help="Wait timeout (seconds)."
63
+ )
64
+ @click.pass_context
65
+ @async_command
66
+ async def setup(ctx, force, timeout):
67
+ """Provision perp trading for the account."""
68
+ try:
69
+ client = await get_client(ctx)
70
+ with console.status("Provisioning perp trading..."):
71
+ result = await client.perp_setup(force_rerun=force, timeout=timeout)
72
+ await client.close()
73
+ print_success(result)
74
+ except click.ClickException:
75
+ raise
76
+ except Exception as e:
77
+ print_error(str(e))
78
+ raise SystemExit(1)
79
+
80
+
81
+ @perp.command()
82
+ @click.pass_context
83
+ @async_command
84
+ async def orders(ctx):
85
+ """List open perp orders (incl. TP/SL triggers)."""
86
+ try:
87
+ client = await get_client(ctx)
88
+ data = await client.get_perp_orders()
89
+ await client.close()
90
+ print_success(data)
91
+ except click.ClickException:
92
+ raise
93
+ except Exception as e:
94
+ print_error(str(e))
95
+ raise SystemExit(1)
96
+
97
+
98
+ @perp.command()
99
+ @click.option("--asset", required=True, help="Asset symbol, e.g. BTC.")
100
+ @click.option(
101
+ "--side",
102
+ required=True,
103
+ type=click.Choice(["long", "short"]),
104
+ help="Position direction.",
105
+ )
106
+ @click.option("--size", required=True, type=float, help="Size in contracts.")
107
+ @click.option("--leverage", default=1, type=int, help="Leverage multiplier.")
108
+ @click.option(
109
+ "--order-type",
110
+ default="market",
111
+ type=click.Choice(["market", "limit"]),
112
+ )
113
+ @click.option("--limit-price", default=None, type=float)
114
+ @click.option("--slippage-pct", default=0.5, type=float)
115
+ @click.option("--tp-price", default=None, type=float, help="Take-profit.")
116
+ @click.option("--sl-price", default=None, type=float, help="Stop-loss.")
117
+ @click.option(
118
+ "--timeout", default=120, type=int, help="Wait timeout (seconds)."
119
+ )
120
+ @click.option("-y", "--yes", is_flag=True, help="Skip confirmation.")
121
+ @click.pass_context
122
+ @async_command
123
+ async def open(
124
+ ctx,
125
+ asset,
126
+ side,
127
+ size,
128
+ leverage,
129
+ order_type,
130
+ limit_price,
131
+ slippage_pct,
132
+ tp_price,
133
+ sl_price,
134
+ timeout,
135
+ yes,
136
+ ):
137
+ """Open a perpetual position."""
138
+ if not yes and is_tty():
139
+ click.echo(f"Open {side} {size} {asset} at {leverage}x ({order_type})")
140
+ if not click.confirm("Proceed?"):
141
+ raise SystemExit(0)
142
+ try:
143
+ client = await get_client(ctx)
144
+ with console.status("Opening position..."):
145
+ result = await client.perp_open(
146
+ asset=asset,
147
+ is_buy=(side == "long"),
148
+ size=size,
149
+ leverage=leverage,
150
+ order_type=order_type,
151
+ limit_price=limit_price,
152
+ slippage_pct=slippage_pct,
153
+ tp_price=tp_price,
154
+ sl_price=sl_price,
155
+ timeout=timeout,
156
+ )
157
+ await client.close()
158
+ print_success(result)
159
+ except click.ClickException:
160
+ raise
161
+ except Exception as e:
162
+ print_error(str(e))
163
+ raise SystemExit(1)
164
+
165
+
166
+ @perp.command()
167
+ @click.option("--asset", required=True, help="Asset symbol to close.")
168
+ @click.option(
169
+ "--size",
170
+ default=None,
171
+ type=float,
172
+ help="Contracts to close; omit for full close.",
173
+ )
174
+ @click.option("--slippage-pct", default=0.5, type=float)
175
+ @click.option(
176
+ "--timeout", default=120, type=int, help="Wait timeout (seconds)."
177
+ )
178
+ @click.option("-y", "--yes", is_flag=True, help="Skip confirmation.")
179
+ @click.pass_context
180
+ @async_command
181
+ async def close(ctx, asset, size, slippage_pct, timeout, yes):
182
+ """Close a perpetual position."""
183
+ if not yes and is_tty():
184
+ amt = f"{size}" if size is not None else "full"
185
+ click.echo(f"Close {amt} {asset} position")
186
+ if not click.confirm("Proceed?"):
187
+ raise SystemExit(0)
188
+ try:
189
+ client = await get_client(ctx)
190
+ with console.status("Closing position..."):
191
+ result = await client.perp_close(
192
+ asset=asset,
193
+ size=size,
194
+ slippage_pct=slippage_pct,
195
+ timeout=timeout,
196
+ )
197
+ await client.close()
198
+ print_success(result)
199
+ except click.ClickException:
200
+ raise
201
+ except Exception as e:
202
+ print_error(str(e))
203
+ raise SystemExit(1)
204
+
205
+
206
+ @perp.command()
207
+ @click.option("--asset", required=True, help="Asset symbol of the order.")
208
+ @click.option(
209
+ "--order-id", required=True, type=int, help="Venue order id (oid)."
210
+ )
211
+ @click.option(
212
+ "--timeout", default=120, type=int, help="Wait timeout (seconds)."
213
+ )
214
+ @click.pass_context
215
+ @async_command
216
+ async def cancel(ctx, asset, order_id, timeout):
217
+ """Cancel an open perp order by its venue order id."""
218
+ try:
219
+ client = await get_client(ctx)
220
+ with console.status("Cancelling order..."):
221
+ result = await client.perp_cancel_order(
222
+ asset=asset, order_id=order_id, timeout=timeout
223
+ )
224
+ await client.close()
225
+ print_success(result)
226
+ except click.ClickException:
227
+ raise
228
+ except Exception as e:
229
+ print_error(str(e))
230
+ raise SystemExit(1)
231
+
232
+
233
+ @perp.command()
234
+ @click.option(
235
+ "--amount",
236
+ required=True,
237
+ help="Source token amount in smallest units.",
238
+ )
239
+ @click.option(
240
+ "--source-chain-id", required=True, type=int, help="Source chain id."
241
+ )
242
+ @click.option(
243
+ "--source-token",
244
+ default=None,
245
+ help="ERC-20 address; omit for native token.",
246
+ )
247
+ @click.option(
248
+ "--usd",
249
+ "display_amount_usd",
250
+ default=None,
251
+ type=float,
252
+ help="USD amount the user entered.",
253
+ )
254
+ @click.option(
255
+ "--timeout", default=120, type=int, help="Wait timeout (seconds)."
256
+ )
257
+ @click.option("-y", "--yes", is_flag=True, help="Skip confirmation.")
258
+ @click.pass_context
259
+ @async_command
260
+ async def fund(
261
+ ctx,
262
+ amount,
263
+ source_chain_id,
264
+ source_token,
265
+ display_amount_usd,
266
+ timeout,
267
+ yes,
268
+ ):
269
+ """Fund the perp account from a connected wallet."""
270
+ if not yes and is_tty():
271
+ click.echo(f"Fund perp account from chain {source_chain_id}")
272
+ if not click.confirm("Proceed?"):
273
+ raise SystemExit(0)
274
+ try:
275
+ client = await get_client(ctx)
276
+ with console.status("Funding..."):
277
+ result = await client.perp_fund(
278
+ amount=amount,
279
+ source_chain_id=source_chain_id,
280
+ source_token=source_token,
281
+ display_amount_usd=display_amount_usd,
282
+ timeout=timeout,
283
+ )
284
+ await client.close()
285
+ print_success(result)
286
+ except click.ClickException:
287
+ raise
288
+ except Exception as e:
289
+ print_error(str(e))
290
+ raise SystemExit(1)
291
+
292
+
293
+ @perp.command()
294
+ @click.option(
295
+ "--amount-usd", required=True, type=float, help="USD to withdraw."
296
+ )
297
+ @click.option(
298
+ "--destination-chain-id",
299
+ required=True,
300
+ type=int,
301
+ help="Destination chain id.",
302
+ )
303
+ @click.option(
304
+ "--destination-token",
305
+ default=None,
306
+ help="ERC-20/mint; omit for native token.",
307
+ )
308
+ @click.option(
309
+ "--timeout", default=120, type=int, help="Wait timeout (seconds)."
310
+ )
311
+ @click.option("-y", "--yes", is_flag=True, help="Skip confirmation.")
312
+ @click.pass_context
313
+ @async_command
314
+ async def withdraw(
315
+ ctx, amount_usd, destination_chain_id, destination_token, timeout, yes
316
+ ):
317
+ """Withdraw perp collateral to a destination chain/token."""
318
+ if not yes and is_tty():
319
+ click.echo(f"Withdraw {amount_usd} USD to chain {destination_chain_id}")
320
+ if not click.confirm("Proceed?"):
321
+ raise SystemExit(0)
322
+ try:
323
+ client = await get_client(ctx)
324
+ with console.status("Withdrawing..."):
325
+ result = await client.perp_withdraw(
326
+ amount_usd=amount_usd,
327
+ destination_chain_id=destination_chain_id,
328
+ destination_token=destination_token,
329
+ timeout=timeout,
330
+ )
331
+ await client.close()
332
+ print_success(result)
333
+ except click.ClickException:
334
+ raise
335
+ except Exception as e:
336
+ print_error(str(e))
337
+ raise SystemExit(1)
@@ -10,6 +10,7 @@ from .commands.chat import chat, threads
10
10
  from .commands.earn import earn
11
11
  from .commands.hooks import hooks
12
12
  from .commands.limit_order import limit_order
13
+ from .commands.perp import perp
13
14
  from .commands.portfolio import holdings, notifications, wallets
14
15
  from .commands.swap import swap
15
16
  from .commands.utility import chains, config, status
@@ -90,6 +91,9 @@ cli.add_command(notifications)
90
91
  # Earn
91
92
  cli.add_command(earn)
92
93
 
94
+ # Perp (venue-neutral perpetual futures)
95
+ cli.add_command(perp)
96
+
93
97
  # Utility
94
98
  cli.add_command(chains)
95
99
  cli.add_command(status)
File without changes