liquidator-indicator 0.0.4__tar.gz → 0.0.6__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.
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6}/PKG-INFO +4 -3
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6}/pyproject.toml +7 -3
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6}/setup.cfg +4 -3
- liquidator_indicator-0.0.6/src/liquidator_indicator/__init__.py +6 -0
- liquidator_indicator-0.0.6/src/liquidator_indicator/collectors/__init__.py +11 -0
- liquidator_indicator-0.0.6/src/liquidator_indicator/collectors/funding.py +202 -0
- liquidator_indicator-0.0.6/src/liquidator_indicator/core.py +447 -0
- liquidator_indicator-0.0.6/src/liquidator_indicator/indicators.py +52 -0
- liquidator_indicator-0.0.6/src/liquidator_indicator/numba_optimized.py +351 -0
- liquidator_indicator-0.0.6/src/liquidator_indicator/parsers.py +119 -0
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6/src}/liquidator_indicator.egg-info/PKG-INFO +4 -3
- liquidator_indicator-0.0.6/src/liquidator_indicator.egg-info/SOURCES.txt +19 -0
- liquidator_indicator-0.0.6/src/liquidator_indicator.egg-info/requires.txt +5 -0
- liquidator_indicator-0.0.6/src/liquidator_indicator.egg-info/top_level.txt +1 -0
- liquidator_indicator-0.0.4/liquidator_indicator.egg-info/SOURCES.txt +0 -12
- liquidator_indicator-0.0.4/liquidator_indicator.egg-info/requires.txt +0 -2
- liquidator_indicator-0.0.4/liquidator_indicator.egg-info/top_level.txt +0 -1
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6}/LICENSE +0 -0
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6}/README.md +0 -0
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6/src}/liquidator_indicator.egg-info/dependency_links.txt +0 -0
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6}/tests/test_core.py +0 -0
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6}/tests/test_indicators_parsers.py +0 -0
- {liquidator_indicator-0.0.4 → liquidator_indicator-0.0.6}/tests/test_zones_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: liquidator_indicator
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: Lightweight liquidation zone indicator with multi-signal detection and visual chart integration
|
|
5
5
|
Home-page: https://github.com/ViWarshawski/liquidator_indicator
|
|
6
6
|
Author: ViWarshawski
|
|
@@ -18,8 +18,9 @@ Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
|
18
18
|
Requires-Python: >=3.9
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
|
-
Requires-Dist: pandas
|
|
22
|
-
Requires-Dist: numpy
|
|
21
|
+
Requires-Dist: pandas>=1.3.0
|
|
22
|
+
Requires-Dist: numpy>=1.20.0
|
|
23
|
+
Requires-Dist: numba>=0.56.0; python_version >= "3.9"
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
25
26
|
# liquidator_indicator
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "liquidator_indicator"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.6"
|
|
4
4
|
description = "Lightweight liquidation zone indicator with multi-signal detection and visual chart integration"
|
|
5
5
|
license = {text = "MIT"}
|
|
6
6
|
requires-python = ">=3.9"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"pandas
|
|
9
|
-
"numpy
|
|
8
|
+
"pandas>=1.3.0",
|
|
9
|
+
"numpy>=1.20.0",
|
|
10
|
+
"numba>=0.56.0; python_version>='3.9'"
|
|
10
11
|
]
|
|
11
12
|
readme = "README.md"
|
|
12
13
|
keywords = ["trading", "liquidation", "zones", "cryptocurrency", "technical-analysis"]
|
|
@@ -21,6 +22,9 @@ classifiers = [
|
|
|
21
22
|
"Topic :: Office/Business :: Financial :: Investment",
|
|
22
23
|
]
|
|
23
24
|
|
|
25
|
+
[tool.setuptools.packages.find]
|
|
26
|
+
where = ["src"]
|
|
27
|
+
|
|
24
28
|
[build-system]
|
|
25
29
|
requires = ["setuptools>=61.0","wheel"]
|
|
26
30
|
build-backend = "setuptools.build_meta"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = liquidator_indicator
|
|
3
|
-
version = 0.0.
|
|
3
|
+
version = 0.0.6
|
|
4
4
|
author = ViWarshawski
|
|
5
5
|
author_email = nexus2.0.2026@gmail.com
|
|
6
6
|
description = Lightweight liquidation zone indicator with multi-signal detection and visual chart integration
|
|
@@ -23,8 +23,9 @@ classifiers =
|
|
|
23
23
|
packages = find:
|
|
24
24
|
python_requires = >=3.9
|
|
25
25
|
install_requires =
|
|
26
|
-
pandas
|
|
27
|
-
numpy
|
|
26
|
+
pandas>=1.3.0
|
|
27
|
+
numpy>=1.20.0
|
|
28
|
+
numba>=0.56.0; python_version>='3.9'
|
|
28
29
|
|
|
29
30
|
[egg_info]
|
|
30
31
|
tag_build =
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data stream collectors for liquidator_indicator package.
|
|
3
|
+
|
|
4
|
+
These are OPTIONAL utilities that help users collect public data feeds.
|
|
5
|
+
Users can either:
|
|
6
|
+
1. Use these collectors to automate data gathering
|
|
7
|
+
2. Implement their own collectors
|
|
8
|
+
3. Feed data from existing sources (files, APIs, etc.)
|
|
9
|
+
|
|
10
|
+
The indicator package itself is data-source agnostic.
|
|
11
|
+
"""
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FundingRateCollector - Collect live funding rates and open interest from Hyperliquid WebSocket.
|
|
3
|
+
|
|
4
|
+
Based on src/data/funding_ws_connection.py pattern but simplified for package distribution.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from liquidator_indicator.collectors.funding import FundingRateCollector
|
|
8
|
+
|
|
9
|
+
collector = FundingRateCollector(symbols=['BTC', 'ETH'])
|
|
10
|
+
collector.start()
|
|
11
|
+
|
|
12
|
+
# Get latest data
|
|
13
|
+
data = collector.get_latest()
|
|
14
|
+
print(data)
|
|
15
|
+
# {'BTC': {'funding_rate': 0.0001, 'open_interest': 12345.67, 'timestamp': '2026-02-02T...'}}
|
|
16
|
+
|
|
17
|
+
# Feed to indicator
|
|
18
|
+
liq.ingest_funding_rates(data)
|
|
19
|
+
|
|
20
|
+
collector.stop()
|
|
21
|
+
"""
|
|
22
|
+
import json
|
|
23
|
+
import time
|
|
24
|
+
import threading
|
|
25
|
+
from datetime import datetime, timezone
|
|
26
|
+
from typing import List, Dict, Optional, Callable
|
|
27
|
+
import logging
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import websocket
|
|
31
|
+
except ImportError:
|
|
32
|
+
raise ImportError("websocket-client required: pip install websocket-client")
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger("liquidator_indicator.funding_collector")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class FundingRateCollector:
|
|
38
|
+
"""Collect live funding rates and open interest from Hyperliquid WebSocket."""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
symbols: List[str],
|
|
43
|
+
ws_url: str = "wss://api.hyperliquid.xyz/ws",
|
|
44
|
+
callback: Optional[Callable] = None
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
Args:
|
|
48
|
+
symbols: List of coin symbols to track (e.g. ['BTC', 'ETH'])
|
|
49
|
+
ws_url: WebSocket endpoint URL
|
|
50
|
+
callback: Optional function called on each update: callback(symbol, data)
|
|
51
|
+
"""
|
|
52
|
+
self.symbols = [s.upper() for s in symbols]
|
|
53
|
+
self.ws_url = ws_url
|
|
54
|
+
self.callback = callback
|
|
55
|
+
|
|
56
|
+
self._data = {} # {symbol: {funding_rate, open_interest, timestamp}}
|
|
57
|
+
self._ws = None
|
|
58
|
+
self._thread = None
|
|
59
|
+
self._running = False
|
|
60
|
+
self._lock = threading.Lock()
|
|
61
|
+
|
|
62
|
+
def start(self):
|
|
63
|
+
"""Start WebSocket connection in background thread."""
|
|
64
|
+
if self._running:
|
|
65
|
+
logger.warning("Collector already running")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
self._running = True
|
|
69
|
+
self._thread = threading.Thread(target=self._run_ws, daemon=True)
|
|
70
|
+
self._thread.start()
|
|
71
|
+
logger.info(f"FundingRateCollector started for {self.symbols}")
|
|
72
|
+
|
|
73
|
+
def stop(self):
|
|
74
|
+
"""Stop WebSocket connection."""
|
|
75
|
+
self._running = False
|
|
76
|
+
if self._ws:
|
|
77
|
+
try:
|
|
78
|
+
self._ws.close()
|
|
79
|
+
except Exception:
|
|
80
|
+
pass
|
|
81
|
+
if self._thread:
|
|
82
|
+
self._thread.join(timeout=2)
|
|
83
|
+
logger.info("FundingRateCollector stopped")
|
|
84
|
+
|
|
85
|
+
def get_latest(self) -> Dict[str, Dict]:
|
|
86
|
+
"""Get latest funding data for all symbols.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
{symbol: {funding_rate, open_interest, timestamp}}
|
|
90
|
+
"""
|
|
91
|
+
with self._lock:
|
|
92
|
+
return self._data.copy()
|
|
93
|
+
|
|
94
|
+
def get_symbol(self, symbol: str) -> Optional[Dict]:
|
|
95
|
+
"""Get latest data for specific symbol."""
|
|
96
|
+
with self._lock:
|
|
97
|
+
return self._data.get(symbol.upper())
|
|
98
|
+
|
|
99
|
+
def _run_ws(self):
|
|
100
|
+
"""WebSocket main loop (runs in background thread)."""
|
|
101
|
+
while self._running:
|
|
102
|
+
try:
|
|
103
|
+
self._ws = websocket.WebSocketApp(
|
|
104
|
+
self.ws_url,
|
|
105
|
+
on_message=self._on_message,
|
|
106
|
+
on_error=self._on_error,
|
|
107
|
+
on_close=self._on_close,
|
|
108
|
+
on_open=self._on_open
|
|
109
|
+
)
|
|
110
|
+
self._ws.run_forever()
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error(f"WebSocket error: {e}")
|
|
113
|
+
|
|
114
|
+
if self._running:
|
|
115
|
+
logger.info("Reconnecting in 5s...")
|
|
116
|
+
time.sleep(5)
|
|
117
|
+
|
|
118
|
+
def _on_open(self, ws):
|
|
119
|
+
"""Subscribe to activeAssetCtx channel for funding rates."""
|
|
120
|
+
logger.info("WebSocket connected")
|
|
121
|
+
for symbol in self.symbols:
|
|
122
|
+
sub_msg = json.dumps({
|
|
123
|
+
"method": "subscribe",
|
|
124
|
+
"subscription": {
|
|
125
|
+
"type": "activeAssetCtx",
|
|
126
|
+
"coin": symbol
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
ws.send(sub_msg)
|
|
130
|
+
logger.info(f"Subscribed to {symbol} funding/OI")
|
|
131
|
+
|
|
132
|
+
def _on_message(self, ws, message):
|
|
133
|
+
"""Process funding rate updates."""
|
|
134
|
+
try:
|
|
135
|
+
if message == '{"channel":"pong"}':
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
data = json.loads(message)
|
|
139
|
+
channel = data.get('channel', '')
|
|
140
|
+
|
|
141
|
+
if channel == 'activeAssetCtx':
|
|
142
|
+
asset_data = data.get('data', {})
|
|
143
|
+
coin = asset_data.get('coin', '')
|
|
144
|
+
ctx = asset_data.get('ctx', {})
|
|
145
|
+
|
|
146
|
+
if coin and ctx and coin in self.symbols:
|
|
147
|
+
funding_rate = float(ctx.get('funding', 0))
|
|
148
|
+
open_interest = float(ctx.get('openInterest', 0))
|
|
149
|
+
timestamp = datetime.now(timezone.utc).isoformat()
|
|
150
|
+
|
|
151
|
+
update = {
|
|
152
|
+
'funding_rate': funding_rate,
|
|
153
|
+
'open_interest': open_interest,
|
|
154
|
+
'timestamp': timestamp
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
with self._lock:
|
|
158
|
+
self._data[coin] = update
|
|
159
|
+
|
|
160
|
+
logger.debug(f"{coin}: funding={funding_rate:.6f}, oi={open_interest:.2f}")
|
|
161
|
+
|
|
162
|
+
# Call user callback if provided
|
|
163
|
+
if self.callback:
|
|
164
|
+
try:
|
|
165
|
+
self.callback(coin, update)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logger.error(f"Callback error: {e}")
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
logger.error(f"Message parse error: {e}")
|
|
171
|
+
|
|
172
|
+
def _on_error(self, ws, error):
|
|
173
|
+
logger.error(f"WebSocket error: {error}")
|
|
174
|
+
|
|
175
|
+
def _on_close(self, ws, close_status_code, close_msg):
|
|
176
|
+
logger.info(f"WebSocket closed: {close_status_code} {close_msg}")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# Example usage
|
|
180
|
+
if __name__ == '__main__':
|
|
181
|
+
logging.basicConfig(level=logging.INFO)
|
|
182
|
+
|
|
183
|
+
def on_update(symbol, data):
|
|
184
|
+
print(f"UPDATE: {symbol} @ {data['timestamp']}")
|
|
185
|
+
print(f" Funding: {data['funding_rate']:.6f}")
|
|
186
|
+
print(f" OI: {data['open_interest']:.2f}")
|
|
187
|
+
|
|
188
|
+
collector = FundingRateCollector(
|
|
189
|
+
symbols=['BTC', 'ETH', 'SOL'],
|
|
190
|
+
callback=on_update
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
collector.start()
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
while True:
|
|
197
|
+
time.sleep(10)
|
|
198
|
+
latest = collector.get_latest()
|
|
199
|
+
print(f"\nLatest data: {len(latest)} symbols")
|
|
200
|
+
except KeyboardInterrupt:
|
|
201
|
+
print("\nStopping...")
|
|
202
|
+
collector.stop()
|