kuhl-haus-mdp 0.1.8__py3-none-any.whl → 0.1.10__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.
- kuhl_haus/mdp/analyzers/massive_data_analyzer.py +8 -8
- kuhl_haus/mdp/analyzers/top_stocks.py +15 -15
- kuhl_haus/mdp/components/market_data_cache.py +5 -3
- kuhl_haus/mdp/models/constants.py +24 -0
- kuhl_haus/mdp/models/market_data_cache_ttl.py +25 -15
- {kuhl_haus_mdp-0.1.8.dist-info → kuhl_haus_mdp-0.1.10.dist-info}/METADATA +2 -2
- {kuhl_haus_mdp-0.1.8.dist-info → kuhl_haus_mdp-0.1.10.dist-info}/RECORD +10 -9
- {kuhl_haus_mdp-0.1.8.dist-info → kuhl_haus_mdp-0.1.10.dist-info}/WHEEL +0 -0
- {kuhl_haus_mdp-0.1.8.dist-info → kuhl_haus_mdp-0.1.10.dist-info}/entry_points.txt +0 -0
- {kuhl_haus_mdp-0.1.8.dist-info → kuhl_haus_mdp-0.1.10.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -50,7 +50,7 @@ class MassiveDataAnalyzer:
|
|
|
50
50
|
return [MarketDataAnalyzerResult(
|
|
51
51
|
data=data,
|
|
52
52
|
cache_key=f"{MarketDataCacheKeys.HALTS.value}:{symbol}",
|
|
53
|
-
cache_ttl=MarketDataCacheTTL.
|
|
53
|
+
cache_ttl=MarketDataCacheTTL.HALTS.value,
|
|
54
54
|
publish_key=f"{MarketDataCacheKeys.HALTS.value}:{symbol}",
|
|
55
55
|
)]
|
|
56
56
|
|
|
@@ -58,8 +58,8 @@ class MassiveDataAnalyzer:
|
|
|
58
58
|
def handle_equity_agg_event(data: dict, symbol: str) -> Optional[List[MarketDataAnalyzerResult]]:
|
|
59
59
|
return [MarketDataAnalyzerResult(
|
|
60
60
|
data=data,
|
|
61
|
-
cache_key=f"{MarketDataCacheKeys.AGGREGATE.value}:{symbol}",
|
|
62
|
-
cache_ttl=MarketDataCacheTTL.
|
|
61
|
+
# cache_key=f"{MarketDataCacheKeys.AGGREGATE.value}:{symbol}",
|
|
62
|
+
# cache_ttl=MarketDataCacheTTL.AGGREGATE.value,
|
|
63
63
|
publish_key=f"{MarketDataCacheKeys.AGGREGATE.value}:{symbol}",
|
|
64
64
|
)]
|
|
65
65
|
|
|
@@ -67,8 +67,8 @@ class MassiveDataAnalyzer:
|
|
|
67
67
|
def handle_equity_trade_event(data: dict, symbol: str) -> Optional[List[MarketDataAnalyzerResult]]:
|
|
68
68
|
return [MarketDataAnalyzerResult(
|
|
69
69
|
data=data,
|
|
70
|
-
cache_key=f"{MarketDataCacheKeys.TRADES.value}:{symbol}",
|
|
71
|
-
cache_ttl=MarketDataCacheTTL.
|
|
70
|
+
# cache_key=f"{MarketDataCacheKeys.TRADES.value}:{symbol}",
|
|
71
|
+
# cache_ttl=MarketDataCacheTTL.TRADES.value,
|
|
72
72
|
publish_key=f"{MarketDataCacheKeys.TRADES.value}:{symbol}",
|
|
73
73
|
)]
|
|
74
74
|
|
|
@@ -76,8 +76,8 @@ class MassiveDataAnalyzer:
|
|
|
76
76
|
def handle_equity_quote_event(data: dict, symbol: str) -> Optional[List[MarketDataAnalyzerResult]]:
|
|
77
77
|
return [MarketDataAnalyzerResult(
|
|
78
78
|
data=data,
|
|
79
|
-
cache_key=f"{MarketDataCacheKeys.QUOTES.value}:{symbol}",
|
|
80
|
-
cache_ttl=MarketDataCacheTTL.
|
|
79
|
+
# cache_key=f"{MarketDataCacheKeys.QUOTES.value}:{symbol}",
|
|
80
|
+
# cache_ttl=MarketDataCacheTTL.QUOTES.value,
|
|
81
81
|
publish_key=f"{MarketDataCacheKeys.QUOTES.value}:{symbol}",
|
|
82
82
|
)]
|
|
83
83
|
|
|
@@ -88,6 +88,6 @@ class MassiveDataAnalyzer:
|
|
|
88
88
|
return [MarketDataAnalyzerResult(
|
|
89
89
|
data=data,
|
|
90
90
|
cache_key=cache_key,
|
|
91
|
-
cache_ttl=MarketDataCacheTTL.
|
|
91
|
+
cache_ttl=MarketDataCacheTTL.UNKNOWN.value,
|
|
92
92
|
publish_key=f"{MarketDataCacheKeys.UNKNOWN.value}",
|
|
93
93
|
)]
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import time
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
|
-
from typing import Optional, List
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from typing import Optional, List
|
|
5
5
|
from zoneinfo import ZoneInfo
|
|
6
6
|
|
|
7
|
-
from massive.exceptions import BadResponse
|
|
8
|
-
from massive.rest import RESTClient
|
|
9
|
-
from massive.rest.models import (
|
|
10
|
-
TickerSnapshot,
|
|
11
|
-
Agg,
|
|
12
|
-
)
|
|
13
7
|
from massive.websocket.models import (
|
|
14
8
|
EquityAgg,
|
|
15
9
|
EventType
|
|
@@ -19,6 +13,7 @@ from kuhl_haus.mdp.analyzers.analyzer import Analyzer
|
|
|
19
13
|
from kuhl_haus.mdp.components.market_data_cache import MarketDataCache
|
|
20
14
|
from kuhl_haus.mdp.models.market_data_analyzer_result import MarketDataAnalyzerResult
|
|
21
15
|
from kuhl_haus.mdp.models.market_data_cache_keys import MarketDataCacheKeys
|
|
16
|
+
from kuhl_haus.mdp.models.market_data_cache_ttl import MarketDataCacheTTL
|
|
22
17
|
from kuhl_haus.mdp.models.market_data_pubsub_keys import MarketDataPubSubKeys
|
|
23
18
|
from kuhl_haus.mdp.models.top_stocks_cache_item import TopStocksCacheItem
|
|
24
19
|
|
|
@@ -94,24 +89,24 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
94
89
|
MarketDataAnalyzerResult(
|
|
95
90
|
data=self.cache_item.to_dict(),
|
|
96
91
|
cache_key=self.cache_key,
|
|
97
|
-
cache_ttl=
|
|
92
|
+
cache_ttl=MarketDataCacheTTL.TOP_STOCKS_SCANNER.value,
|
|
98
93
|
),
|
|
99
94
|
MarketDataAnalyzerResult(
|
|
100
95
|
data=self.cache_item.top_volume(100),
|
|
101
96
|
cache_key=MarketDataPubSubKeys.TOP_VOLUME_SCANNER.value,
|
|
102
|
-
cache_ttl=
|
|
97
|
+
cache_ttl=MarketDataCacheTTL.TOP_VOLUME_SCANNER.value,
|
|
103
98
|
publish_key=MarketDataPubSubKeys.TOP_VOLUME_SCANNER.value,
|
|
104
99
|
),
|
|
105
100
|
MarketDataAnalyzerResult(
|
|
106
101
|
data=self.cache_item.top_gainers(500),
|
|
107
102
|
cache_key=MarketDataPubSubKeys.TOP_GAINERS_SCANNER.value,
|
|
108
|
-
cache_ttl=
|
|
103
|
+
cache_ttl=MarketDataCacheTTL.TOP_GAINERS_SCANNER.value,
|
|
109
104
|
publish_key=MarketDataPubSubKeys.TOP_GAINERS_SCANNER.value,
|
|
110
105
|
),
|
|
111
106
|
MarketDataAnalyzerResult(
|
|
112
107
|
data=self.cache_item.top_gappers(500),
|
|
113
108
|
cache_key=MarketDataPubSubKeys.TOP_GAPPERS_SCANNER.value,
|
|
114
|
-
cache_ttl=
|
|
109
|
+
cache_ttl=MarketDataCacheTTL.TOP_GAPPERS_SCANNER.value,
|
|
115
110
|
publish_key=MarketDataPubSubKeys.TOP_GAPPERS_SCANNER.value,
|
|
116
111
|
)
|
|
117
112
|
]
|
|
@@ -141,10 +136,12 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
141
136
|
prev_day_volume = snapshot.prev_day.volume
|
|
142
137
|
prev_day_vwap = snapshot.prev_day.vwap
|
|
143
138
|
break
|
|
144
|
-
except Exception:
|
|
139
|
+
except Exception as e:
|
|
140
|
+
self.logger.error(f"Failed to get snapshot for {event.symbol}: {e}")
|
|
145
141
|
retry_count += 1
|
|
146
142
|
if retry_count == max_tries and prev_day_close == 0:
|
|
147
143
|
self.logger.error(f"Failed to get snapshot for {event.symbol} after {max_tries} tries.")
|
|
144
|
+
return
|
|
148
145
|
|
|
149
146
|
# Get average volume
|
|
150
147
|
retry_count = 0
|
|
@@ -154,10 +151,12 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
154
151
|
try:
|
|
155
152
|
avg_volume = await self.cache.get_avg_volume(event.symbol)
|
|
156
153
|
break
|
|
157
|
-
except Exception:
|
|
154
|
+
except Exception as e:
|
|
155
|
+
self.logger.error(f"Failed to get average volume for {event.symbol}: {e}")
|
|
158
156
|
retry_count += 1
|
|
159
157
|
if retry_count == max_tries and avg_volume == 0:
|
|
160
158
|
self.logger.error(f"Failed to get average volume for {event.symbol} after {max_tries} tries.")
|
|
159
|
+
return
|
|
161
160
|
|
|
162
161
|
# Get free float - this uses an experimental API
|
|
163
162
|
retry_count = 0
|
|
@@ -167,7 +166,8 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
167
166
|
try:
|
|
168
167
|
free_float = await self.cache.get_free_float(event.symbol)
|
|
169
168
|
break
|
|
170
|
-
except Exception:
|
|
169
|
+
except Exception as e:
|
|
170
|
+
self.logger.error(f"Failed to get free float for {event.symbol}: {e}")
|
|
171
171
|
retry_count += 1
|
|
172
172
|
if retry_count == max_tries and free_float == 0:
|
|
173
173
|
self.logger.error(f"Failed to get free float for {event.symbol} after {max_tries} tries.")
|
|
@@ -61,7 +61,7 @@ class MarketDataCache:
|
|
|
61
61
|
await self.cache_data(
|
|
62
62
|
data=data,
|
|
63
63
|
cache_key=cache_key,
|
|
64
|
-
cache_ttl=MarketDataCacheTTL.
|
|
64
|
+
cache_ttl=MarketDataCacheTTL.TICKER_SNAPSHOTS.value
|
|
65
65
|
)
|
|
66
66
|
return snapshot
|
|
67
67
|
|
|
@@ -109,13 +109,15 @@ class MarketDataCache:
|
|
|
109
109
|
periods_calculated += 1
|
|
110
110
|
else:
|
|
111
111
|
break
|
|
112
|
+
if periods_calculated == 0:
|
|
113
|
+
raise Exception(f"No volume data returned for {ticker}")
|
|
112
114
|
avg_volume = total_volume / periods_calculated
|
|
113
115
|
|
|
114
116
|
self.logger.info(f"average volume {ticker}: {avg_volume}")
|
|
115
117
|
await self.cache_data(
|
|
116
118
|
data=avg_volume,
|
|
117
119
|
cache_key=cache_key,
|
|
118
|
-
cache_ttl=MarketDataCacheTTL.
|
|
120
|
+
cache_ttl=MarketDataCacheTTL.TICKER_AVG_VOLUME.value
|
|
119
121
|
)
|
|
120
122
|
return avg_volume
|
|
121
123
|
|
|
@@ -162,7 +164,7 @@ class MarketDataCache:
|
|
|
162
164
|
await self.cache_data(
|
|
163
165
|
data=free_float,
|
|
164
166
|
cache_key=cache_key,
|
|
165
|
-
cache_ttl=MarketDataCacheTTL.
|
|
167
|
+
cache_ttl=MarketDataCacheTTL.TICKER_FREE_FLOAT.value
|
|
166
168
|
)
|
|
167
169
|
return free_float
|
|
168
170
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Minutes in seconds
|
|
2
|
+
ONE_MINUTE = 60
|
|
3
|
+
FIVE_MINUTES = 300
|
|
4
|
+
TEN_MINUTES = 600
|
|
5
|
+
FIFTEEN_MINUTES = 900
|
|
6
|
+
TWENTY_MINUTES = 1200
|
|
7
|
+
THIRTY_MINUTES = 1800
|
|
8
|
+
|
|
9
|
+
# Hours in seconds
|
|
10
|
+
ONE_HOUR = 3600
|
|
11
|
+
TWO_HOURS = 7200
|
|
12
|
+
FOUR_HOURS = 14400
|
|
13
|
+
SIX_HOURS = 21600
|
|
14
|
+
EIGHT_HOURS = 28800
|
|
15
|
+
TWELVE_HOURS = 43200
|
|
16
|
+
|
|
17
|
+
# Days in seconds
|
|
18
|
+
ONE_DAY = 86400
|
|
19
|
+
TWO_DAYS = 172800
|
|
20
|
+
THREE_DAYS = 259200
|
|
21
|
+
FOUR_DAYS = 345600
|
|
22
|
+
FIVE_DAYS = 432000
|
|
23
|
+
SIX_DAYS = 518400
|
|
24
|
+
SEVEN_DAYS = 604800
|
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
+
from kuhl_haus.mdp.models.constants import (
|
|
3
|
+
EIGHT_HOURS,
|
|
4
|
+
FIVE_MINUTES,
|
|
5
|
+
ONE_DAY,
|
|
6
|
+
ONE_HOUR,
|
|
7
|
+
THREE_DAYS,
|
|
8
|
+
TWELVE_HOURS,
|
|
9
|
+
)
|
|
2
10
|
|
|
3
11
|
|
|
4
12
|
class MarketDataCacheTTL(Enum):
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
# Raw market data caches
|
|
14
|
+
AGGREGATE = FIVE_MINUTES
|
|
15
|
+
HALTS = ONE_DAY
|
|
16
|
+
QUOTES = ONE_HOUR
|
|
17
|
+
TRADES = ONE_HOUR
|
|
18
|
+
UNKNOWN = ONE_DAY
|
|
19
|
+
|
|
20
|
+
# Ticker caches
|
|
21
|
+
TICKER_AVG_VOLUME = TWELVE_HOURS
|
|
22
|
+
TICKER_FREE_FLOAT = TWELVE_HOURS
|
|
23
|
+
TICKER_SNAPSHOTS = EIGHT_HOURS
|
|
24
|
+
|
|
25
|
+
# Scanner caches
|
|
26
|
+
TOP_STOCKS_SCANNER = EIGHT_HOURS
|
|
27
|
+
TOP_VOLUME_SCANNER = THREE_DAYS
|
|
28
|
+
TOP_GAINERS_SCANNER = THREE_DAYS
|
|
29
|
+
TOP_GAPPERS_SCANNER = THREE_DAYS
|
|
12
30
|
|
|
13
|
-
# Days
|
|
14
|
-
ONE_DAY = 86400
|
|
15
|
-
TWO_DAYS = 172800
|
|
16
|
-
THREE_DAYS = 259200
|
|
17
|
-
FOUR_DAYS = 345600
|
|
18
|
-
FIVE_DAYS = 432000
|
|
19
|
-
SIX_DAYS = 518400
|
|
20
|
-
SEVEN_DAYS = 604800
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kuhl-haus-mdp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10
|
|
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
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
kuhl_haus/mdp/__init__.py,sha256=5dEpAdB3kypH8tCRECoXwbly1WV9kFU5kh8ldGSa0VI,349
|
|
2
2
|
kuhl_haus/mdp/analyzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
kuhl_haus/mdp/analyzers/analyzer.py,sha256=rIU1lcHwP2IBai0QLt0y-4ySg_ibWsutNU8JUgSxa1U,471
|
|
4
|
-
kuhl_haus/mdp/analyzers/massive_data_analyzer.py,sha256=
|
|
5
|
-
kuhl_haus/mdp/analyzers/top_stocks.py,sha256=
|
|
4
|
+
kuhl_haus/mdp/analyzers/massive_data_analyzer.py,sha256=i3XFnornuGGhe6nbk6QqIupND0MYTxwKPjvyFw_UBDU,3863
|
|
5
|
+
kuhl_haus/mdp/analyzers/top_stocks.py,sha256=ZZy7legxmLYad-p_a7N6hqj2mN7bn-ZdPEt9xgk4mrQ,10111
|
|
6
6
|
kuhl_haus/mdp/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
kuhl_haus/mdp/components/market_data_cache.py,sha256=
|
|
7
|
+
kuhl_haus/mdp/components/market_data_cache.py,sha256=jhBdR8rJEvby7eXTTvXn_PceMSf_gS71oEZV1bK7HdM,7337
|
|
8
8
|
kuhl_haus/mdp/components/market_data_scanner.py,sha256=45MgprFlq03MvmIRYXENsrc7UlTcBE_hIsPyOvNs1zc,9745
|
|
9
9
|
kuhl_haus/mdp/components/widget_data_service.py,sha256=ikygD9NRpidcXBEqft5Q11rHy_eUOwKGyOLEezo-Dd4,7439
|
|
10
10
|
kuhl_haus/mdp/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -17,15 +17,16 @@ kuhl_haus/mdp/integ/massive_data_processor.py,sha256=H1WlbGtuSF45n7qLTLleuNlG-Ol
|
|
|
17
17
|
kuhl_haus/mdp/integ/massive_data_queues.py,sha256=zC_uV2vwZCMyVerDQ18RAQwIMMF75iK4qUSqwuWqgwc,5050
|
|
18
18
|
kuhl_haus/mdp/integ/web_socket_message_serde.py,sha256=XdaoaByc7IhtzbPDXBtXKOTjyDzfPSDuZVCoHSIaTl4,5468
|
|
19
19
|
kuhl_haus/mdp/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
kuhl_haus/mdp/models/constants.py,sha256=t55gVHl3IVDSA5Y-mu7zdA8FXAuw4TbOEkI2r2U__1Y,420
|
|
20
21
|
kuhl_haus/mdp/models/market_data_analyzer_result.py,sha256=iICb5GVCtuqARNbR1JNCAfbxMijM3uppDNdL8_FB3eI,422
|
|
21
22
|
kuhl_haus/mdp/models/market_data_cache_keys.py,sha256=_CSPtwvZo5W6cuj9vb7lRTu6MMuRGASe_VigAaOQXwE,1067
|
|
22
|
-
kuhl_haus/mdp/models/market_data_cache_ttl.py,sha256=
|
|
23
|
+
kuhl_haus/mdp/models/market_data_cache_ttl.py,sha256=v7tnc7-qaxSIWvpInlxqDDTByHLY2uilCPhnP7UaNR0,643
|
|
23
24
|
kuhl_haus/mdp/models/market_data_pubsub_keys.py,sha256=PEIPXK9jBehJB7G4pqoSuQZcfMZgOQq8Yho1itqv-1A,1306
|
|
24
25
|
kuhl_haus/mdp/models/market_data_scanner_names.py,sha256=BYn1C0rYgGF1Sq583BkHADKUu-28ytNZQ-XgptuCH-Y,260
|
|
25
26
|
kuhl_haus/mdp/models/massive_data_queue.py,sha256=MfYBcjVc4Fi61DWIvvhhWLUOiLmRpE9egtW-2KH6FTE,188
|
|
26
27
|
kuhl_haus/mdp/models/top_stocks_cache_item.py,sha256=Zn9LociBatP2a1OUfjXDFlWM7tCudO-CR4oj6jYfEEM,7916
|
|
27
|
-
kuhl_haus_mdp-0.1.
|
|
28
|
-
kuhl_haus_mdp-0.1.
|
|
29
|
-
kuhl_haus_mdp-0.1.
|
|
30
|
-
kuhl_haus_mdp-0.1.
|
|
31
|
-
kuhl_haus_mdp-0.1.
|
|
28
|
+
kuhl_haus_mdp-0.1.10.dist-info/METADATA,sha256=Sku0puFIexyTNpzIO8EoUfV8GXQrfw-T6gAREUrqKYs,8888
|
|
29
|
+
kuhl_haus_mdp-0.1.10.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
|
|
30
|
+
kuhl_haus_mdp-0.1.10.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
|
31
|
+
kuhl_haus_mdp-0.1.10.dist-info/licenses/LICENSE.txt,sha256=DRkJftAJcMqoTkQ_Y6-HtKj3nm4pZah_p8XBZiYnw-c,1079
|
|
32
|
+
kuhl_haus_mdp-0.1.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|