quantjourney-bidask 0.9.4__py3-none-any.whl → 1.0.1__py3-none-any.whl

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.
@@ -1,308 +0,0 @@
1
- """
2
- WebSocket Live Data Fetcher
3
-
4
- Real-time data fetching for cryptocurrency exchanges using WebSockets.
5
- """
6
-
7
- import json
8
- import threading
9
- import time
10
- import pandas as pd
11
- import numpy as np
12
- from datetime import datetime, timezone
13
- from typing import Dict, List, Callable, Optional
14
- import websocket
15
- from collections import deque
16
- from .edge_rolling import edge_rolling
17
-
18
- class LiveSpreadMonitor:
19
- """
20
- Real-time spread monitoring using WebSocket connections.
21
-
22
- Supports Binance WebSocket streams for live OHLC data and real-time
23
- spread calculation with configurable alerts.
24
- """
25
-
26
- def __init__(self, symbols: List[str], window: int = 20, buffer_size: int = 1000):
27
- """
28
- Initialize the live spread monitor.
29
-
30
- Parameters
31
- ----------
32
- symbols : List[str]
33
- List of trading symbols to monitor (e.g., ['BTCUSDT', 'ETHUSDT'])
34
- window : int
35
- Rolling window size for spread calculation
36
- buffer_size : int
37
- Maximum number of candles to keep in memory
38
- """
39
- self.symbols = [s.lower() for s in symbols]
40
- self.window = window
41
- self.buffer_size = buffer_size
42
-
43
- # Data storage
44
- self.data_buffers = {symbol: deque(maxlen=buffer_size) for symbol in self.symbols}
45
- self.spread_buffers = {symbol: deque(maxlen=buffer_size) for symbol in self.symbols}
46
-
47
- # WebSocket connections
48
- self.ws_connections = {}
49
- self.running = False
50
-
51
- # Callbacks
52
- self.data_callbacks = []
53
- self.alert_callbacks = []
54
-
55
- # Alert thresholds (in basis points)
56
- self.alert_thresholds = {symbol: {'high': 100, 'low': 5} for symbol in self.symbols}
57
-
58
- def add_data_callback(self, callback: Callable):
59
- """Add callback function for new data events."""
60
- self.data_callbacks.append(callback)
61
-
62
- def add_alert_callback(self, callback: Callable):
63
- """Add callback function for alert events."""
64
- self.alert_callbacks.append(callback)
65
-
66
- def set_alert_threshold(self, symbol: str, high_bps: float, low_bps: float):
67
- """Set alert thresholds for a symbol (in basis points)."""
68
- symbol = symbol.lower()
69
- if symbol in self.alert_thresholds:
70
- self.alert_thresholds[symbol] = {'high': high_bps, 'low': low_bps}
71
-
72
- def _create_websocket_url(self, symbols: List[str]) -> str:
73
- """Create Binance WebSocket URL for multiple symbols."""
74
- streams = []
75
- for symbol in symbols:
76
- streams.append(f"{symbol}@kline_1m") # 1-minute klines
77
-
78
- if len(streams) == 1:
79
- return f"wss://stream.binance.com:9443/ws/{streams[0]}"
80
- else:
81
- stream_string = "/".join(streams)
82
- return f"wss://stream.binance.com:9443/stream?streams={stream_string}"
83
-
84
- def _on_message(self, ws, message):
85
- """Handle incoming WebSocket messages."""
86
- try:
87
- data = json.loads(message)
88
-
89
- # Handle multi-stream format
90
- if 'stream' in data:
91
- stream_data = data['data']
92
- symbol = stream_data['s'].lower()
93
- else:
94
- stream_data = data
95
- symbol = stream_data['s'].lower()
96
-
97
- # Extract kline data
98
- kline = stream_data['k']
99
- is_closed = kline['x'] # Whether kline is closed
100
-
101
- if is_closed: # Only process closed candles
102
- candle_data = {
103
- 'timestamp': pd.to_datetime(kline['t'], unit='ms'),
104
- 'symbol': symbol,
105
- 'open': float(kline['o']),
106
- 'high': float(kline['h']),
107
- 'low': float(kline['l']),
108
- 'close': float(kline['c']),
109
- 'volume': float(kline['v'])
110
- }
111
-
112
- self._process_candle(candle_data)
113
-
114
- except Exception as e:
115
- print(f"Error processing message: {e}")
116
-
117
- def _process_candle(self, candle_data: Dict):
118
- """Process new candle data and update spreads."""
119
- symbol = candle_data['symbol']
120
-
121
- # Add to buffer
122
- self.data_buffers[symbol].append(candle_data)
123
-
124
- # Calculate spread if we have enough data
125
- if len(self.data_buffers[symbol]) >= self.window:
126
- # Convert buffer to DataFrame for spread calculation
127
- df = pd.DataFrame(list(self.data_buffers[symbol])[-self.window:])
128
-
129
- # Calculate current spread
130
- try:
131
- current_spread = edge_rolling(df.tail(1), window=min(len(df), self.window)).iloc[-1]
132
-
133
- if not pd.isna(current_spread):
134
- spread_bps = current_spread * 10000 # Convert to basis points
135
-
136
- spread_data = {
137
- 'timestamp': candle_data['timestamp'],
138
- 'symbol': symbol,
139
- 'spread_bps': spread_bps,
140
- 'price': candle_data['close']
141
- }
142
-
143
- self.spread_buffers[symbol].append(spread_data)
144
-
145
- # Check for alerts
146
- self._check_alerts(spread_data)
147
-
148
- # Notify callbacks
149
- for callback in self.data_callbacks:
150
- callback(candle_data, spread_data)
151
-
152
- except Exception as e:
153
- print(f"Error calculating spread for {symbol}: {e}")
154
-
155
- def _check_alerts(self, spread_data: Dict):
156
- """Check if spread triggers any alerts."""
157
- symbol = spread_data['symbol']
158
- spread_bps = spread_data['spread_bps']
159
- thresholds = self.alert_thresholds[symbol]
160
-
161
- alert_type = None
162
- if spread_bps > thresholds['high']:
163
- alert_type = 'HIGH'
164
- elif spread_bps < thresholds['low']:
165
- alert_type = 'LOW'
166
-
167
- if alert_type:
168
- alert_data = {
169
- 'type': alert_type,
170
- 'symbol': symbol,
171
- 'spread_bps': spread_bps,
172
- 'threshold': thresholds[alert_type.lower()],
173
- 'timestamp': spread_data['timestamp'],
174
- 'price': spread_data['price']
175
- }
176
-
177
- for callback in self.alert_callbacks:
178
- callback(alert_data)
179
-
180
- def _on_error(self, ws, error):
181
- """Handle WebSocket errors."""
182
- print(f"WebSocket error: {error}")
183
-
184
- def _on_close(self, ws, close_status_code, close_msg):
185
- """Handle WebSocket connection close."""
186
- print("WebSocket connection closed")
187
-
188
- def _on_open(self, ws):
189
- """Handle WebSocket connection open."""
190
- print(f"WebSocket connected for symbols: {', '.join(self.symbols)}")
191
-
192
- def start(self):
193
- """Start the live monitoring."""
194
- if self.running:
195
- print("Monitor is already running")
196
- return
197
-
198
- self.running = True
199
-
200
- # Create WebSocket URL
201
- ws_url = self._create_websocket_url(self.symbols)
202
-
203
- # Create WebSocket connection
204
- self.ws = websocket.WebSocketApp(
205
- ws_url,
206
- on_message=self._on_message,
207
- on_error=self._on_error,
208
- on_close=self._on_close,
209
- on_open=self._on_open
210
- )
211
-
212
- # Start WebSocket in a separate thread
213
- self.ws_thread = threading.Thread(target=self.ws.run_forever)
214
- self.ws_thread.daemon = True
215
- self.ws_thread.start()
216
-
217
- print("Live spread monitoring started...")
218
-
219
- def stop(self):
220
- """Stop the live monitoring."""
221
- if not self.running:
222
- return
223
-
224
- self.running = False
225
- if hasattr(self, 'ws'):
226
- self.ws.close()
227
-
228
- print("Live spread monitoring stopped.")
229
-
230
- def get_current_data(self) -> Dict[str, pd.DataFrame]:
231
- """Get current data for all symbols."""
232
- result = {}
233
- for symbol in self.symbols:
234
- if len(self.data_buffers[symbol]) > 0:
235
- result[symbol] = pd.DataFrame(list(self.data_buffers[symbol]))
236
- return result
237
-
238
- def get_current_spreads(self) -> Dict[str, pd.DataFrame]:
239
- """Get current spread data for all symbols."""
240
- result = {}
241
- for symbol in self.symbols:
242
- if len(self.spread_buffers[symbol]) > 0:
243
- result[symbol] = pd.DataFrame(list(self.spread_buffers[symbol]))
244
- return result
245
-
246
- def create_live_dashboard_example():
247
- """
248
- Example of creating a live dashboard (console-based).
249
- """
250
- import time
251
-
252
- def data_callback(candle_data, spread_data):
253
- """Print new data to console."""
254
- symbol = spread_data['symbol'].upper()
255
- timestamp = spread_data['timestamp'].strftime('%H:%M:%S')
256
- price = spread_data['price']
257
- spread_bps = spread_data['spread_bps']
258
-
259
- print(f"[{timestamp}] {symbol}: ${price:.2f} | Spread: {spread_bps:.2f}bps")
260
-
261
- def alert_callback(alert_data):
262
- """Print alerts to console."""
263
- symbol = alert_data['symbol'].upper()
264
- alert_type = alert_data['type']
265
- spread_bps = alert_data['spread_bps']
266
- threshold = alert_data['threshold']
267
- timestamp = alert_data['timestamp'].strftime('%H:%M:%S')
268
-
269
- print(f"🚨 [{timestamp}] {alert_type} SPREAD ALERT for {symbol}: "
270
- f"{spread_bps:.2f}bps (threshold: {threshold}bps)")
271
-
272
- # Create monitor
273
- monitor = LiveSpreadMonitor(['BTCUSDT', 'ETHUSDT'], window=10)
274
-
275
- # Set custom thresholds
276
- monitor.set_alert_threshold('BTCUSDT', high_bps=50, low_bps=2)
277
- monitor.set_alert_threshold('ETHUSDT', high_bps=60, low_bps=3)
278
-
279
- # Add callbacks
280
- monitor.add_data_callback(data_callback)
281
- monitor.add_alert_callback(alert_callback)
282
-
283
- return monitor
284
-
285
- if __name__ == "__main__":
286
- print("Live Spread Monitor Example")
287
- print("==========================")
288
- print("This example demonstrates real-time spread monitoring using WebSockets.")
289
- print("Note: This requires an active internet connection and will connect to Binance WebSocket.")
290
- print()
291
-
292
- try:
293
- # Create and start monitor
294
- monitor = create_live_dashboard_example()
295
-
296
- print("Starting live monitor... (Press Ctrl+C to stop)")
297
- monitor.start()
298
-
299
- # Run for a demo period
300
- time.sleep(60) # Run for 1 minute
301
-
302
- except KeyboardInterrupt:
303
- print("\nStopping monitor...")
304
- finally:
305
- if 'monitor' in locals():
306
- monitor.stop()
307
-
308
- print("Example completed.")
@@ -1,11 +0,0 @@
1
- quantjourney_bidask/__init__.py,sha256=ycBwUmX5uZZvnXIygnREyF_VdGQmoXvX-Kkwb0OKegU,298
2
- quantjourney_bidask/_version.py,sha256=7lbdg1SKFTzau2oIJGxKQ7-_f5qeY69-EB_gg5wGNI8,220
3
- quantjourney_bidask/edge.py,sha256=YdL8So3i9CKQsDm6lI6mNRe-ODhisRhipksq-sfRmuk,6274
4
- quantjourney_bidask/edge_expanding.py,sha256=gAdow81VBb2rXtfoEzDur8xvu-rwfo1OQbt3LbUfq4w,2268
5
- quantjourney_bidask/edge_rolling.py,sha256=z8463emBLaxa0ceUBk9TPfVayFANo8IeDJ_fuDzGcfA,9103
6
- quantjourney_bidask/websocket_fetcher.py,sha256=xMS_qLbSW9hCS3RbNKvkn5HTK0XGmAO4wpaAl4_Mxb4,10895
7
- quantjourney_bidask-0.9.4.dist-info/licenses/LICENSE,sha256=m8MEOGnpSBtS6m9z4M9m1JksWWPzu1OK3UgY1wuHf04,1081
8
- quantjourney_bidask-0.9.4.dist-info/METADATA,sha256=AeWjnxksyf-aXjdLycosfszLWGX4RhE28wV1q6kuUPw,13202
9
- quantjourney_bidask-0.9.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- quantjourney_bidask-0.9.4.dist-info/top_level.txt,sha256=rOBM4GxA87iQv-mR8-WZdu3-Yj5ESyggRICpUhJ-4Dg,20
11
- quantjourney_bidask-0.9.4.dist-info/RECORD,,