kuhl-haus-mdp 0.1.9__py3-none-any.whl → 0.1.11__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.
@@ -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.THREE_DAYS.value,
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.THREE_DAYS.value,
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.EIGHT_HOURS.value,
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.THREE_DAYS.value,
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.ONE_DAY.value,
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, timedelta
4
- from typing import Optional, List, Iterator
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=28500, # 7 hours, 55 minutes
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=259200, # 3 days
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=259200, # 3 days
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=259200, # 3 days
109
+ cache_ttl=MarketDataCacheTTL.TOP_GAPPERS_SCANNER.value,
115
110
  publish_key=MarketDataPubSubKeys.TOP_GAPPERS_SCANNER.value,
116
111
  )
117
112
  ]
@@ -141,7 +136,11 @@ 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 KeyError:
140
+ retry_count += 1
141
+ await self.cache.delete_ticker_snapshot(event.symbol)
142
+ except Exception as e:
143
+ self.logger.error(f"Failed to get snapshot for {event.symbol}: {e}")
145
144
  retry_count += 1
146
145
  if retry_count == max_tries and prev_day_close == 0:
147
146
  self.logger.error(f"Failed to get snapshot for {event.symbol} after {max_tries} tries.")
@@ -155,7 +154,8 @@ class TopStocksAnalyzer(Analyzer):
155
154
  try:
156
155
  avg_volume = await self.cache.get_avg_volume(event.symbol)
157
156
  break
158
- except Exception:
157
+ except Exception as e:
158
+ self.logger.error(f"Failed to get average volume for {event.symbol}: {e}")
159
159
  retry_count += 1
160
160
  if retry_count == max_tries and avg_volume == 0:
161
161
  self.logger.error(f"Failed to get average volume for {event.symbol} after {max_tries} tries.")
@@ -169,7 +169,8 @@ class TopStocksAnalyzer(Analyzer):
169
169
  try:
170
170
  free_float = await self.cache.get_free_float(event.symbol)
171
171
  break
172
- except Exception:
172
+ except Exception as e:
173
+ self.logger.error(f"Failed to get free float for {event.symbol}: {e}")
173
174
  retry_count += 1
174
175
  if retry_count == max_tries and free_float == 0:
175
176
  self.logger.error(f"Failed to get free float for {event.symbol} after {max_tries} tries.")
@@ -26,6 +26,18 @@ class MarketDataCache:
26
26
  self.redis_client = redis_client
27
27
  self.http_session = None
28
28
 
29
+ async def delete_cache(self, cache_key: str):
30
+ """
31
+ Delete cache entry.
32
+
33
+ :arg cache_key: Cache key to delete
34
+ """
35
+ try:
36
+ await self.redis_client.delete(cache_key)
37
+ self.logger.info(f"Deleted cache entry: {cache_key}")
38
+ except Exception as e:
39
+ self.logger.error(f"Error deleting cache entry: {e}")
40
+
29
41
  async def get_cache(self, cache_key: str) -> Optional[dict]:
30
42
  """Fetch current value from Redis cache (for snapshot requests)."""
31
43
  value = await self.redis_client.get(cache_key)
@@ -44,6 +56,16 @@ class MarketDataCache:
44
56
  await self.redis_client.publish(publish_key, json.dumps(data))
45
57
  self.logger.info(f"Published data for {publish_key}")
46
58
 
59
+ async def delete_ticker_snapshot(self, ticker: str):
60
+ """
61
+ Delete ticker snapshot from cache.
62
+
63
+ :param ticker: symbol of ticker to delete snapshot for
64
+ :return: None
65
+ """
66
+ cache_key = f"{MarketDataCacheKeys.TICKER_SNAPSHOTS.value}:{ticker}"
67
+ await self.delete_cache(cache_key=cache_key)
68
+
47
69
  async def get_ticker_snapshot(self, ticker: str) -> TickerSnapshot:
48
70
  self.logger.info(f"Getting snapshot for {ticker}")
49
71
  cache_key = f"{MarketDataCacheKeys.TICKER_SNAPSHOTS.value}:{ticker}"
@@ -61,7 +83,7 @@ class MarketDataCache:
61
83
  await self.cache_data(
62
84
  data=data,
63
85
  cache_key=cache_key,
64
- cache_ttl=MarketDataCacheTTL.EIGHT_HOURS.value
86
+ cache_ttl=MarketDataCacheTTL.TICKER_SNAPSHOTS.value
65
87
  )
66
88
  return snapshot
67
89
 
@@ -109,13 +131,15 @@ class MarketDataCache:
109
131
  periods_calculated += 1
110
132
  else:
111
133
  break
134
+ if periods_calculated == 0:
135
+ raise Exception(f"No volume data returned for {ticker}")
112
136
  avg_volume = total_volume / periods_calculated
113
137
 
114
138
  self.logger.info(f"average volume {ticker}: {avg_volume}")
115
139
  await self.cache_data(
116
140
  data=avg_volume,
117
141
  cache_key=cache_key,
118
- cache_ttl=MarketDataCacheTTL.TWELVE_HOURS.value
142
+ cache_ttl=MarketDataCacheTTL.TICKER_AVG_VOLUME.value
119
143
  )
120
144
  return avg_volume
121
145
 
@@ -162,7 +186,7 @@ class MarketDataCache:
162
186
  await self.cache_data(
163
187
  data=free_float,
164
188
  cache_key=cache_key,
165
- cache_ttl=MarketDataCacheTTL.TWELVE_HOURS.value
189
+ cache_ttl=MarketDataCacheTTL.TICKER_FREE_FLOAT.value
166
190
  )
167
191
  return free_float
168
192
 
@@ -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,29 @@
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
- # Hours
6
- ONE_HOUR = 3600
7
- TWO_HOURS = 7200
8
- FOUR_HOURS = 14400
9
- SIX_HOURS = 21600
10
- EIGHT_HOURS = 28800
11
- TWELVE_HOURS = 43200
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
12
19
 
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
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kuhl-haus-mdp
3
- Version: 0.1.9
3
+ Version: 0.1.11
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)
@@ -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=WSb7T8X4u2ue7Du7sf_fqxjgjEbR6ThllSNT1CncIM0,3866
5
- kuhl_haus/mdp/analyzers/top_stocks.py,sha256=s4xI6_S7K3CxUm3jtMi4BPD17v2T1URTMgKIRK85V_s,9814
4
+ kuhl_haus/mdp/analyzers/massive_data_analyzer.py,sha256=i3XFnornuGGhe6nbk6QqIupND0MYTxwKPjvyFw_UBDU,3863
5
+ kuhl_haus/mdp/analyzers/top_stocks.py,sha256=uKH7uRJNaPRcpBvFLFM_wJVqo8QyHZ8Obj8zjU3dx9E,10255
6
6
  kuhl_haus/mdp/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- kuhl_haus/mdp/components/market_data_cache.py,sha256=LT_yddNwB-l6skJgvDWHmUO6GjdBM0EmlMCGHX5kwKY,7209
7
+ kuhl_haus/mdp/components/market_data_cache.py,sha256=Jp5bTycVlY1y5cY9wolqlhwFFqUgBIHnTJh8zPyqxCY,8064
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=a43ys3S61Y0ADdb03ThgrRd9x7B1EsI6FplCjecdNLY,373
23
+ kuhl_haus/mdp/models/market_data_cache_ttl.py,sha256=mfNNrt0mkdppQtjb6gvyd2toSPCcmK_s-EcY5nQ0xLE,642
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.9.dist-info/METADATA,sha256=iwhFbwovPdoPkMO2vMr-9KroOXZ5vNGhz4YMPcjYIpk,8887
28
- kuhl_haus_mdp-0.1.9.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
29
- kuhl_haus_mdp-0.1.9.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
30
- kuhl_haus_mdp-0.1.9.dist-info/licenses/LICENSE.txt,sha256=DRkJftAJcMqoTkQ_Y6-HtKj3nm4pZah_p8XBZiYnw-c,1079
31
- kuhl_haus_mdp-0.1.9.dist-info/RECORD,,
28
+ kuhl_haus_mdp-0.1.11.dist-info/METADATA,sha256=U5ev3SWbfM1L1bIChjE8LYmjz_xqyHMg-9lpRQcponQ,8888
29
+ kuhl_haus_mdp-0.1.11.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
30
+ kuhl_haus_mdp-0.1.11.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
31
+ kuhl_haus_mdp-0.1.11.dist-info/licenses/LICENSE.txt,sha256=DRkJftAJcMqoTkQ_Y6-HtKj3nm4pZah_p8XBZiYnw-c,1079
32
+ kuhl_haus_mdp-0.1.11.dist-info/RECORD,,