quantjourney-bidask 0.9.3__py3-none-any.whl → 1.0__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.
- quantjourney_bidask/__init__.py +31 -5
- quantjourney_bidask/_compare_edge.py +152 -0
- quantjourney_bidask/edge.py +149 -127
- quantjourney_bidask/edge_expanding.py +44 -58
- quantjourney_bidask/edge_hft.py +126 -0
- quantjourney_bidask/edge_rolling.py +90 -199
- {quantjourney_bidask-0.9.3.dist-info → quantjourney_bidask-1.0.dist-info}/METADATA +93 -35
- quantjourney_bidask-1.0.dist-info/RECORD +11 -0
- quantjourney_bidask/_version.py +0 -7
- quantjourney_bidask/data_fetcher.py +0 -160
- quantjourney_bidask/websocket_fetcher.py +0 -308
- quantjourney_bidask-0.9.3.dist-info/RECORD +0 -12
- {quantjourney_bidask-0.9.3.dist-info → quantjourney_bidask-1.0.dist-info}/WHEEL +0 -0
- {quantjourney_bidask-0.9.3.dist-info → quantjourney_bidask-1.0.dist-info}/licenses/LICENSE +0 -0
- {quantjourney_bidask-0.9.3.dist-info → quantjourney_bidask-1.0.dist-info}/top_level.txt +0 -0
@@ -1,160 +0,0 @@
|
|
1
|
-
import pandas as pd
|
2
|
-
import requests
|
3
|
-
import yfinance as yf
|
4
|
-
from typing import Optional, List
|
5
|
-
from datetime import datetime
|
6
|
-
|
7
|
-
def fetch_binance_data(
|
8
|
-
symbols: List[str],
|
9
|
-
timeframe: str,
|
10
|
-
start: str,
|
11
|
-
end: str,
|
12
|
-
api_key: str,
|
13
|
-
api_url: str = "http://localhost:8000"
|
14
|
-
) -> pd.DataFrame:
|
15
|
-
"""
|
16
|
-
Fetch OHLCV data from Binance using the provided FastAPI server.
|
17
|
-
|
18
|
-
Parameters
|
19
|
-
----------
|
20
|
-
symbols : List[str]
|
21
|
-
List of trading pairs (e.g., ["BTCUSDT", "ETHUSDT"]).
|
22
|
-
timeframe : str
|
23
|
-
Data timeframe (e.g., "1m", "1h", "1d").
|
24
|
-
start : str
|
25
|
-
Start time in ISO 8601 format (e.g., "2024-01-01T00:00:00Z").
|
26
|
-
end : str
|
27
|
-
End time in ISO 8601 format (e.g., "2024-01-02T00:00:00Z").
|
28
|
-
api_key : str
|
29
|
-
API key for authentication.
|
30
|
-
api_url : str, default "http://localhost:8000"
|
31
|
-
Base URL of the FastAPI server.
|
32
|
-
|
33
|
-
Returns
|
34
|
-
-------
|
35
|
-
pd.DataFrame
|
36
|
-
DataFrame with columns ['open', 'high', 'low', 'close', 'volume', 'timestamp', 'symbol'].
|
37
|
-
|
38
|
-
Raises
|
39
|
-
------
|
40
|
-
ValueError
|
41
|
-
If the API request fails or returns an error.
|
42
|
-
"""
|
43
|
-
payload = {
|
44
|
-
"exchange": "binance",
|
45
|
-
"symbols": symbols,
|
46
|
-
"start": start,
|
47
|
-
"end": end,
|
48
|
-
"timeframe": timeframe,
|
49
|
-
"upload_d1": False,
|
50
|
-
"force": False
|
51
|
-
}
|
52
|
-
headers = {"X-API-Key": api_key}
|
53
|
-
|
54
|
-
# Initiate fetch request
|
55
|
-
response = requests.post(f"{api_url}/fetch", json=payload, headers=headers)
|
56
|
-
if response.status_code != 200:
|
57
|
-
raise ValueError(f"Fetch request failed: {response.text}")
|
58
|
-
|
59
|
-
task_id = response.json().get("task_id")
|
60
|
-
if not task_id:
|
61
|
-
raise ValueError("No task ID returned from fetch request")
|
62
|
-
|
63
|
-
# Poll task status
|
64
|
-
while True:
|
65
|
-
status_response = requests.get(f"{api_url}/tasks/{task_id}")
|
66
|
-
if status_response.status_code != 200:
|
67
|
-
raise ValueError(f"Task status check failed: {status_response.text}")
|
68
|
-
|
69
|
-
task = status_response.json().get("task")
|
70
|
-
if task["status"] in ["completed", "failed"]:
|
71
|
-
if task["status"] == "failed":
|
72
|
-
raise ValueError(f"Task failed: {task.get('message')}")
|
73
|
-
break
|
74
|
-
|
75
|
-
# Query data
|
76
|
-
data = []
|
77
|
-
for symbol in symbols:
|
78
|
-
query_payload = {
|
79
|
-
"symbol": symbol,
|
80
|
-
"timeframe": timeframe,
|
81
|
-
"start": start,
|
82
|
-
"end": end
|
83
|
-
}
|
84
|
-
query_response = requests.post(f"{api_url}/d1/query", json=query_payload)
|
85
|
-
if query_response.status_code != 200:
|
86
|
-
raise ValueError(f"Data query failed for {symbol}: {query_response.text}")
|
87
|
-
|
88
|
-
rows = query_response.json().get("data", [])
|
89
|
-
df = pd.DataFrame(rows)
|
90
|
-
if not df.empty:
|
91
|
-
df['symbol'] = symbol
|
92
|
-
data.append(df)
|
93
|
-
|
94
|
-
if not data:
|
95
|
-
raise ValueError("No data retrieved for the specified parameters")
|
96
|
-
|
97
|
-
result = pd.concat(data, ignore_index=True)
|
98
|
-
result['timestamp'] = pd.to_datetime(result['timestamp'])
|
99
|
-
return result[['timestamp', 'symbol', 'open', 'high', 'low', 'close', 'volume']]
|
100
|
-
|
101
|
-
def fetch_yfinance_data(
|
102
|
-
tickers: List[str],
|
103
|
-
period: str = "1mo",
|
104
|
-
interval: str = "1d",
|
105
|
-
start: Optional[str] = None,
|
106
|
-
end: Optional[str] = None
|
107
|
-
) -> pd.DataFrame:
|
108
|
-
"""
|
109
|
-
Fetch OHLCV data from Yahoo Finance using yfinance.
|
110
|
-
|
111
|
-
Parameters
|
112
|
-
----------
|
113
|
-
tickers : List[str]
|
114
|
-
List of ticker symbols (e.g., ["AAPL", "MSFT"]).
|
115
|
-
period : str, default "1mo"
|
116
|
-
Data period (e.g., "1d", "1mo", "1y"). Ignored if start and end are provided.
|
117
|
-
interval : str, default "1d"
|
118
|
-
Data interval (e.g., "1m", "1h", "1d").
|
119
|
-
start : str, optional
|
120
|
-
Start date (e.g., "2024-01-01"). Overrides period if provided.
|
121
|
-
end : str, optional
|
122
|
-
End date (e.g., "2024-01-31"). Overrides period if provided.
|
123
|
-
|
124
|
-
Returns
|
125
|
-
-------
|
126
|
-
pd.DataFrame
|
127
|
-
DataFrame with columns ['open', 'high', 'low', 'close', 'volume', 'timestamp', 'symbol'].
|
128
|
-
|
129
|
-
Raises
|
130
|
-
------
|
131
|
-
ValueError
|
132
|
-
If no data is retrieved for the specified parameters.
|
133
|
-
"""
|
134
|
-
data = []
|
135
|
-
for ticker in tickers:
|
136
|
-
stock = yf.Ticker(ticker)
|
137
|
-
if start and end:
|
138
|
-
df = stock.history(start=start, end=end, interval=interval)
|
139
|
-
else:
|
140
|
-
df = stock.history(period=period, interval=interval)
|
141
|
-
|
142
|
-
if df.empty:
|
143
|
-
continue
|
144
|
-
|
145
|
-
df = df.reset_index()
|
146
|
-
df['symbol'] = ticker
|
147
|
-
df = df.rename(columns={
|
148
|
-
'Date': 'timestamp',
|
149
|
-
'Open': 'open',
|
150
|
-
'High': 'high',
|
151
|
-
'Low': 'low',
|
152
|
-
'Close': 'close',
|
153
|
-
'Volume': 'volume'
|
154
|
-
})
|
155
|
-
data.append(df[['timestamp', 'symbol', 'open', 'high', 'low', 'close', 'volume']])
|
156
|
-
|
157
|
-
if not data:
|
158
|
-
raise ValueError("No data retrieved for the specified parameters")
|
159
|
-
|
160
|
-
return pd.concat(data, ignore_index=True)
|
@@ -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,12 +0,0 @@
|
|
1
|
-
quantjourney_bidask/__init__.py,sha256=vumoRDEDOTclYapknfSwKpCZi9IdfJbukdp7S1-kphA,409
|
2
|
-
quantjourney_bidask/_version.py,sha256=ExaLWHYvdqbI_ddgEgRrj-rvi6RGAjSAg6mmfcNOMEM,220
|
3
|
-
quantjourney_bidask/data_fetcher.py,sha256=GMVf4wRVwIE2JJ2sYAR_CCo56JQnReNhTWTSrZc0-L0,4931
|
4
|
-
quantjourney_bidask/edge.py,sha256=YdL8So3i9CKQsDm6lI6mNRe-ODhisRhipksq-sfRmuk,6274
|
5
|
-
quantjourney_bidask/edge_expanding.py,sha256=gAdow81VBb2rXtfoEzDur8xvu-rwfo1OQbt3LbUfq4w,2268
|
6
|
-
quantjourney_bidask/edge_rolling.py,sha256=z8463emBLaxa0ceUBk9TPfVayFANo8IeDJ_fuDzGcfA,9103
|
7
|
-
quantjourney_bidask/websocket_fetcher.py,sha256=xMS_qLbSW9hCS3RbNKvkn5HTK0XGmAO4wpaAl4_Mxb4,10895
|
8
|
-
quantjourney_bidask-0.9.3.dist-info/licenses/LICENSE,sha256=m8MEOGnpSBtS6m9z4M9m1JksWWPzu1OK3UgY1wuHf04,1081
|
9
|
-
quantjourney_bidask-0.9.3.dist-info/METADATA,sha256=VLnrW86ALr9MLWT_Ch6yxkb25CUqN3m-RhD7JOO62SA,13164
|
10
|
-
quantjourney_bidask-0.9.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
11
|
-
quantjourney_bidask-0.9.3.dist-info/top_level.txt,sha256=rOBM4GxA87iQv-mR8-WZdu3-Yj5ESyggRICpUhJ-4Dg,20
|
12
|
-
quantjourney_bidask-0.9.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|