kuhl-haus-mdp 0.1.5__py3-none-any.whl → 0.1.7__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/analyzer.py +5 -4
- kuhl_haus/mdp/analyzers/top_stocks.py +8 -9
- kuhl_haus/mdp/components/market_data_cache.py +119 -13
- kuhl_haus/mdp/components/market_data_scanner.py +21 -5
- kuhl_haus/mdp/helpers/utils.py +99 -0
- {kuhl_haus_mdp-0.1.5.dist-info → kuhl_haus_mdp-0.1.7.dist-info}/METADATA +2 -2
- {kuhl_haus_mdp-0.1.5.dist-info → kuhl_haus_mdp-0.1.7.dist-info}/RECORD +10 -10
- {kuhl_haus_mdp-0.1.5.dist-info → kuhl_haus_mdp-0.1.7.dist-info}/WHEEL +0 -0
- {kuhl_haus_mdp-0.1.5.dist-info → kuhl_haus_mdp-0.1.7.dist-info}/entry_points.txt +0 -0
- {kuhl_haus_mdp-0.1.5.dist-info → kuhl_haus_mdp-0.1.7.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
from typing import Optional, List
|
|
2
2
|
from kuhl_haus.mdp.models.market_data_analyzer_result import MarketDataAnalyzerResult
|
|
3
|
+
from kuhl_haus.mdp.components.market_data_cache import MarketDataCache
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class Analyzer:
|
|
6
|
-
|
|
7
|
+
cache: MarketDataCache
|
|
7
8
|
|
|
8
|
-
def __init__(self,
|
|
9
|
-
self.
|
|
9
|
+
def __init__(self, cache: MarketDataCache, **kwargs):
|
|
10
|
+
self.cache = cache
|
|
10
11
|
|
|
11
|
-
async def rehydrate(self
|
|
12
|
+
async def rehydrate(self):
|
|
12
13
|
pass
|
|
13
14
|
|
|
14
15
|
async def analyze_data(self, data: dict) -> Optional[List[MarketDataAnalyzerResult]]:
|
|
@@ -26,21 +26,15 @@ from kuhl_haus.mdp.models.top_stocks_cache_item import TopStocksCacheItem
|
|
|
26
26
|
class TopStocksAnalyzer(Analyzer):
|
|
27
27
|
|
|
28
28
|
def __init__(self, cache: MarketDataCache, **kwargs):
|
|
29
|
-
|
|
30
|
-
kwargs["cache_key"] = MarketDataCacheKeys.TOP_STOCKS_SCANNER.value
|
|
31
|
-
super().__init__(**kwargs)
|
|
29
|
+
super().__init__(cache=cache, **kwargs)
|
|
32
30
|
self.cache = cache
|
|
31
|
+
self.cache_key = MarketDataCacheKeys.TOP_STOCKS_SCANNER.value
|
|
33
32
|
self.logger = logging.getLogger(__name__)
|
|
34
33
|
self.cache_item = TopStocksCacheItem()
|
|
35
34
|
self.last_update_time = 0
|
|
36
35
|
self.pre_market_reset = False
|
|
37
36
|
|
|
38
|
-
async def rehydrate(self
|
|
39
|
-
if not data:
|
|
40
|
-
self.cache_item = TopStocksCacheItem()
|
|
41
|
-
self.logger.info("No data to rehydrate TopStocksCacheItem.")
|
|
42
|
-
return
|
|
43
|
-
|
|
37
|
+
async def rehydrate(self):
|
|
44
38
|
# Get current time in UTC, then convert to Eastern Time
|
|
45
39
|
utc_now = datetime.now(timezone.utc)
|
|
46
40
|
et_now = utc_now.astimezone(ZoneInfo("America/New_York"))
|
|
@@ -52,6 +46,11 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
52
46
|
self.cache_item = TopStocksCacheItem()
|
|
53
47
|
self.logger.info(f"Outside market hours ({et_now.strftime('%H:%M:%S %Z')}), clearing cache.")
|
|
54
48
|
return
|
|
49
|
+
data = await self.cache.get_cache(self.cache_key)
|
|
50
|
+
if not data:
|
|
51
|
+
self.cache_item = TopStocksCacheItem()
|
|
52
|
+
self.logger.info("No data to rehydrate TopStocksCacheItem.")
|
|
53
|
+
return
|
|
55
54
|
self.cache_item = TopStocksCacheItem(**data)
|
|
56
55
|
self.logger.info("Rehydrated TopStocksCacheItem")
|
|
57
56
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
from typing import Any, Optional, Iterator, List
|
|
4
|
+
from datetime import datetime, timezone, timedelta
|
|
5
|
+
from zoneinfo import ZoneInfo
|
|
4
6
|
|
|
5
7
|
import aiohttp
|
|
6
8
|
import redis.asyncio as aioredis
|
|
@@ -8,8 +10,10 @@ from massive.rest import RESTClient
|
|
|
8
10
|
from massive.rest.models import (
|
|
9
11
|
TickerSnapshot,
|
|
10
12
|
FinancialRatio,
|
|
13
|
+
Agg,
|
|
11
14
|
)
|
|
12
15
|
|
|
16
|
+
from kuhl_haus.mdp.helpers.utils import ticker_snapshot_to_dict
|
|
13
17
|
from kuhl_haus.mdp.models.market_data_cache_keys import MarketDataCacheKeys
|
|
14
18
|
from kuhl_haus.mdp.models.market_data_cache_ttl import MarketDataCacheTTL
|
|
15
19
|
|
|
@@ -34,49 +38,151 @@ class MarketDataCache:
|
|
|
34
38
|
await self.redis_client.setex(cache_key, cache_ttl, json.dumps(data))
|
|
35
39
|
else:
|
|
36
40
|
await self.redis_client.set(cache_key, json.dumps(data))
|
|
37
|
-
self.logger.
|
|
41
|
+
self.logger.info(f"Cached data for {cache_key}")
|
|
38
42
|
|
|
39
43
|
async def publish_data(self, data: Any, publish_key: str = None):
|
|
40
44
|
await self.redis_client.publish(publish_key, json.dumps(data))
|
|
41
|
-
self.logger.
|
|
45
|
+
self.logger.info(f"Published data for {publish_key}")
|
|
42
46
|
|
|
43
47
|
async def get_ticker_snapshot(self, ticker: str) -> TickerSnapshot:
|
|
44
|
-
self.logger.
|
|
48
|
+
self.logger.info(f"Getting snapshot for {ticker}")
|
|
45
49
|
cache_key = f"{MarketDataCacheKeys.TICKER_SNAPSHOTS.value}:{ticker}"
|
|
46
50
|
result = await self.get_cache(cache_key=cache_key)
|
|
47
51
|
if result:
|
|
48
|
-
snapshot
|
|
52
|
+
self.logger.info(f"Returning cached snapshot for {ticker}")
|
|
53
|
+
snapshot = TickerSnapshot(**result)
|
|
49
54
|
else:
|
|
50
55
|
snapshot: TickerSnapshot = self.rest_client.get_snapshot_ticker(
|
|
51
56
|
market_type="stocks",
|
|
52
57
|
ticker=ticker
|
|
53
58
|
)
|
|
54
|
-
self.logger.
|
|
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
|
+
data = ticker_snapshot_to_dict(snapshot)
|
|
55
132
|
await self.cache_data(
|
|
56
|
-
data=
|
|
133
|
+
data=data,
|
|
57
134
|
cache_key=cache_key,
|
|
58
135
|
cache_ttl=MarketDataCacheTTL.EIGHT_HOURS.value
|
|
59
136
|
)
|
|
60
137
|
return snapshot
|
|
61
138
|
|
|
62
139
|
async def get_avg_volume(self, ticker: str):
|
|
63
|
-
self.logger.
|
|
140
|
+
self.logger.info(f"Getting average volume for {ticker}")
|
|
64
141
|
cache_key = f"{MarketDataCacheKeys.TICKER_AVG_VOLUME.value}:{ticker}"
|
|
65
142
|
avg_volume = await self.get_cache(cache_key=cache_key)
|
|
66
143
|
if avg_volume:
|
|
67
|
-
self.logger.
|
|
144
|
+
self.logger.info(f"Returning cached value for {ticker}: {avg_volume}")
|
|
68
145
|
return avg_volume
|
|
69
146
|
|
|
147
|
+
# Experimental version - unreliable
|
|
70
148
|
results: Iterator[FinancialRatio] = self.rest_client.list_financials_ratios(ticker=ticker)
|
|
71
149
|
ratios: List[FinancialRatio] = []
|
|
72
150
|
for financial_ratio in results:
|
|
73
151
|
ratios.append(financial_ratio)
|
|
152
|
+
|
|
153
|
+
# If there is only one financial ratio, use it's average volume.
|
|
154
|
+
# Otherwise, calculate average volume from 30 trading sessions.'
|
|
74
155
|
if len(ratios) == 1:
|
|
75
156
|
avg_volume = ratios[0].average_volume
|
|
76
157
|
else:
|
|
77
|
-
|
|
158
|
+
# Get date string in YYYY-MM-DD format
|
|
159
|
+
end_date = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
160
|
+
# Get date from 30 trading sessions ago in YYYY-MM-DD format
|
|
161
|
+
start_date = (datetime.now(timezone.utc) - timedelta(days=42)).strftime("%Y-%m-%d")
|
|
162
|
+
|
|
163
|
+
result: Iterator[Agg] = self.rest_client.list_aggs(
|
|
164
|
+
ticker=ticker,
|
|
165
|
+
multiplier=1,
|
|
166
|
+
timespan="day",
|
|
167
|
+
from_=start_date,
|
|
168
|
+
to=end_date,
|
|
169
|
+
adjusted=True,
|
|
170
|
+
sort="desc"
|
|
171
|
+
)
|
|
172
|
+
self.logger.info(f"average volume result: {result}")
|
|
173
|
+
|
|
174
|
+
total_volume = 0
|
|
175
|
+
max_periods = 30
|
|
176
|
+
periods_calculated = 0
|
|
177
|
+
for agg in result:
|
|
178
|
+
if periods_calculated < max_periods:
|
|
179
|
+
total_volume += agg.volume
|
|
180
|
+
periods_calculated += 1
|
|
181
|
+
else:
|
|
182
|
+
break
|
|
183
|
+
avg_volume = total_volume / periods_calculated
|
|
78
184
|
|
|
79
|
-
self.logger.
|
|
185
|
+
self.logger.info(f"average volume {ticker}: {avg_volume}")
|
|
80
186
|
await self.cache_data(
|
|
81
187
|
data=avg_volume,
|
|
82
188
|
cache_key=cache_key,
|
|
@@ -85,11 +191,11 @@ class MarketDataCache:
|
|
|
85
191
|
return avg_volume
|
|
86
192
|
|
|
87
193
|
async def get_free_float(self, ticker: str):
|
|
88
|
-
self.logger.
|
|
194
|
+
self.logger.info(f"Getting free float for {ticker}")
|
|
89
195
|
cache_key = f"{MarketDataCacheKeys.TICKER_FREE_FLOAT.value}:{ticker}"
|
|
90
196
|
free_float = await self.get_cache(cache_key=cache_key)
|
|
91
197
|
if free_float:
|
|
92
|
-
self.logger.
|
|
198
|
+
self.logger.info(f"Returning cached value for {ticker}: {free_float}")
|
|
93
199
|
return free_float
|
|
94
200
|
|
|
95
201
|
# NOTE: This endpoint is experimental and the interface may change.
|
|
@@ -123,7 +229,7 @@ class MarketDataCache:
|
|
|
123
229
|
self.logger.error(f"Error fetching free float for {ticker}: {e}")
|
|
124
230
|
raise
|
|
125
231
|
|
|
126
|
-
self.logger.
|
|
232
|
+
self.logger.info(f"free float {ticker}: {free_float}")
|
|
127
233
|
await self.cache_data(
|
|
128
234
|
data=free_float,
|
|
129
235
|
cache_key=cache_key,
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Union, Optional, List
|
|
4
|
+
from typing import Any, Union, Optional, List
|
|
5
5
|
|
|
6
6
|
import redis.asyncio as aioredis
|
|
7
7
|
from redis.exceptions import ConnectionError
|
|
8
8
|
|
|
9
|
+
from massive.rest import RESTClient
|
|
10
|
+
|
|
9
11
|
from kuhl_haus.mdp.analyzers.analyzer import Analyzer
|
|
10
12
|
from kuhl_haus.mdp.models.market_data_analyzer_result import MarketDataAnalyzerResult
|
|
13
|
+
from kuhl_haus.mdp.components.market_data_cache import MarketDataCache
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
class MarketDataScanner:
|
|
@@ -18,11 +21,14 @@ class MarketDataScanner:
|
|
|
18
21
|
error: int
|
|
19
22
|
restarts: int
|
|
20
23
|
|
|
21
|
-
def __init__(self, redis_url: str,
|
|
24
|
+
def __init__(self, redis_url: str, massive_api_key: str, subscriptions: List[str], analyzer_class: Any):
|
|
22
25
|
self.redis_url = redis_url
|
|
23
|
-
self.
|
|
26
|
+
self.massive_api_key = massive_api_key
|
|
24
27
|
self.logger = logging.getLogger(__name__)
|
|
25
28
|
|
|
29
|
+
self.analyzer: Analyzer = None
|
|
30
|
+
self.analyzer_class = analyzer_class
|
|
31
|
+
|
|
26
32
|
# Connection objects
|
|
27
33
|
self.redis_client = None # : aioredis.Redis = None
|
|
28
34
|
self.pubsub_client: Optional[aioredis.client.PubSub] = None
|
|
@@ -30,6 +36,7 @@ class MarketDataScanner:
|
|
|
30
36
|
# State
|
|
31
37
|
self.mdc_connected = False
|
|
32
38
|
self.running = False
|
|
39
|
+
self.mdc: Optional[MarketDataCache] = None
|
|
33
40
|
|
|
34
41
|
self.subscriptions: List[str] = subscriptions
|
|
35
42
|
self._pubsub_task: Union[asyncio.Task, None] = None
|
|
@@ -48,9 +55,9 @@ class MarketDataScanner:
|
|
|
48
55
|
await self.connect()
|
|
49
56
|
self.pubsub_client = self.redis_client.pubsub()
|
|
50
57
|
|
|
51
|
-
|
|
58
|
+
self.analyzer = self.analyzer_class(cache=self.mdc)
|
|
52
59
|
self.logger.info(f"mds rehydrating from cache")
|
|
53
|
-
await self.analyzer.rehydrate(
|
|
60
|
+
await self.analyzer.rehydrate()
|
|
54
61
|
self.logger.info("mds rehydration complete")
|
|
55
62
|
|
|
56
63
|
for subscription in self.subscriptions:
|
|
@@ -73,6 +80,10 @@ class MarketDataScanner:
|
|
|
73
80
|
pass
|
|
74
81
|
self._pubsub_task = None
|
|
75
82
|
|
|
83
|
+
if self.mdc:
|
|
84
|
+
await self.mdc.close()
|
|
85
|
+
self.mdc = None
|
|
86
|
+
|
|
76
87
|
if self.pubsub_client:
|
|
77
88
|
for subscription in self.subscriptions:
|
|
78
89
|
if subscription.endswith("*"):
|
|
@@ -104,6 +115,11 @@ class MarketDataScanner:
|
|
|
104
115
|
|
|
105
116
|
# Test Redis connection
|
|
106
117
|
await self.redis_client.ping()
|
|
118
|
+
self.mdc = MarketDataCache(
|
|
119
|
+
rest_client=RESTClient(api_key=self.massive_api_key),
|
|
120
|
+
redis_client=self.redis_client,
|
|
121
|
+
massive_api_key=self.massive_api_key
|
|
122
|
+
)
|
|
107
123
|
self.mdc_connected = True
|
|
108
124
|
self.logger.debug(f"Connected to Redis: {self.redis_url}")
|
|
109
125
|
except Exception as e:
|
kuhl_haus/mdp/helpers/utils.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
|
+
from massive.rest.models import TickerSnapshot
|
|
4
5
|
|
|
5
6
|
logging.basicConfig(
|
|
6
7
|
level=logging.INFO,
|
|
@@ -35,3 +36,101 @@ def get_massive_api_key():
|
|
|
35
36
|
raise ValueError("MASSIVE_API_KEY environment variable not set")
|
|
36
37
|
logger.info("Done.")
|
|
37
38
|
return api_key
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def ticker_snapshot_to_dict(snapshot: TickerSnapshot) -> dict:
|
|
42
|
+
"""
|
|
43
|
+
Convert a TickerSnapshot instance into a JSON-serializable dictionary.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
snapshot: TickerSnapshot instance to convert
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Dictionary with keys matching the from_dict format (camelCase)
|
|
50
|
+
"""
|
|
51
|
+
data = {
|
|
52
|
+
"ticker": snapshot.ticker,
|
|
53
|
+
"todays_change": snapshot.todays_change,
|
|
54
|
+
"todays_change_perc": snapshot.todays_change_percent,
|
|
55
|
+
"updated": snapshot.updated,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if snapshot.day is not None:
|
|
59
|
+
data["day"] = {
|
|
60
|
+
"open": snapshot.day.open,
|
|
61
|
+
"high": snapshot.day.high,
|
|
62
|
+
"low": snapshot.day.low,
|
|
63
|
+
"close": snapshot.day.close,
|
|
64
|
+
"volume": snapshot.day.volume,
|
|
65
|
+
"vwap": snapshot.day.vwap,
|
|
66
|
+
"timestamp": snapshot.day.timestamp,
|
|
67
|
+
"transactions": snapshot.day.transactions,
|
|
68
|
+
"otc": snapshot.day.otc,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if snapshot.last_quote is not None:
|
|
72
|
+
data["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
|
+
|
|
89
|
+
if snapshot.last_trade is not None:
|
|
90
|
+
data["last_trade"] = {
|
|
91
|
+
"ticker": snapshot.last_trade.ticker,
|
|
92
|
+
"trf_timestamp": snapshot.last_trade.trf_timestamp,
|
|
93
|
+
"sequence_number": snapshot.last_trade.sequence_number,
|
|
94
|
+
"sip_timestamp": snapshot.last_trade.sip_timestamp,
|
|
95
|
+
"participant_timestamp": snapshot.last_trade.participant_timestamp,
|
|
96
|
+
"conditions": snapshot.last_trade.conditions,
|
|
97
|
+
"correction": snapshot.last_trade.correction,
|
|
98
|
+
"id": snapshot.last_trade.id,
|
|
99
|
+
"price": snapshot.last_trade.price,
|
|
100
|
+
"trf_id": snapshot.last_trade.trf_id,
|
|
101
|
+
"size": snapshot.last_trade.size,
|
|
102
|
+
"exchange": snapshot.last_trade.exchange,
|
|
103
|
+
"tape": snapshot.last_trade.tape,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if snapshot.min is not None:
|
|
107
|
+
data["min"] = {
|
|
108
|
+
"accumulated_volume": snapshot.min.accumulated_volume,
|
|
109
|
+
"open": snapshot.min.open,
|
|
110
|
+
"high": snapshot.min.high,
|
|
111
|
+
"low": snapshot.min.low,
|
|
112
|
+
"close": snapshot.min.close,
|
|
113
|
+
"volume": snapshot.min.volume,
|
|
114
|
+
"vwap": snapshot.min.vwap,
|
|
115
|
+
"otc": snapshot.min.otc,
|
|
116
|
+
"timestamp": snapshot.min.timestamp,
|
|
117
|
+
"transactions": snapshot.min.transactions,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if snapshot.prev_day is not None:
|
|
121
|
+
data["prev_day"] = {
|
|
122
|
+
"open": snapshot.prev_day.open,
|
|
123
|
+
"high": snapshot.prev_day.high,
|
|
124
|
+
"low": snapshot.prev_day.low,
|
|
125
|
+
"close": snapshot.prev_day.close,
|
|
126
|
+
"volume": snapshot.prev_day.volume,
|
|
127
|
+
"vwap": snapshot.prev_day.vwap,
|
|
128
|
+
"timestamp": snapshot.prev_day.timestamp,
|
|
129
|
+
"transactions": snapshot.prev_day.transactions,
|
|
130
|
+
"otc": snapshot.prev_day.otc,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if snapshot.fair_market_value is not None:
|
|
134
|
+
data["fmv"] = snapshot.fair_market_value
|
|
135
|
+
|
|
136
|
+
return data
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kuhl-haus-mdp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
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)
|
|
@@ -68,7 +68,7 @@ Description-Content-Type: text/markdown
|
|
|
68
68
|
[](https://codecov.io/gh/kuhl-haus/kuhl-haus-mdp)
|
|
69
69
|
[](https://github.com/kuhl-haus/kuhl-haus-mdp/issues)
|
|
70
70
|
[](https://github.com/kuhl-haus/kuhl-haus-mdp/pulls)
|
|
71
|
-
|
|
71
|
+
[](https://kuhl-haus-mdp.readthedocs.io/en/latest/)
|
|
72
72
|
|
|
73
73
|
# kuhl-haus-mdp
|
|
74
74
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
kuhl_haus/mdp/__init__.py,sha256=5dEpAdB3kypH8tCRECoXwbly1WV9kFU5kh8ldGSa0VI,349
|
|
2
2
|
kuhl_haus/mdp/analyzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
kuhl_haus/mdp/analyzers/analyzer.py,sha256=
|
|
3
|
+
kuhl_haus/mdp/analyzers/analyzer.py,sha256=rIU1lcHwP2IBai0QLt0y-4ySg_ibWsutNU8JUgSxa1U,471
|
|
4
4
|
kuhl_haus/mdp/analyzers/massive_data_analyzer.py,sha256=WSb7T8X4u2ue7Du7sf_fqxjgjEbR6ThllSNT1CncIM0,3866
|
|
5
|
-
kuhl_haus/mdp/analyzers/top_stocks.py,sha256=
|
|
5
|
+
kuhl_haus/mdp/analyzers/top_stocks.py,sha256=GvNSa7yWZZ7WUgpaV2t1nVOxWA2R2qpISy16x2RGaQ8,9463
|
|
6
6
|
kuhl_haus/mdp/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
kuhl_haus/mdp/components/market_data_cache.py,sha256=
|
|
8
|
-
kuhl_haus/mdp/components/market_data_scanner.py,sha256=
|
|
7
|
+
kuhl_haus/mdp/components/market_data_cache.py,sha256=qc19TN05efgdpqobcF9rlNL8D8ZFDVYfKN-flTaZLks,11086
|
|
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
|
|
11
11
|
kuhl_haus/mdp/helpers/process_manager.py,sha256=Is3Jx8nlBWvywQ1acdsdaSJTAG0olKskpPvrRB4VMDE,9024
|
|
12
12
|
kuhl_haus/mdp/helpers/queue_name_resolver.py,sha256=l_zfRLxrjR9uwRCV2VDO4vPWLK_lj5KVG2p4Lh8xWiw,770
|
|
13
|
-
kuhl_haus/mdp/helpers/utils.py,sha256=
|
|
13
|
+
kuhl_haus/mdp/helpers/utils.py,sha256=i6ILSa884jp7ijBsP_wBT3yHsUa91xSQEFkDhzqe1aQ,5164
|
|
14
14
|
kuhl_haus/mdp/integ/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
kuhl_haus/mdp/integ/massive_data_listener.py,sha256=fPEYc6zZzHzFFjbP3zFInajKtEGInj8UQKKo3nKQEwQ,5098
|
|
16
16
|
kuhl_haus/mdp/integ/massive_data_processor.py,sha256=H1WlbGtuSF45n7qLTLleuNlG-OlIXz4llJ7q3XRSS-s,8605
|
|
@@ -24,8 +24,8 @@ kuhl_haus/mdp/models/market_data_pubsub_keys.py,sha256=PEIPXK9jBehJB7G4pqoSuQZcf
|
|
|
24
24
|
kuhl_haus/mdp/models/market_data_scanner_names.py,sha256=BYn1C0rYgGF1Sq583BkHADKUu-28ytNZQ-XgptuCH-Y,260
|
|
25
25
|
kuhl_haus/mdp/models/massive_data_queue.py,sha256=MfYBcjVc4Fi61DWIvvhhWLUOiLmRpE9egtW-2KH6FTE,188
|
|
26
26
|
kuhl_haus/mdp/models/top_stocks_cache_item.py,sha256=4vwwPTMkRRf1ct6iFInJnLSbBadM-tRk-zhqdD_ITE0,7676
|
|
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.
|
|
27
|
+
kuhl_haus_mdp-0.1.7.dist-info/METADATA,sha256=YbJ2dQ8MBJrdtgCtDtfuWYEtX7FZDCF-gilSTjxDTmw,8898
|
|
28
|
+
kuhl_haus_mdp-0.1.7.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
|
|
29
|
+
kuhl_haus_mdp-0.1.7.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
|
30
|
+
kuhl_haus_mdp-0.1.7.dist-info/licenses/LICENSE.txt,sha256=DRkJftAJcMqoTkQ_Y6-HtKj3nm4pZah_p8XBZiYnw-c,1079
|
|
31
|
+
kuhl_haus_mdp-0.1.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|