kuhl-haus-mdp 0.1.7__tar.gz → 0.1.9__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.
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/PKG-INFO +2 -2
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/pyproject.toml +2 -2
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/analyzers/top_stocks.py +17 -4
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/components/market_data_cache.py +0 -71
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/helpers/utils.py +2 -1
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_cache_keys.py +4 -4
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/top_stocks_cache_item.py +3 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/components/test_market_data_cache.py +29 -27
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/models/test_top_stocks_cache_item.py +9 -9
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/LICENSE.txt +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/README.md +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/analyzers/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/analyzers/analyzer.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/analyzers/massive_data_analyzer.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/components/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/components/market_data_scanner.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/components/widget_data_service.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/helpers/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/helpers/process_manager.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/helpers/queue_name_resolver.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/integ/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/integ/massive_data_listener.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/integ/massive_data_processor.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/integ/massive_data_queues.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/integ/web_socket_message_serde.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_analyzer_result.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_cache_ttl.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_pubsub_keys.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_scanner_names.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/massive_data_queue.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/analyzers/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/analyzers/test_massive_data_analyzer.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/analyzers/test_top_stocks_rehydrate.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/components/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/components/test_market_data_scanner.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/components/test_widget_data_service.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/helpers/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/helpers/test_process_manager.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/helpers/test_queue_name_resolver.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/helpers/test_utils.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/integ/__init__.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/integ/test_web_socket_message_serde.py +0 -0
- {kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/tests/models/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kuhl-haus-mdp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Market data processing pipeline for stock market scanner
|
|
5
5
|
Author-Email: Tom Pounders <git@oldschool.engineer>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -28,7 +28,7 @@ License: The MIT License (MIT)
|
|
|
28
28
|
Classifier: Development Status :: 4 - Beta
|
|
29
29
|
Classifier: Programming Language :: Python
|
|
30
30
|
Project-URL: Homepage, https://github.com/kuhl-haus/kuhl-haus-mdp
|
|
31
|
-
Project-URL: Documentation, https://
|
|
31
|
+
Project-URL: Documentation, https://kuhl-haus-mdp.readthedocs.io
|
|
32
32
|
Project-URL: Source, https://github.com/kuhl-haus/kuhl-haus-mdp.git
|
|
33
33
|
Project-URL: Changelog, https://github.com/kuhl-haus/kuhl-haus-mdp/commits
|
|
34
34
|
Project-URL: Tracker, https://github.com/kuhl-haus/kuhl-haus-mdp/issues
|
|
@@ -30,14 +30,14 @@ dependencies = [
|
|
|
30
30
|
"uvicorn[standard]",
|
|
31
31
|
"websockets",
|
|
32
32
|
]
|
|
33
|
-
version = "0.1.
|
|
33
|
+
version = "0.1.9"
|
|
34
34
|
|
|
35
35
|
[project.license]
|
|
36
36
|
file = "LICENSE.txt"
|
|
37
37
|
|
|
38
38
|
[project.urls]
|
|
39
39
|
Homepage = "https://github.com/kuhl-haus/kuhl-haus-mdp"
|
|
40
|
-
Documentation = "https://
|
|
40
|
+
Documentation = "https://kuhl-haus-mdp.readthedocs.io"
|
|
41
41
|
Source = "https://github.com/kuhl-haus/kuhl-haus-mdp.git"
|
|
42
42
|
Changelog = "https://github.com/kuhl-haus/kuhl-haus-mdp/commits"
|
|
43
43
|
Tracker = "https://github.com/kuhl-haus/kuhl-haus-mdp/issues"
|
|
@@ -126,6 +126,7 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
126
126
|
prev_day_close = cached_data["prev_day_close"]
|
|
127
127
|
prev_day_volume = cached_data["prev_day_volume"]
|
|
128
128
|
prev_day_vwap = cached_data["prev_day_vwap"]
|
|
129
|
+
free_float = cached_data["free_float"]
|
|
129
130
|
else:
|
|
130
131
|
# Get snapshot for previous day's data
|
|
131
132
|
retry_count = 0
|
|
@@ -140,8 +141,7 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
140
141
|
prev_day_volume = snapshot.prev_day.volume
|
|
141
142
|
prev_day_vwap = snapshot.prev_day.vwap
|
|
142
143
|
break
|
|
143
|
-
except
|
|
144
|
-
self.logger.error(f"Error getting snapshot for {event.symbol}: {repr(e)}", exc_info=e, stack_info=True)
|
|
144
|
+
except Exception:
|
|
145
145
|
retry_count += 1
|
|
146
146
|
if retry_count == max_tries and prev_day_close == 0:
|
|
147
147
|
self.logger.error(f"Failed to get snapshot for {event.symbol} after {max_tries} tries.")
|
|
@@ -155,13 +155,25 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
155
155
|
try:
|
|
156
156
|
avg_volume = await self.cache.get_avg_volume(event.symbol)
|
|
157
157
|
break
|
|
158
|
-
except
|
|
159
|
-
self.logger.error(f"Error getting average volume for {event.symbol}: {repr(e)}", exc_info=e, stack_info=True)
|
|
158
|
+
except Exception:
|
|
160
159
|
retry_count += 1
|
|
161
160
|
if retry_count == max_tries and avg_volume == 0:
|
|
162
161
|
self.logger.error(f"Failed to get average volume for {event.symbol} after {max_tries} tries.")
|
|
163
162
|
return
|
|
164
163
|
|
|
164
|
+
# Get free float - this uses an experimental API
|
|
165
|
+
retry_count = 0
|
|
166
|
+
max_tries = 2
|
|
167
|
+
free_float = 0
|
|
168
|
+
while retry_count < max_tries:
|
|
169
|
+
try:
|
|
170
|
+
free_float = await self.cache.get_free_float(event.symbol)
|
|
171
|
+
break
|
|
172
|
+
except Exception:
|
|
173
|
+
retry_count += 1
|
|
174
|
+
if retry_count == max_tries and free_float == 0:
|
|
175
|
+
self.logger.error(f"Failed to get free float for {event.symbol} after {max_tries} tries.")
|
|
176
|
+
|
|
165
177
|
# Calculate relative volume
|
|
166
178
|
if avg_volume == 0:
|
|
167
179
|
relative_volume = 0
|
|
@@ -196,6 +208,7 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
196
208
|
self.cache_item.symbol_data_cache[event.symbol] = {
|
|
197
209
|
"symbol": event.symbol,
|
|
198
210
|
"volume": event.volume,
|
|
211
|
+
"free_float": free_float,
|
|
199
212
|
"accumulated_volume": event.accumulated_volume,
|
|
200
213
|
"relative_volume": relative_volume,
|
|
201
214
|
"official_open_price": event.official_open_price,
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/components/market_data_cache.py
RENAMED
|
@@ -57,77 +57,6 @@ class MarketDataCache:
|
|
|
57
57
|
ticker=ticker
|
|
58
58
|
)
|
|
59
59
|
self.logger.info(f"Snapshot result: {snapshot}")
|
|
60
|
-
# data = {
|
|
61
|
-
# "day": {
|
|
62
|
-
# "open": snapshot.day.open,
|
|
63
|
-
# "high": snapshot.day.high,
|
|
64
|
-
# "low": snapshot.day.low,
|
|
65
|
-
# "close": snapshot.day.close,
|
|
66
|
-
# "volume": snapshot.day.volume,
|
|
67
|
-
# "vwap": snapshot.day.vwap,
|
|
68
|
-
# "timestamp": snapshot.day.timestamp,
|
|
69
|
-
# "transactions": snapshot.day.transactions,
|
|
70
|
-
# "otc": snapshot.day.otc,
|
|
71
|
-
# },
|
|
72
|
-
# "last_quote": {
|
|
73
|
-
# "ticker": snapshot.last_quote.ticker,
|
|
74
|
-
# "trf_timestamp": snapshot.last_quote.trf_timestamp,
|
|
75
|
-
# "sequence_number": snapshot.last_quote.sequence_number,
|
|
76
|
-
# "sip_timestamp": snapshot.last_quote.sip_timestamp,
|
|
77
|
-
# "participant_timestamp": snapshot.last_quote.participant_timestamp,
|
|
78
|
-
# "ask_price": snapshot.last_quote.ask_price,
|
|
79
|
-
# "ask_size": snapshot.last_quote.ask_size,
|
|
80
|
-
# "ask_exchange": snapshot.last_quote.ask_exchange,
|
|
81
|
-
# "conditions": snapshot.last_quote.conditions,
|
|
82
|
-
# "indicators": snapshot.last_quote.indicators,
|
|
83
|
-
# "bid_price": snapshot.last_quote.bid_price,
|
|
84
|
-
# "bid_size": snapshot.last_quote.bid_size,
|
|
85
|
-
# "bid_exchange": snapshot.last_quote.bid_exchange,
|
|
86
|
-
# "tape": snapshot.last_quote.tape,
|
|
87
|
-
# },
|
|
88
|
-
# "last_trade": {
|
|
89
|
-
# "ticker": snapshot.last_trade.ticker,
|
|
90
|
-
# "trf_timestamp": snapshot.last_trade.trf_timestamp,
|
|
91
|
-
# "sequence_number": snapshot.last_trade.sequence_number,
|
|
92
|
-
# "sip_timestamp": snapshot.last_trade.sip_timestamp,
|
|
93
|
-
# "participant_timestamp": snapshot.last_trade.participant_timestamp,
|
|
94
|
-
# "conditions": snapshot.last_trade.conditions,
|
|
95
|
-
# "correction": snapshot.last_trade.correction,
|
|
96
|
-
# "id": snapshot.last_trade.id,
|
|
97
|
-
# "price": snapshot.last_trade.price,
|
|
98
|
-
# "trf_id": snapshot.last_trade.trf_id,
|
|
99
|
-
# "size": snapshot.last_trade.size,
|
|
100
|
-
# "exchange": snapshot.last_trade.exchange,
|
|
101
|
-
# "tape": snapshot.last_trade.tape,
|
|
102
|
-
# },
|
|
103
|
-
# "min": {
|
|
104
|
-
# "accumulated_volume": snapshot.min.accumulated_volume,
|
|
105
|
-
# "open": snapshot.min.open,
|
|
106
|
-
# "high": snapshot.min.high,
|
|
107
|
-
# "low": snapshot.min.low,
|
|
108
|
-
# "close": snapshot.min.close,
|
|
109
|
-
# "volume": snapshot.min.volume,
|
|
110
|
-
# "vwap": snapshot.min.vwap,
|
|
111
|
-
# "otc": snapshot.min.otc,
|
|
112
|
-
# "timestamp": snapshot.min.timestamp,
|
|
113
|
-
# "transactions": snapshot.min.transactions,
|
|
114
|
-
# },
|
|
115
|
-
# "prev_day": {
|
|
116
|
-
# "open": snapshot.prev_day.open,
|
|
117
|
-
# "high": snapshot.prev_day.high,
|
|
118
|
-
# "low": snapshot.prev_day.low,
|
|
119
|
-
# "close": snapshot.prev_day.close,
|
|
120
|
-
# "volume": snapshot.prev_day.volume,
|
|
121
|
-
# "vwap": snapshot.prev_day.vwap,
|
|
122
|
-
# "timestamp": snapshot.prev_day.timestamp,
|
|
123
|
-
# "transactions": snapshot.prev_day.transactions,
|
|
124
|
-
# "otc": snapshot.prev_day.otc,
|
|
125
|
-
# },
|
|
126
|
-
# "ticker": snapshot.ticker,
|
|
127
|
-
# "todaysChange": snapshot.todays_change,
|
|
128
|
-
# "todaysChangePerc": snapshot.todays_change_percent,
|
|
129
|
-
# "updated": snapshot.updated,
|
|
130
|
-
# }
|
|
131
60
|
data = ticker_snapshot_to_dict(snapshot)
|
|
132
61
|
await self.cache_data(
|
|
133
62
|
data=data,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
+
from typing import Dict, Any
|
|
3
4
|
|
|
4
5
|
from massive.rest.models import TickerSnapshot
|
|
5
6
|
|
|
@@ -38,7 +39,7 @@ def get_massive_api_key():
|
|
|
38
39
|
return api_key
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
def ticker_snapshot_to_dict(snapshot: TickerSnapshot) ->
|
|
42
|
+
def ticker_snapshot_to_dict(snapshot: TickerSnapshot) -> Dict[str, Any]:
|
|
42
43
|
"""
|
|
43
44
|
Convert a TickerSnapshot instance into a JSON-serializable dictionary.
|
|
44
45
|
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_cache_keys.py
RENAMED
|
@@ -15,10 +15,10 @@ class MarketDataCacheKeys(Enum):
|
|
|
15
15
|
UNKNOWN = 'unknown'
|
|
16
16
|
|
|
17
17
|
# MARKET DATA CACHE
|
|
18
|
-
DAILY_AGGREGATES = 'aggregate:daily'
|
|
19
|
-
TICKER_SNAPSHOTS = 'snapshots'
|
|
20
|
-
TICKER_AVG_VOLUME = 'avg_volume'
|
|
21
|
-
TICKER_FREE_FLOAT = 'free_float'
|
|
18
|
+
DAILY_AGGREGATES = 'mdc:aggregate:daily'
|
|
19
|
+
TICKER_SNAPSHOTS = 'mdc:snapshots'
|
|
20
|
+
TICKER_AVG_VOLUME = 'mdc:avg_volume'
|
|
21
|
+
TICKER_FREE_FLOAT = 'mdc:free_float'
|
|
22
22
|
|
|
23
23
|
# MARKET DATA PROCESSOR CACHE
|
|
24
24
|
TOP_TRADES_SCANNER = f'cache:{MarketDataScannerNames.TOP_TRADES.value}'
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/top_stocks_cache_item.py
RENAMED
|
@@ -45,6 +45,7 @@ class TopStocksCacheItem:
|
|
|
45
45
|
ret.append({
|
|
46
46
|
"symbol": ticker,
|
|
47
47
|
"volume": self.symbol_data_cache[ticker]["volume"],
|
|
48
|
+
"free_float": self.symbol_data_cache[ticker]["free_float"],
|
|
48
49
|
"accumulated_volume": self.symbol_data_cache[ticker]["accumulated_volume"],
|
|
49
50
|
"relative_volume": self.symbol_data_cache[ticker]["relative_volume"],
|
|
50
51
|
"official_open_price": self.symbol_data_cache[ticker]["official_open_price"],
|
|
@@ -81,6 +82,7 @@ class TopStocksCacheItem:
|
|
|
81
82
|
ret.append({
|
|
82
83
|
"symbol": ticker,
|
|
83
84
|
"volume": self.symbol_data_cache[ticker]["volume"],
|
|
85
|
+
"free_float": self.symbol_data_cache[ticker]["free_float"],
|
|
84
86
|
"accumulated_volume": self.symbol_data_cache[ticker]["accumulated_volume"],
|
|
85
87
|
"relative_volume": self.symbol_data_cache[ticker]["relative_volume"],
|
|
86
88
|
"official_open_price": self.symbol_data_cache[ticker]["official_open_price"],
|
|
@@ -117,6 +119,7 @@ class TopStocksCacheItem:
|
|
|
117
119
|
ret.append({
|
|
118
120
|
"symbol": ticker,
|
|
119
121
|
"volume": self.symbol_data_cache[ticker]["volume"],
|
|
122
|
+
"free_float": self.symbol_data_cache[ticker]["free_float"],
|
|
120
123
|
"accumulated_volume": self.symbol_data_cache[ticker]["accumulated_volume"],
|
|
121
124
|
"relative_volume": self.symbol_data_cache[ticker]["relative_volume"],
|
|
122
125
|
"official_open_price": self.symbol_data_cache[ticker]["official_open_price"],
|
|
@@ -5,6 +5,8 @@ import pytest
|
|
|
5
5
|
from kuhl_haus.mdp.components.market_data_cache import MarketDataCache
|
|
6
6
|
from massive.rest.models import TickerSnapshot
|
|
7
7
|
|
|
8
|
+
from kuhl_haus.mdp.models.market_data_cache_keys import MarketDataCacheKeys
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
@pytest.fixture
|
|
10
12
|
def mock_massive_api_key():
|
|
@@ -93,7 +95,7 @@ async def test_get_ticker_snapshot_with_cache_hit_expect_ticker_snapshot_returne
|
|
|
93
95
|
mock_redis_client = AsyncMock()
|
|
94
96
|
mock_rest_client = MagicMock()
|
|
95
97
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
96
|
-
mock_cache_key = "
|
|
98
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_SNAPSHOTS.value}:TEST"
|
|
97
99
|
mock_cached_value = mock_data_dict
|
|
98
100
|
mock_redis_client.get.return_value = json.dumps(mock_cached_value)
|
|
99
101
|
mock_snapshot.return_value = TickerSnapshot(**mock_cached_value)
|
|
@@ -115,7 +117,7 @@ async def test_get_ticker_snapshot_without_cache_hit_expect_ticker_snapshot_retu
|
|
|
115
117
|
mock_redis_client = AsyncMock()
|
|
116
118
|
mock_rest_client = MagicMock()
|
|
117
119
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
118
|
-
mock_cache_key = "
|
|
120
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_SNAPSHOTS.value}:TEST"
|
|
119
121
|
mock_snapshot_instance = MagicMock(spec=TickerSnapshot)
|
|
120
122
|
mock_snapshot_instance.ticker = "TEST"
|
|
121
123
|
mock_snapshot_instance.todays_change = 5.0
|
|
@@ -145,7 +147,7 @@ async def test_get_ticker_snapshot_with_invalid_cache_data_expect_exception(mock
|
|
|
145
147
|
mock_redis_client = AsyncMock()
|
|
146
148
|
mock_rest_client = MagicMock()
|
|
147
149
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
148
|
-
mock_cache_key = "
|
|
150
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_SNAPSHOTS.value}:TEST"
|
|
149
151
|
mock_redis_client.get.return_value = json.dumps({"invalid": "data"})
|
|
150
152
|
mock_from_dict.side_effect = ValueError("Invalid cache data")
|
|
151
153
|
|
|
@@ -163,7 +165,7 @@ async def test_get_ticker_snapshot_with_invalid_cache_data_expect_exception():
|
|
|
163
165
|
mock_redis_client = AsyncMock()
|
|
164
166
|
mock_rest_client = MagicMock()
|
|
165
167
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
166
|
-
mock_cache_key = "
|
|
168
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_SNAPSHOTS.value}:TEST"
|
|
167
169
|
mock_redis_client.get.return_value = json.dumps({"invalid": "data"})
|
|
168
170
|
|
|
169
171
|
# Act & Assert
|
|
@@ -180,7 +182,7 @@ async def test_get_avg_volume_with_cache_hit_expect_cached_value_returned():
|
|
|
180
182
|
mock_redis_client = AsyncMock()
|
|
181
183
|
mock_rest_client = MagicMock()
|
|
182
184
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
183
|
-
mock_cache_key = "
|
|
185
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_AVG_VOLUME.value}:TEST"
|
|
184
186
|
mock_cached_value = 1500000
|
|
185
187
|
mock_redis_client.get.return_value = json.dumps(mock_cached_value)
|
|
186
188
|
|
|
@@ -199,7 +201,7 @@ async def test_get_avg_volume_without_cache_hit_expect_avg_volume_returned():
|
|
|
199
201
|
mock_redis_client = AsyncMock()
|
|
200
202
|
mock_rest_client = MagicMock()
|
|
201
203
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
202
|
-
mock_cache_key = "
|
|
204
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_AVG_VOLUME.value}:TEST"
|
|
203
205
|
mock_avg_volume = 2500000
|
|
204
206
|
|
|
205
207
|
# Create mock FinancialRatio object
|
|
@@ -225,7 +227,7 @@ async def test_get_avg_volume_without_cache_hit_expect_avg_volume_returned():
|
|
|
225
227
|
# mock_redis_client = AsyncMock()
|
|
226
228
|
# mock_rest_client = MagicMock()
|
|
227
229
|
# sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
228
|
-
# mock_cache_key = "
|
|
230
|
+
# mock_cache_key = f"{MarketDataCacheKeys.TICKER_AVG_VOLUME.value}:TEST"
|
|
229
231
|
#
|
|
230
232
|
# mock_redis_client.get.return_value = None
|
|
231
233
|
# mock_rest_client.list_financials_ratios.return_value = iter([])
|
|
@@ -245,7 +247,7 @@ async def test_get_avg_volume_without_cache_hit_expect_avg_volume_returned():
|
|
|
245
247
|
# mock_redis_client = AsyncMock()
|
|
246
248
|
# mock_rest_client = MagicMock()
|
|
247
249
|
# sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
248
|
-
# mock_cache_key = "
|
|
250
|
+
# mock_cache_key = f"{MarketDataCacheKeys.TICKER_AVG_VOLUME.value}:TEST"
|
|
249
251
|
#
|
|
250
252
|
# # Create multiple mock FinancialRatio objects
|
|
251
253
|
# mock_financial_ratio_1 = MagicMock()
|
|
@@ -271,7 +273,7 @@ async def test_get_avg_volume_caches_with_correct_ttl():
|
|
|
271
273
|
mock_redis_client = AsyncMock()
|
|
272
274
|
mock_rest_client = MagicMock()
|
|
273
275
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
274
|
-
mock_cache_key = "
|
|
276
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_AVG_VOLUME.value}:TEST"
|
|
275
277
|
mock_avg_volume = 3500000
|
|
276
278
|
|
|
277
279
|
# Create mock FinancialRatio object
|
|
@@ -300,7 +302,7 @@ async def test_get_avg_volume_caches_with_correct_ttl():
|
|
|
300
302
|
mock_redis_client = AsyncMock()
|
|
301
303
|
mock_rest_client = MagicMock()
|
|
302
304
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
303
|
-
mock_cache_key = "
|
|
305
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_AVG_VOLUME.value}:TEST"
|
|
304
306
|
mock_avg_volume = 3500000
|
|
305
307
|
|
|
306
308
|
# Create mock FinancialRatio object
|
|
@@ -329,12 +331,12 @@ async def test_get_free_float_with_cache_hit_expect_cached_value_returned():
|
|
|
329
331
|
mock_redis_client = AsyncMock()
|
|
330
332
|
mock_rest_client = MagicMock()
|
|
331
333
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
332
|
-
mock_cache_key = "
|
|
334
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_FREE_FLOAT.value}:TEST"
|
|
333
335
|
mock_cached_value = 2643494955
|
|
334
336
|
mock_redis_client.get.return_value = json.dumps(mock_cached_value)
|
|
335
337
|
|
|
336
338
|
# Act
|
|
337
|
-
result = await sut.get_free_float("
|
|
339
|
+
result = await sut.get_free_float("TEST")
|
|
338
340
|
|
|
339
341
|
# Assert
|
|
340
342
|
mock_redis_client.get.assert_awaited_once_with(mock_cache_key)
|
|
@@ -347,7 +349,7 @@ async def test_get_free_float_without_cache_hit_expect_free_float_returned():
|
|
|
347
349
|
mock_redis_client = AsyncMock()
|
|
348
350
|
mock_rest_client = MagicMock()
|
|
349
351
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_api_key")
|
|
350
|
-
mock_cache_key = "
|
|
352
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_FREE_FLOAT.value}:TEST"
|
|
351
353
|
mock_free_float = 2643494955
|
|
352
354
|
|
|
353
355
|
# Mock API response
|
|
@@ -358,7 +360,7 @@ async def test_get_free_float_without_cache_hit_expect_free_float_returned():
|
|
|
358
360
|
"effective_date": "2025-11-14",
|
|
359
361
|
"free_float": mock_free_float,
|
|
360
362
|
"free_float_percent": 79.5,
|
|
361
|
-
"ticker": "
|
|
363
|
+
"ticker": "TEST"
|
|
362
364
|
}
|
|
363
365
|
],
|
|
364
366
|
"status": "OK"
|
|
@@ -382,14 +384,14 @@ async def test_get_free_float_without_cache_hit_expect_free_float_returned():
|
|
|
382
384
|
sut.http_session = mock_session
|
|
383
385
|
|
|
384
386
|
# Act
|
|
385
|
-
result = await sut.get_free_float("
|
|
387
|
+
result = await sut.get_free_float("TEST")
|
|
386
388
|
|
|
387
389
|
# Assert
|
|
388
390
|
mock_redis_client.get.assert_awaited_once_with(mock_cache_key)
|
|
389
391
|
mock_session.get.assert_called_once()
|
|
390
392
|
call_args = mock_session.get.call_args
|
|
391
393
|
assert call_args[0][0] == "https://api.massive.com/stocks/vX/float"
|
|
392
|
-
assert call_args[1]["params"]["ticker"] == "
|
|
394
|
+
assert call_args[1]["params"]["ticker"] == "TEST"
|
|
393
395
|
assert call_args[1]["params"]["apiKey"] == "test_api_key"
|
|
394
396
|
mock_response.json.assert_awaited_once()
|
|
395
397
|
mock_redis_client.setex.assert_awaited_once()
|
|
@@ -402,7 +404,7 @@ async def test_get_free_float_caches_with_correct_ttl():
|
|
|
402
404
|
mock_redis_client = AsyncMock()
|
|
403
405
|
mock_rest_client = MagicMock()
|
|
404
406
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
405
|
-
mock_cache_key = "
|
|
407
|
+
mock_cache_key = f"{MarketDataCacheKeys.TICKER_FREE_FLOAT.value}:TEST"
|
|
406
408
|
mock_free_float = 2643494955
|
|
407
409
|
|
|
408
410
|
# Mock API response
|
|
@@ -413,7 +415,7 @@ async def test_get_free_float_caches_with_correct_ttl():
|
|
|
413
415
|
"effective_date": "2025-11-14",
|
|
414
416
|
"free_float": mock_free_float,
|
|
415
417
|
"free_float_percent": 79.5,
|
|
416
|
-
"ticker": "
|
|
418
|
+
"ticker": "TEST"
|
|
417
419
|
}
|
|
418
420
|
],
|
|
419
421
|
"status": "OK"
|
|
@@ -436,7 +438,7 @@ async def test_get_free_float_caches_with_correct_ttl():
|
|
|
436
438
|
sut.http_session = mock_session
|
|
437
439
|
|
|
438
440
|
# Act
|
|
439
|
-
result = await sut.get_free_float("
|
|
441
|
+
result = await sut.get_free_float("TEST")
|
|
440
442
|
|
|
441
443
|
# Assert
|
|
442
444
|
mock_redis_client.get.assert_awaited_once_with(mock_cache_key)
|
|
@@ -482,8 +484,8 @@ async def test_get_free_float_with_empty_results_expect_exception():
|
|
|
482
484
|
sut.http_session = mock_session
|
|
483
485
|
|
|
484
486
|
# Act & Assert
|
|
485
|
-
with pytest.raises(Exception, match="No free float data returned for
|
|
486
|
-
await sut.get_free_float("
|
|
487
|
+
with pytest.raises(Exception, match="No free float data returned for TEST"):
|
|
488
|
+
await sut.get_free_float("TEST")
|
|
487
489
|
|
|
488
490
|
mock_redis_client.setex.assert_not_awaited()
|
|
489
491
|
|
|
@@ -523,8 +525,8 @@ async def test_get_free_float_with_invalid_status_expect_exception():
|
|
|
523
525
|
sut.http_session = mock_session
|
|
524
526
|
|
|
525
527
|
# Act & Assert
|
|
526
|
-
with pytest.raises(Exception, match="Invalid response from Massive API for
|
|
527
|
-
await sut.get_free_float("
|
|
528
|
+
with pytest.raises(Exception, match="Invalid response from Massive API for TEST"):
|
|
529
|
+
await sut.get_free_float("TEST")
|
|
528
530
|
|
|
529
531
|
mock_redis_client.setex.assert_not_awaited()
|
|
530
532
|
|
|
@@ -559,7 +561,7 @@ async def test_get_free_float_with_client_error_expect_exception():
|
|
|
559
561
|
|
|
560
562
|
# Act & Assert
|
|
561
563
|
with pytest.raises(aiohttp.ClientError, match="Connection timeout"):
|
|
562
|
-
await sut.get_free_float("
|
|
564
|
+
await sut.get_free_float("TEST")
|
|
563
565
|
|
|
564
566
|
mock_redis_client.setex.assert_not_awaited()
|
|
565
567
|
|
|
@@ -592,7 +594,7 @@ async def test_get_free_float_with_http_error_expect_exception():
|
|
|
592
594
|
|
|
593
595
|
# Act & Assert
|
|
594
596
|
with pytest.raises(Exception, match="HTTP 500 Error"):
|
|
595
|
-
await sut.get_free_float("
|
|
597
|
+
await sut.get_free_float("TEST")
|
|
596
598
|
|
|
597
599
|
mock_redis_client.setex.assert_not_awaited()
|
|
598
600
|
|
|
@@ -674,8 +676,8 @@ async def test_publish_data_expect_publish_called():
|
|
|
674
676
|
mock_rest_client = MagicMock()
|
|
675
677
|
sut = MarketDataCache(rest_client=mock_rest_client, redis_client=mock_redis_client, massive_api_key="test_key")
|
|
676
678
|
|
|
677
|
-
test_data = {"symbol": "
|
|
678
|
-
test_publish_key = "market:updates:
|
|
679
|
+
test_data = {"symbol": "TEST", "price": 250.50, "volume": 1000000}
|
|
680
|
+
test_publish_key = "market:updates:TEST"
|
|
679
681
|
|
|
680
682
|
# Act
|
|
681
683
|
await sut.publish_data(data=test_data, publish_key=test_publish_key)
|
|
@@ -35,17 +35,17 @@ class TestTopStocksCacheItem(unittest.TestCase):
|
|
|
35
35
|
"""Test the top_volume method with a limit."""
|
|
36
36
|
self.cache_item.top_volume_map = {"AAPL": 1200, "GOOG": 600, "AMZN": 500}
|
|
37
37
|
self.cache_item.symbol_data_cache = {
|
|
38
|
-
"AAPL": {"volume": 1000, "accumulated_volume": 1200, "relative_volume": 1.2, "official_open_price": 150,
|
|
38
|
+
"AAPL": {"volume": 1000, "free_float": 14831485766, "accumulated_volume": 1200, "relative_volume": 1.2, "official_open_price": 150,
|
|
39
39
|
"vwap": 155, "open": 145, "close": 152, "high": 160, "low": 142, "aggregate_vwap": 156,
|
|
40
40
|
"average_size": 50, "avg_volume": 1000, "prev_day_close": 148, "prev_day_volume": 900,
|
|
41
41
|
"prev_day_vwap": 154, "change": 4, "pct_change": 2.7, "change_since_open": 7,
|
|
42
42
|
"pct_change_since_open": 4.8, "start_timestamp": 100000, "end_timestamp": 110000},
|
|
43
|
-
"AMZN": {"volume": 300, "accumulated_volume": 500, "relative_volume": 1.2, "official_open_price": 3300,
|
|
43
|
+
"AMZN": {"volume": 300, "free_float": 9698671061, "accumulated_volume": 500, "relative_volume": 1.2, "official_open_price": 3300,
|
|
44
44
|
"vwap": 3500, "open": 3250, "close": 3520, "high": 3600, "low": 3200, "aggregate_vwap": 3450,
|
|
45
45
|
"average_size": 85, "avg_volume": 4000, "prev_day_close": 3200, "prev_day_volume": 3900,
|
|
46
46
|
"prev_day_vwap": 3400, "change": 200, "pct_change": 10.0, "change_since_open": 270,
|
|
47
47
|
"pct_change_since_open": 8.3, "start_timestamp": 300000, "end_timestamp": 310000},
|
|
48
|
-
"GOOG": {"volume": 500, "accumulated_volume": 600, "relative_volume": 1.0, "official_open_price": 2500,
|
|
48
|
+
"GOOG": {"volume": 500, "free_float": 5029591400, "accumulated_volume": 600, "relative_volume": 1.0, "official_open_price": 2500,
|
|
49
49
|
"vwap": 2550, "open": 2450, "close": 2520, "high": 2600, "low": 2400, "aggregate_vwap": 2560,
|
|
50
50
|
"average_size": 65, "avg_volume": 2000, "prev_day_close": 2510, "prev_day_volume": 1900,
|
|
51
51
|
"prev_day_vwap": 2530, "change": 10, "pct_change": 0.4, "change_since_open": 70,
|
|
@@ -60,17 +60,17 @@ class TestTopStocksCacheItem(unittest.TestCase):
|
|
|
60
60
|
"""Test the top_gappers method with a limit."""
|
|
61
61
|
self.cache_item.top_gappers_map = {"AAPL": 5.0, "GOOG": -3.0, "AMZN": 10.0}
|
|
62
62
|
self.cache_item.symbol_data_cache = {
|
|
63
|
-
"AAPL": {"volume": 1000, "accumulated_volume": 1200, "relative_volume": 1.5, "official_open_price": 150,
|
|
63
|
+
"AAPL": {"volume": 1000, "free_float": 14831485766, "accumulated_volume": 1200, "relative_volume": 1.5, "official_open_price": 150,
|
|
64
64
|
"vwap": 155, "open": 145, "close": 152, "high": 160, "low": 142, "aggregate_vwap": 156,
|
|
65
65
|
"average_size": 50, "avg_volume": 1000, "prev_day_close": 148, "prev_day_volume": 900,
|
|
66
66
|
"prev_day_vwap": 154, "change": 4, "pct_change": 5.0, "change_since_open": 7,
|
|
67
67
|
"pct_change_since_open": 2.5, "start_timestamp": 100000, "end_timestamp": 110000},
|
|
68
|
-
"AMZN": {"volume": 300, "accumulated_volume": 500, "relative_volume": 1.2, "official_open_price": 3300,
|
|
68
|
+
"AMZN": {"volume": 300, "free_float": 9698671061, "accumulated_volume": 500, "relative_volume": 1.2, "official_open_price": 3300,
|
|
69
69
|
"vwap": 3500, "open": 3250, "close": 3520, "high": 3600, "low": 3200, "aggregate_vwap": 3450,
|
|
70
70
|
"average_size": 85, "avg_volume": 4000, "prev_day_close": 3200, "prev_day_volume": 3900,
|
|
71
71
|
"prev_day_vwap": 3400, "change": 200, "pct_change": 10.0, "change_since_open": 270,
|
|
72
72
|
"pct_change_since_open": 8.3, "start_timestamp": 300000, "end_timestamp": 310000},
|
|
73
|
-
"GOOG": {"volume": 500, "accumulated_volume": 600, "relative_volume": 1.0, "official_open_price": 2500,
|
|
73
|
+
"GOOG": {"volume": 500, "free_float": 5029591400, "accumulated_volume": 600, "relative_volume": 1.0, "official_open_price": 2500,
|
|
74
74
|
"vwap": 2550, "open": 2450, "close": 2520, "high": 2600, "low": 2400, "aggregate_vwap": 2560,
|
|
75
75
|
"average_size": 65, "avg_volume": 2000, "prev_day_close": 2510, "prev_day_volume": 1900,
|
|
76
76
|
"prev_day_vwap": 2530, "change": 10, "pct_change": 0.4, "change_since_open": 70,
|
|
@@ -84,17 +84,17 @@ class TestTopStocksCacheItem(unittest.TestCase):
|
|
|
84
84
|
"""Test the top_gainers method with a limit."""
|
|
85
85
|
self.cache_item.top_gainers_map = {"AAPL": 2.5, "GOOG": -0.5, "AMZN": 8.3}
|
|
86
86
|
self.cache_item.symbol_data_cache = {
|
|
87
|
-
"AAPL": {"volume": 500, "accumulated_volume": 800, "relative_volume": 1.6, "official_open_price": 140,
|
|
87
|
+
"AAPL": {"volume": 500, "free_float": 14831485766, "accumulated_volume": 800, "relative_volume": 1.6, "official_open_price": 140,
|
|
88
88
|
"vwap": 145, "open": 130, "close": 150, "high": 155, "low": 128, "aggregate_vwap": 150,
|
|
89
89
|
"average_size": 45, "avg_volume": 900, "prev_day_close": 142, "prev_day_volume": 850,
|
|
90
90
|
"prev_day_vwap": 145, "change": 8, "pct_change": 2.5, "change_since_open": 20,
|
|
91
91
|
"pct_change_since_open": 15.4, "start_timestamp": 150000, "end_timestamp": 160000},
|
|
92
|
-
"AMZN": {"volume": 800, "accumulated_volume": 1200, "relative_volume": 1.4, "official_open_price": 3200,
|
|
92
|
+
"AMZN": {"volume": 800, "free_float": 9698671061, "accumulated_volume": 1200, "relative_volume": 1.4, "official_open_price": 3200,
|
|
93
93
|
"vwap": 3300, "open": 3150, "close": 3400, "high": 3450, "low": 3100, "aggregate_vwap": 3350,
|
|
94
94
|
"average_size": 70, "avg_volume": 3900, "prev_day_close": 3250, "prev_day_volume": 3800,
|
|
95
95
|
"prev_day_vwap": 3300, "change": 150, "pct_change": 4.6, "change_since_open": 250,
|
|
96
96
|
"pct_change_since_open": 8.3, "start_timestamp": 170000, "end_timestamp": 180000},
|
|
97
|
-
"GOOG": {"volume": 500, "accumulated_volume": 600, "relative_volume": 1.0, "official_open_price": 2500,
|
|
97
|
+
"GOOG": {"volume": 500, "free_float": 5029591400, "accumulated_volume": 600, "relative_volume": 1.0, "official_open_price": 2500,
|
|
98
98
|
"vwap": 2550, "open": 2450, "close": 2520, "high": 2600, "low": 2400, "aggregate_vwap": 2560,
|
|
99
99
|
"average_size": 65, "avg_volume": 2000, "prev_day_close": 2510, "prev_day_volume": 1900,
|
|
100
100
|
"prev_day_vwap": 2530, "change": 10, "pct_change": 0.4, "change_since_open": 70,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/analyzers/massive_data_analyzer.py
RENAMED
|
File without changes
|
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/components/market_data_scanner.py
RENAMED
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/components/widget_data_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/helpers/queue_name_resolver.py
RENAMED
|
File without changes
|
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/integ/massive_data_listener.py
RENAMED
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/integ/massive_data_processor.py
RENAMED
|
File without changes
|
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/integ/web_socket_message_serde.py
RENAMED
|
File without changes
|
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_analyzer_result.py
RENAMED
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_cache_ttl.py
RENAMED
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_pubsub_keys.py
RENAMED
|
File without changes
|
{kuhl_haus_mdp-0.1.7 → kuhl_haus_mdp-0.1.9}/src/kuhl_haus/mdp/models/market_data_scanner_names.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|