hyperliquid-cli 0.1.0__tar.gz → 0.1.1__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 (28) hide show
  1. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/PKG-INFO +1 -1
  2. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/pyproject.toml +1 -1
  3. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/commands/act.py +1 -24
  4. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/commands/review.py +1 -28
  5. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/main.py +9 -0
  6. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/tests/__snapshots__/test_review.ambr +0 -24
  7. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/tests/test_act.py +0 -8
  8. hyperliquid_cli-0.1.1/tests/test_review.py +34 -0
  9. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/uv.lock +1 -1
  10. hyperliquid_cli-0.1.0/tests/test_review.py +0 -70
  11. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/.github/workflows/publish.yml +0 -0
  12. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/.gitignore +0 -0
  13. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/LICENSE +0 -0
  14. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/README.md +0 -0
  15. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/__init__.py +0 -0
  16. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/client.py +0 -0
  17. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/commands/__init__.py +0 -0
  18. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/commands/orient.py +0 -0
  19. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/commands/research.py +0 -0
  20. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/output.py +0 -0
  21. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/src/hl_cli/resolver.py +0 -0
  22. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/tests/__init__.py +0 -0
  23. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/tests/__snapshots__/test_act.ambr +0 -0
  24. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/tests/__snapshots__/test_orient.ambr +0 -0
  25. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/tests/__snapshots__/test_research.ambr +0 -0
  26. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/tests/conftest.py +0 -0
  27. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/tests/test_orient.py +0 -0
  28. {hyperliquid_cli-0.1.0 → hyperliquid_cli-0.1.1}/tests/test_research.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperliquid-cli
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Agent-first CLI for Hyperliquid trading
5
5
  Author: tab55
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hyperliquid-cli"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  description = "Agent-first CLI for Hyperliquid trading"
5
5
  requires-python = ">=3.10"
6
6
  license = "MIT"
@@ -1,16 +1,13 @@
1
1
  """Act commands — order, cancel, cancel-all, leverage."""
2
2
 
3
- import json
4
3
  import sys
5
- import time
6
- from pathlib import Path
7
4
  from typing import Annotated, Optional
8
5
 
9
6
  import typer
10
7
 
11
8
  from hl_cli.client import get_address, get_exchange
12
9
  from hl_cli.main import app
13
- from hl_cli.output import die, format_timestamp, output
10
+ from hl_cli.output import die, output
14
11
  from hl_cli.resolver import get_resolver
15
12
 
16
13
  SIDE_MAP = {"buy": True, "long": True, "sell": False, "short": False}
@@ -53,23 +50,6 @@ def _parse_order_response(resp, asset: str, side_str: str, size: str):
53
50
  return {"status": "unknown", "raw": st}
54
51
 
55
52
 
56
- def _log_trade(result: dict, order_type: str, price: str = ""):
57
- """Append trade record to data/trades.jsonl."""
58
- trades_dir = Path("data")
59
- trades_dir.mkdir(exist_ok=True)
60
- record = {
61
- "timestamp": format_timestamp(int(time.time() * 1000)),
62
- "asset": result.get("asset", ""),
63
- "side": result.get("side", ""),
64
- "size": result.get("size", ""),
65
- "price": result.get("avg_price", price),
66
- "type": order_type,
67
- "oid": result.get("oid", 0),
68
- "status": result.get("status", ""),
69
- }
70
- with open(trades_dir / "trades.jsonl", "a") as f:
71
- f.write(json.dumps(record, separators=(",", ":")) + "\n")
72
-
73
53
 
74
54
  @app.command()
75
55
  def order(
@@ -106,7 +86,6 @@ def order(
106
86
  # Market order
107
87
  resp = exchange.market_open(resolved, is_buy, sz, cloid=sdk_cloid)
108
88
  result = _parse_order_response(resp, resolved, side_lower, size)
109
- _log_trade(result, "market")
110
89
  output(result, opts["fields"])
111
90
  return
112
91
 
@@ -162,12 +141,10 @@ def order(
162
141
  grouping="normalTpsl",
163
142
  )
164
143
  result = _parse_order_response(resp, resolved, side_lower, size)
165
- _log_trade(result, "limit", price)
166
144
  output(result, opts["fields"])
167
145
  else:
168
146
  resp = exchange.order(resolved, is_buy, sz, px, order_type, reduce_only, cloid=sdk_cloid)
169
147
  result = _parse_order_response(resp, resolved, side_lower, size)
170
- _log_trade(result, "limit", price)
171
148
  output(result, opts["fields"])
172
149
 
173
150
 
@@ -1,7 +1,5 @@
1
- """Review commands — fills, trades."""
1
+ """Review commands — fills."""
2
2
 
3
- import json
4
- from pathlib import Path
5
3
  from typing import Annotated, Optional
6
4
 
7
5
  import typer
@@ -57,28 +55,3 @@ def fills(
57
55
  output(result, opts["fields"])
58
56
 
59
57
 
60
- @app.command()
61
- def trades(
62
- ctx: typer.Context,
63
- asset_name: Annotated[Optional[str], typer.Option("--asset", help="Filter by asset")] = None,
64
- limit: Annotated[int, typer.Option("--limit", help="Number of trades")] = 20,
65
- ):
66
- """Local trade log (from data/trades.jsonl)."""
67
- opts = ctx.obj
68
- trades_file = Path("data/trades.jsonl")
69
-
70
- if not trades_file.exists():
71
- output([], opts["fields"])
72
- return
73
-
74
- result = []
75
- for line in trades_file.read_text().strip().split("\n"):
76
- if not line:
77
- continue
78
- trade = json.loads(line)
79
- if asset_name and trade.get("asset") != asset_name:
80
- continue
81
- result.append(trade)
82
-
83
- result = result[-limit:]
84
- output(result, opts["fields"])
@@ -1,9 +1,17 @@
1
1
  """hl — Agent-first CLI for Hyperliquid trading."""
2
2
 
3
+ from importlib.metadata import version as pkg_version
3
4
  from typing import Optional
4
5
 
5
6
  import typer
6
7
 
8
+
9
+ def _version_callback(value: bool):
10
+ if value:
11
+ print(pkg_version("hyperliquid-cli"))
12
+ raise typer.Exit()
13
+
14
+
7
15
  app = typer.Typer(
8
16
  name="hl",
9
17
  add_completion=False,
@@ -15,6 +23,7 @@ app = typer.Typer(
15
23
  @app.callback()
16
24
  def main(
17
25
  ctx: typer.Context,
26
+ version: bool = typer.Option(False, "--version", callback=_version_callback, is_eager=True, help="Show version and exit"),
18
27
  fields: Optional[str] = typer.Option(None, "--fields", help="Comma-separated field projection"),
19
28
  verbose: bool = typer.Option(False, "--verbose", help="Debug info to stderr"),
20
29
  testnet: bool = typer.Option(False, "--testnet", help="Use testnet endpoints"),
@@ -23,27 +23,3 @@
23
23
  }),
24
24
  ])
25
25
  # ---
26
- # name: TestTrades.test_with_data
27
- list([
28
- dict({
29
- 'asset': 'BTC',
30
- 'oid': 200001,
31
- 'price': '69666.0',
32
- 'side': 'buy',
33
- 'size': '0.1',
34
- 'status': 'filled',
35
- 'timestamp': '2026-03-11T10:30:00Z',
36
- 'type': 'market',
37
- }),
38
- dict({
39
- 'asset': 'ETH',
40
- 'oid': 200002,
41
- 'price': '3300.0',
42
- 'side': 'sell',
43
- 'size': '1.0',
44
- 'status': 'resting',
45
- 'timestamp': '2026-03-11T10:31:00Z',
46
- 'type': 'limit',
47
- }),
48
- ])
49
- # ---
@@ -42,14 +42,6 @@ class TestOrder:
42
42
  assert result.exit_code == 1
43
43
  assert parse(result)["error"] == "input_error"
44
44
 
45
- def test_trade_log_written(self, runner, patched, tmp_path):
46
- runner.invoke(app, ["order", "BTC", "buy", "0.1", "--market"])
47
- trades_file = tmp_path / "data" / "trades.jsonl"
48
- assert trades_file.exists()
49
- record = json.loads(trades_file.read_text().strip())
50
- assert record["asset"] == "BTC"
51
- assert record["type"] == "market"
52
-
53
45
  def test_unknown_asset(self, runner, patched):
54
46
  result = runner.invoke(app, ["order", "FAKECOIN", "buy", "0.1", "--market"])
55
47
  assert result.exit_code == 4
@@ -0,0 +1,34 @@
1
+ """Tests for review commands — fills, trades."""
2
+
3
+ import json
4
+ import os
5
+
6
+ import pytest
7
+
8
+ from hl_cli.main import app
9
+
10
+
11
+ def parse(result):
12
+ return json.loads(result.output)
13
+
14
+
15
+ class TestFills:
16
+ def test_basic(self, runner, patched, snapshot):
17
+ result = runner.invoke(app, ["fills"])
18
+ assert result.exit_code == 0
19
+ assert parse(result) == snapshot
20
+
21
+ def test_filter_asset(self, runner, patched):
22
+ result = runner.invoke(app, ["fills", "BTC"])
23
+ assert result.exit_code == 0
24
+ data = parse(result)
25
+ assert len(data) == 1
26
+ assert data[0]["asset"] == "BTC"
27
+
28
+ def test_limit(self, runner, patched):
29
+ result = runner.invoke(app, ["fills", "--limit", "1"])
30
+ assert result.exit_code == 0
31
+ data = parse(result)
32
+ assert len(data) == 1
33
+
34
+
@@ -580,7 +580,7 @@ wheels = [
580
580
 
581
581
  [[package]]
582
582
  name = "hyperliquid-cli"
583
- version = "0.1.0"
583
+ version = "0.1.1"
584
584
  source = { editable = "." }
585
585
  dependencies = [
586
586
  { name = "hyperliquid-python-sdk" },
@@ -1,70 +0,0 @@
1
- """Tests for review commands — fills, trades."""
2
-
3
- import json
4
- import os
5
-
6
- import pytest
7
-
8
- from hl_cli.main import app
9
-
10
-
11
- def parse(result):
12
- return json.loads(result.output)
13
-
14
-
15
- class TestFills:
16
- def test_basic(self, runner, patched, snapshot):
17
- result = runner.invoke(app, ["fills"])
18
- assert result.exit_code == 0
19
- assert parse(result) == snapshot
20
-
21
- def test_filter_asset(self, runner, patched):
22
- result = runner.invoke(app, ["fills", "BTC"])
23
- assert result.exit_code == 0
24
- data = parse(result)
25
- assert len(data) == 1
26
- assert data[0]["asset"] == "BTC"
27
-
28
- def test_limit(self, runner, patched):
29
- result = runner.invoke(app, ["fills", "--limit", "1"])
30
- assert result.exit_code == 0
31
- data = parse(result)
32
- assert len(data) == 1
33
-
34
-
35
- class TestTrades:
36
- @pytest.fixture(autouse=True)
37
- def _use_tmp_dir(self, tmp_path):
38
- original = os.getcwd()
39
- os.chdir(tmp_path)
40
- yield
41
- os.chdir(original)
42
-
43
- def test_empty(self, runner, patched):
44
- result = runner.invoke(app, ["trades"])
45
- assert result.exit_code == 0
46
- assert parse(result) == []
47
-
48
- def test_with_data(self, runner, patched, tmp_path, snapshot):
49
- trades_dir = tmp_path / "data"
50
- trades_dir.mkdir()
51
- (trades_dir / "trades.jsonl").write_text(
52
- '{"timestamp":"2026-03-11T10:30:00Z","asset":"BTC","side":"buy","size":"0.1","price":"69666.0","type":"market","oid":200001,"status":"filled"}\n'
53
- '{"timestamp":"2026-03-11T10:31:00Z","asset":"ETH","side":"sell","size":"1.0","price":"3300.0","type":"limit","oid":200002,"status":"resting"}\n'
54
- )
55
- result = runner.invoke(app, ["trades"])
56
- assert result.exit_code == 0
57
- assert parse(result) == snapshot
58
-
59
- def test_filter_asset(self, runner, patched, tmp_path):
60
- trades_dir = tmp_path / "data"
61
- trades_dir.mkdir()
62
- (trades_dir / "trades.jsonl").write_text(
63
- '{"timestamp":"2026-03-11T10:30:00Z","asset":"BTC","side":"buy","size":"0.1","price":"69666.0","type":"market","oid":200001,"status":"filled"}\n'
64
- '{"timestamp":"2026-03-11T10:31:00Z","asset":"ETH","side":"sell","size":"1.0","price":"3300.0","type":"limit","oid":200002,"status":"resting"}\n'
65
- )
66
- result = runner.invoke(app, ["trades", "--asset", "BTC"])
67
- assert result.exit_code == 0
68
- data = parse(result)
69
- assert len(data) == 1
70
- assert data[0]["asset"] == "BTC"
File without changes