hunterkiller 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.
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: hunterkiller
3
+ Version: 0.1.0
4
+ Summary: Hunter Killer Desk Read client: check where liquidation money is stacked before a leveraged trade, and watch for walls coming into range.
5
+ License: MIT
6
+ Project-URL: Homepage, https://hunterkiller.io/for-agents
7
+ Keywords: crypto,liquidations,perpetuals,trading,agents,mcp
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+
11
+ # hunterkiller (Python)
12
+
13
+ Client for the Hunter Killer Desk Read. Standard library only.
14
+
15
+ ```
16
+ pip install hunterkiller
17
+ ```
18
+
19
+ ```python
20
+ import hunterkiller as hk
21
+
22
+ read = hk.desk_read("BTCUSDT", side="long", entry=81000, leverage=20)
23
+ print(read["verdict"], read["text"])
24
+
25
+ print(hk.symbols()["covered"])
26
+
27
+ for event in hk.watch("BTCUSDT", threshold_pct=1.5): # needs a key
28
+ print(event["reason"], event["text"])
29
+ ```
30
+
31
+ `desk_read` works without a key (delayed data). `watch` needs a key: a free key (delayed, one
32
+ stream) is returned as `api_key` when you sign up at https://hunterkiller.io/signup. Pass `key=`
33
+ or set `HK_API_KEY`. A paid key is live and gets five streams. Errors raise `HunterKillerError`
34
+ with `.status` and `.body`.
35
+
36
+ `watch` yields the `desk_read` payload plus `event`, `reason` (`connect`, `wall_in_range:above`,
37
+ `wall_in_range:below`, `pull_flip`) and `threshold_pct`. It reconnects on its own after the
38
+ 30 minute cap. Descriptive data, not a forecast. Docs: https://hunterkiller.io/for-agents
@@ -0,0 +1,28 @@
1
+ # hunterkiller (Python)
2
+
3
+ Client for the Hunter Killer Desk Read. Standard library only.
4
+
5
+ ```
6
+ pip install hunterkiller
7
+ ```
8
+
9
+ ```python
10
+ import hunterkiller as hk
11
+
12
+ read = hk.desk_read("BTCUSDT", side="long", entry=81000, leverage=20)
13
+ print(read["verdict"], read["text"])
14
+
15
+ print(hk.symbols()["covered"])
16
+
17
+ for event in hk.watch("BTCUSDT", threshold_pct=1.5): # needs a key
18
+ print(event["reason"], event["text"])
19
+ ```
20
+
21
+ `desk_read` works without a key (delayed data). `watch` needs a key: a free key (delayed, one
22
+ stream) is returned as `api_key` when you sign up at https://hunterkiller.io/signup. Pass `key=`
23
+ or set `HK_API_KEY`. A paid key is live and gets five streams. Errors raise `HunterKillerError`
24
+ with `.status` and `.body`.
25
+
26
+ `watch` yields the `desk_read` payload plus `event`, `reason` (`connect`, `wall_in_range:above`,
27
+ `wall_in_range:below`, `pull_flip`) and `threshold_pct`. It reconnects on its own after the
28
+ 30 minute cap. Descriptive data, not a forecast. Docs: https://hunterkiller.io/for-agents
@@ -0,0 +1,127 @@
1
+ """hunterkiller: Python client for the Hunter Killer Desk Read. Standard library only.
2
+
3
+ import hunterkiller as hk
4
+ read = hk.desk_read("BTCUSDT", side="long", entry=81000, leverage=20, key="hk_live_...")
5
+ print(read["verdict"], read["text"])
6
+
7
+ for event in hk.watch("BTCUSDT", threshold_pct=1.5, key="hk_live_..."):
8
+ print(event["reason"], event["text"])
9
+
10
+ A key is optional for desk_read (keyless calls get the delayed tier) and required for watch.
11
+ Get a free key (delayed data) at https://hunterkiller.io/signup. The key can also come from the
12
+ HK_API_KEY environment variable. Descriptive data, not a forecast."""
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import os
17
+ import time
18
+ import urllib.error
19
+ import urllib.parse
20
+ import urllib.request
21
+
22
+ __all__ = ["desk_read", "watch", "symbols", "HunterKillerError", "BASE_URL"]
23
+ __version__ = "0.1.0"
24
+
25
+ BASE_URL = os.environ.get("HK_BASE_URL", "https://hunterkiller.io")
26
+ _UA = "hunterkiller-python/" + __version__
27
+
28
+
29
+ class HunterKillerError(Exception):
30
+ """An HTTP error from the API. .status is the code, .body the parsed JSON error body."""
31
+
32
+ def __init__(self, status, body):
33
+ self.status, self.body = status, body
34
+ msg = body.get("message") or body.get("error") if isinstance(body, dict) else None
35
+ super().__init__(f"HTTP {status}: {msg or body}")
36
+
37
+
38
+ def _headers(key):
39
+ key = key or os.environ.get("HK_API_KEY")
40
+ h = {"User-Agent": _UA, "Accept": "application/json"}
41
+ if key:
42
+ h["Authorization"] = "Bearer " + key
43
+ return h
44
+
45
+
46
+ def _open(url, headers, timeout):
47
+ req = urllib.request.Request(url, headers=headers)
48
+ try:
49
+ return urllib.request.urlopen(req, timeout=timeout)
50
+ except urllib.error.HTTPError as e:
51
+ raw = e.read().decode("utf-8", "replace")
52
+ try:
53
+ body = json.loads(raw)
54
+ except ValueError:
55
+ body = {"error": raw[:200]}
56
+ raise HunterKillerError(e.code, body) from None
57
+
58
+
59
+ def _get_json(path, params, key, base_url, timeout=15):
60
+ q = urllib.parse.urlencode({k: v for k, v in params.items() if v is not None})
61
+ with _open(f"{base_url or BASE_URL}{path}" + (f"?{q}" if q else ""), _headers(key), timeout) as r:
62
+ return json.load(r)
63
+
64
+
65
+ def desk_read(symbol, side=None, entry=None, leverage=None, key=None, base_url=None):
66
+ """The Desk Read for one perp pair: pull, hurt, cascade, crowd, plan, verdict, lines, text.
67
+ side, entry and leverage together add where your liquidation sits and a verdict."""
68
+ return _get_json("/api/desk_read", {"symbol": symbol, "side": side, "entry": entry,
69
+ "leverage": leverage, "ref": "hunterkiller-python"},
70
+ key, base_url)
71
+
72
+
73
+ def symbols(base_url=None):
74
+ """Covered symbols per tier and the free-tier delay."""
75
+ return _get_json("/api/desk_read/symbols", {}, None, base_url)
76
+
77
+
78
+ def _sse(lines):
79
+ """Yield (event, data) from an iterable of decoded SSE lines. Comments are skipped."""
80
+ event, data = "message", []
81
+ for raw in lines:
82
+ line = raw.rstrip("\r\n")
83
+ if line == "":
84
+ if data:
85
+ yield event, "\n".join(data)
86
+ event, data = "message", []
87
+ elif line.startswith(":"):
88
+ continue
89
+ elif line.startswith("event:"):
90
+ event = line[6:].strip()
91
+ elif line.startswith("data:"):
92
+ data.append(line[5:].lstrip())
93
+
94
+
95
+ def watch(symbol, threshold_pct=1.5, key=None, base_url=None, reconnect=True, max_events=None):
96
+ """Generator of desk_read events: one on connect, then whenever the nearest wall on either
97
+ side comes within threshold_pct of price or the pull direction flips. Reconnects on the
98
+ server's 30-minute reconnect event and on dropped connections (back off up to 30 s).
99
+ Auth and limit errors raise HunterKillerError (a keyless call is 401). max_events stops
100
+ the generator after that many events (handy in tests)."""
101
+ q = urllib.parse.urlencode({"symbol": symbol, "threshold_pct": threshold_pct,
102
+ "ref": "hunterkiller-python"})
103
+ url = f"{base_url or BASE_URL}/api/desk_read/watch?{q}"
104
+ headers = dict(_headers(key), Accept="text/event-stream")
105
+ sent, delay = 0, 1.0
106
+ while True:
107
+ try:
108
+ with _open(url, headers, 90) as resp:
109
+ delay = 1.0
110
+ for event, data in _sse(line.decode("utf-8", "replace") for line in resp):
111
+ if event == "desk_read":
112
+ yield json.loads(data)
113
+ sent += 1
114
+ if max_events and sent >= max_events:
115
+ return
116
+ elif event == "reconnect":
117
+ break
118
+ except HunterKillerError as e:
119
+ if e.status != 429 or not reconnect:
120
+ raise
121
+ except OSError:
122
+ if not reconnect:
123
+ return
124
+ if not reconnect:
125
+ return
126
+ time.sleep(delay)
127
+ delay = min(delay * 2, 30.0)
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: hunterkiller
3
+ Version: 0.1.0
4
+ Summary: Hunter Killer Desk Read client: check where liquidation money is stacked before a leveraged trade, and watch for walls coming into range.
5
+ License: MIT
6
+ Project-URL: Homepage, https://hunterkiller.io/for-agents
7
+ Keywords: crypto,liquidations,perpetuals,trading,agents,mcp
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+
11
+ # hunterkiller (Python)
12
+
13
+ Client for the Hunter Killer Desk Read. Standard library only.
14
+
15
+ ```
16
+ pip install hunterkiller
17
+ ```
18
+
19
+ ```python
20
+ import hunterkiller as hk
21
+
22
+ read = hk.desk_read("BTCUSDT", side="long", entry=81000, leverage=20)
23
+ print(read["verdict"], read["text"])
24
+
25
+ print(hk.symbols()["covered"])
26
+
27
+ for event in hk.watch("BTCUSDT", threshold_pct=1.5): # needs a key
28
+ print(event["reason"], event["text"])
29
+ ```
30
+
31
+ `desk_read` works without a key (delayed data). `watch` needs a key: a free key (delayed, one
32
+ stream) is returned as `api_key` when you sign up at https://hunterkiller.io/signup. Pass `key=`
33
+ or set `HK_API_KEY`. A paid key is live and gets five streams. Errors raise `HunterKillerError`
34
+ with `.status` and `.body`.
35
+
36
+ `watch` yields the `desk_read` payload plus `event`, `reason` (`connect`, `wall_in_range:above`,
37
+ `wall_in_range:below`, `pull_flip`) and `threshold_pct`. It reconnects on its own after the
38
+ 30 minute cap. Descriptive data, not a forecast. Docs: https://hunterkiller.io/for-agents
@@ -0,0 +1,7 @@
1
+ README.md
2
+ pyproject.toml
3
+ hunterkiller/__init__.py
4
+ hunterkiller.egg-info/PKG-INFO
5
+ hunterkiller.egg-info/SOURCES.txt
6
+ hunterkiller.egg-info/dependency_links.txt
7
+ hunterkiller.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ hunterkiller
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "hunterkiller"
7
+ version = "0.1.0"
8
+ description = "Hunter Killer Desk Read client: check where liquidation money is stacked before a leveraged trade, and watch for walls coming into range."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ dependencies = []
13
+ keywords = ["crypto", "liquidations", "perpetuals", "trading", "agents", "mcp"]
14
+
15
+ [project.urls]
16
+ Homepage = "https://hunterkiller.io/for-agents"
17
+
18
+ [tool.setuptools]
19
+ packages = ["hunterkiller"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+