analyser_hj3415 4.1.4__py3-none-any.whl → 4.2.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.
- analyser_hj3415/analyser/eval/mil.py +6 -47
- analyser_hj3415/analyser/eval/red.py +9 -48
- analyser_hj3415/analyser/tsa/prophet.py +65 -77
- {analyser_hj3415-4.1.4.dist-info → analyser_hj3415-4.2.0.dist-info}/METADATA +2 -2
- {analyser_hj3415-4.1.4.dist-info → analyser_hj3415-4.2.0.dist-info}/RECORD +7 -7
- {analyser_hj3415-4.1.4.dist-info → analyser_hj3415-4.2.0.dist-info}/WHEEL +0 -0
- {analyser_hj3415-4.1.4.dist-info → analyser_hj3415-4.2.0.dist-info}/entry_points.txt +0 -0
@@ -7,8 +7,6 @@ from utils_hj3415 import tools, setup_logger
|
|
7
7
|
from db_hj3415 import myredis, mymongo
|
8
8
|
|
9
9
|
from analyser_hj3415.analyser.eval.common import Tools
|
10
|
-
import pickle
|
11
|
-
|
12
10
|
|
13
11
|
mylogger = setup_logger(__name__,'INFO')
|
14
12
|
expire_time = tools.to_int(os.getenv('DEFAULT_EXPIRE_TIME_H', 48)) * 3600
|
@@ -377,48 +375,9 @@ class Mil:
|
|
377
375
|
return myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_generate_data, refresh, timer=expire_time)
|
378
376
|
|
379
377
|
@classmethod
|
380
|
-
def
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
results_from_redis = pipe.execute() # [val1, val2, ...]
|
387
|
-
|
388
|
-
final_results = {}
|
389
|
-
missing_codes = []
|
390
|
-
|
391
|
-
# refresh=True 이면 기존 데이터 무시하고 다시 계산해야 하므로 모두 missing 처리
|
392
|
-
if refresh:
|
393
|
-
missing_codes = codes[:]
|
394
|
-
else:
|
395
|
-
# refresh=False 이면, Redis 값이 None인 티커만 다시 계산
|
396
|
-
for code, val in zip(codes, results_from_redis):
|
397
|
-
if val is None:
|
398
|
-
missing_codes.append(code)
|
399
|
-
else:
|
400
|
-
# Redis에 pickled 데이터가 있다면 언피클해서 담기
|
401
|
-
red_data = pickle.loads(val)
|
402
|
-
final_results[code] = red_data
|
403
|
-
|
404
|
-
# --- (2) 필요한 티커만 직접 연산 ---
|
405
|
-
newly_computed_data = {}
|
406
|
-
for code in missing_codes:
|
407
|
-
mylogger.debug(f"*** bulk_generate_data : {code}")
|
408
|
-
data = cls(code)._generate_data(refresh=True)
|
409
|
-
newly_computed_data[code] = data
|
410
|
-
|
411
|
-
# --- (3) 파이프라인 SET ---
|
412
|
-
if newly_computed_data:
|
413
|
-
pipe = myredis.Base.redis_client.pipeline()
|
414
|
-
for code, data in newly_computed_data.items():
|
415
|
-
redis_key = f"{code}_{cls.REDIS_MIL_DATA_SUFFIX}"
|
416
|
-
# ProphetLatestData 객체를 pickle로 직렬화
|
417
|
-
pickled_data = pickle.dumps(data)
|
418
|
-
# SET + expire_time
|
419
|
-
pipe.setex(redis_key, expire_time, pickled_data)
|
420
|
-
pipe.execute()
|
421
|
-
|
422
|
-
# 최종 결과 딕셔너리 (캐시에 있었던 것 + 새로 만든 것)
|
423
|
-
final_results.update(newly_computed_data)
|
424
|
-
return final_results
|
378
|
+
def bulk_get_data(cls, codes: List[str], refresh: bool) -> Dict[str, MilData]:
|
379
|
+
return myredis.Corps.bulk_get_or_compute(
|
380
|
+
[f"{code}_{cls.REDIS_MIL_DATA_SUFFIX}" for code in codes],
|
381
|
+
lambda key: cls(key[:6])._generate_data(refresh=True),
|
382
|
+
refresh=refresh
|
383
|
+
)
|
@@ -3,7 +3,6 @@ from collections import OrderedDict
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from typing import Tuple, Dict, List
|
5
5
|
import math
|
6
|
-
import pickle
|
7
6
|
|
8
7
|
from utils_hj3415 import tools, setup_logger
|
9
8
|
from db_hj3415 import myredis
|
@@ -162,7 +161,7 @@ class Red:
|
|
162
161
|
# 날짜 데이터가 없는경우
|
163
162
|
date = ''
|
164
163
|
계산된비유동부채value = round(tools.nan_to_zero(v1) + tools.nan_to_zero(v2) + tools.nan_to_zero(v3) + tools.nan_to_zero(v4),1)
|
165
|
-
mylogger.
|
164
|
+
mylogger.debug(f"{self} - 계산된 비유동부채 : {계산된비유동부채value}")
|
166
165
|
return date, 계산된비유동부채value
|
167
166
|
else:
|
168
167
|
return d, 비유동부채
|
@@ -278,57 +277,19 @@ class Red:
|
|
278
277
|
return myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_generate_data, refresh, timer=expire_time)
|
279
278
|
|
280
279
|
@classmethod
|
281
|
-
def
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
final_results = {}
|
290
|
-
missing_codes = []
|
291
|
-
|
292
|
-
# refresh=True 이면 기존 데이터 무시하고 다시 계산해야 하므로 모두 missing 처리
|
293
|
-
if refresh:
|
294
|
-
missing_codes = codes[:]
|
295
|
-
else:
|
296
|
-
# refresh=False 이면, Redis 값이 None인 티커만 다시 계산
|
297
|
-
for code, val in zip(codes, results_from_redis):
|
298
|
-
if val is None:
|
299
|
-
missing_codes.append(code)
|
300
|
-
else:
|
301
|
-
# Redis에 pickled 데이터가 있다면 언피클해서 담기
|
302
|
-
red_data = pickle.loads(val)
|
303
|
-
final_results[code] = red_data
|
304
|
-
|
305
|
-
# --- (2) 필요한 티커만 직접 연산 ---
|
306
|
-
newly_computed_data = {}
|
307
|
-
for code in missing_codes:
|
308
|
-
mylogger.debug(f"*** bulk_generate_data : {code}")
|
309
|
-
data = cls(code, expect_earn)._generate_data(refresh=True)
|
310
|
-
newly_computed_data[code] = data
|
311
|
-
|
312
|
-
# --- (3) 파이프라인 SET ---
|
313
|
-
if newly_computed_data:
|
314
|
-
pipe = myredis.Base.redis_client.pipeline()
|
315
|
-
for code, data in newly_computed_data.items():
|
316
|
-
redis_key = f"{code}_{cls.REDIS_RED_DATA_SUFFIX}_{expect_earn}"
|
317
|
-
# ProphetLatestData 객체를 pickle로 직렬화
|
318
|
-
pickled_data = pickle.dumps(data)
|
319
|
-
# SET + expire_time
|
320
|
-
pipe.setex(redis_key, expire_time, pickled_data)
|
321
|
-
pipe.execute()
|
322
|
-
|
323
|
-
# 최종 결과 딕셔너리 (캐시에 있었던 것 + 새로 만든 것)
|
324
|
-
final_results.update(newly_computed_data)
|
325
|
-
return final_results
|
280
|
+
def bulk_get_data(cls, codes: List[str], expect_earn: float, refresh: bool) -> Dict[str, RedData]:
|
281
|
+
return myredis.Corps.bulk_get_or_compute(
|
282
|
+
[f"{code}_{cls.REDIS_RED_DATA_SUFFIX}_{expect_earn}" for code in codes],
|
283
|
+
lambda key: cls(key[:6], expect_earn)._generate_data(refresh=True),
|
284
|
+
refresh=refresh
|
285
|
+
)
|
286
|
+
|
326
287
|
|
327
288
|
@staticmethod
|
328
289
|
def ranking(expect_earn: float = 0.06, refresh=False) -> OrderedDict:
|
329
290
|
mylogger.info("**** Start red ranking ... ****")
|
330
291
|
|
331
|
-
data = Red.
|
292
|
+
data = Red.bulk_get_data(myredis.Corps.list_all_codes(), expect_earn, refresh)
|
332
293
|
mylogger.debug(data)
|
333
294
|
return OrderedDict(sorted(data.items(), key=lambda x: x[1].score, reverse=True))
|
334
295
|
|
@@ -2,7 +2,6 @@ from collections import OrderedDict
|
|
2
2
|
from datetime import datetime, timedelta
|
3
3
|
from typing import Tuple, List, Dict, Union
|
4
4
|
|
5
|
-
import pickle
|
6
5
|
import yfinance as yf
|
7
6
|
import pandas as pd
|
8
7
|
from prophet import Prophet
|
@@ -137,6 +136,7 @@ class MyProphet:
|
|
137
136
|
df['ds'] = df['ds'].dt.tz_localize(None)
|
138
137
|
# 추가 변수를 정규화
|
139
138
|
df['volume_scaled'] = self.scaler.fit_transform(df[['volume']])
|
139
|
+
|
140
140
|
mylogger.debug('_preprocessing_for_prophet')
|
141
141
|
mylogger.debug(df)
|
142
142
|
self.initialized = True
|
@@ -171,15 +171,20 @@ class MyProphet:
|
|
171
171
|
mylogger.debug(forecast)
|
172
172
|
return forecast
|
173
173
|
|
174
|
-
mylogger.debug("Initializing data for MyProphet")
|
174
|
+
mylogger.debug(f"{self.ticker} : Initializing data for MyProphet")
|
175
175
|
|
176
176
|
self.scaler = StandardScaler()
|
177
177
|
self.model = Prophet()
|
178
178
|
|
179
179
|
self.raw_data = get_raw_data()
|
180
180
|
mylogger.debug(self.raw_data)
|
181
|
-
|
182
|
-
|
181
|
+
try:
|
182
|
+
self.df_real = preprocessing_for_prophet()
|
183
|
+
self.df_forecast = make_forecast()
|
184
|
+
except ValueError as e:
|
185
|
+
mylogger.error(f"{self.ticker} : 빈 데이터프레임...{e}")
|
186
|
+
self.df_real = pd.DataFrame()
|
187
|
+
self.df_forecast = pd.DataFrame()
|
183
188
|
|
184
189
|
def _make_prophet_latest_data(self) -> ProphetLatestData:
|
185
190
|
def scoring(price: float, yhat_lower: float, yhat_upper: float, method: str = 'sigmoid') -> Tuple[str, int]:
|
@@ -226,22 +231,32 @@ class MyProphet:
|
|
226
231
|
|
227
232
|
if not self.initialized:
|
228
233
|
self.initializing()
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
self.df_forecast
|
233
|
-
|
234
|
-
|
235
|
-
data = ProphetLatestData(
|
236
|
-
ticker=self.ticker,
|
237
|
-
date=latest_row['ds'].date(),
|
238
|
-
price=latest_row['y'],
|
239
|
-
yhat=latest_yhat['yhat'],
|
240
|
-
yhat_lower=latest_yhat['yhat_lower'],
|
241
|
-
yhat_upper=latest_yhat['yhat_upper'],
|
242
|
-
)
|
234
|
+
try:
|
235
|
+
latest_row = self.df_real.iloc[-1]
|
236
|
+
latest_yhat = \
|
237
|
+
self.df_forecast.loc[
|
238
|
+
self.df_forecast['ds'] == latest_row['ds'], ['ds', 'yhat_lower', 'yhat_upper', 'yhat']].iloc[
|
239
|
+
0].to_dict()
|
243
240
|
|
244
|
-
|
241
|
+
data = ProphetLatestData(
|
242
|
+
ticker=self.ticker,
|
243
|
+
date=latest_row['ds'].date(),
|
244
|
+
price=latest_row['y'],
|
245
|
+
yhat=latest_yhat['yhat'],
|
246
|
+
yhat_lower=latest_yhat['yhat_lower'],
|
247
|
+
yhat_upper=latest_yhat['yhat_upper'],
|
248
|
+
)
|
249
|
+
|
250
|
+
data.trading_action, data.score = scoring(data.price, data.yhat_lower, data.yhat_upper)
|
251
|
+
except Exception:
|
252
|
+
data = ProphetLatestData(
|
253
|
+
ticker=self.ticker,
|
254
|
+
date=datetime.now().date(),
|
255
|
+
price=float('nan'),
|
256
|
+
yhat=float('nan'),
|
257
|
+
yhat_lower=float('nan'),
|
258
|
+
yhat_upper=float('nan'),
|
259
|
+
)
|
245
260
|
return data
|
246
261
|
|
247
262
|
def generate_latest_data(self, refresh: bool) -> ProphetLatestData:
|
@@ -257,7 +272,7 @@ class MyProphet:
|
|
257
272
|
mylogger.debug("**** Start generate_data... ****")
|
258
273
|
redis_name = f'{self.ticker}_{self.REDIS_LATEST_DATA_SUFFIX}'
|
259
274
|
|
260
|
-
mylogger.
|
275
|
+
mylogger.debug(
|
261
276
|
f"redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time / 3600}h")
|
262
277
|
|
263
278
|
prophet_data = myredis.Base.fetch_and_cache_data(redis_name, refresh, self._make_prophet_latest_data, timer=expire_time)
|
@@ -285,20 +300,31 @@ class MyProphet:
|
|
285
300
|
if not self.initialized:
|
286
301
|
self.initializing()
|
287
302
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
303
|
+
try:
|
304
|
+
# 날짜를 기준으로 합치기 (outer join)
|
305
|
+
merged_df = pd.merge(self.df_real, self.df_forecast, on="ds", how="outer")
|
306
|
+
# 날짜 정렬
|
307
|
+
merged_df = merged_df.sort_values(by="ds").reset_index(drop=True)
|
308
|
+
|
309
|
+
data = ProphetChartData(
|
310
|
+
ticker=self.ticker,
|
311
|
+
labels=merged_df["ds"].tolist(),
|
312
|
+
prices=[{"x": ds, "y": y} for ds, y in zip(merged_df["ds"], merged_df["y"]) if pd.notna(y)], # type: ignore
|
313
|
+
yhats=[{"x": ds, "y": yhat} for ds, yhat in zip(merged_df["ds"], merged_df["yhat"])], # type: ignore
|
314
|
+
yhat_uppers=[{"x": ds, "y": yhat_upper} for ds, yhat_upper in zip(merged_df["ds"], merged_df["yhat_upper"])], # type: ignore
|
315
|
+
yhat_lowers=[{"x": ds, "y": yhat_lower} for ds, yhat_lower in zip(merged_df["ds"], merged_df["yhat_lower"])], # type: ignore
|
316
|
+
is_prophet_up=tsa.common.is_up_by_OLS(self.df_forecast.set_index('ds')['yhat'].to_dict()),
|
317
|
+
)
|
318
|
+
except Exception:
|
319
|
+
data = ProphetChartData(
|
320
|
+
ticker=self.ticker,
|
321
|
+
labels=[],
|
322
|
+
prices=[],
|
323
|
+
yhats=[],
|
324
|
+
yhat_uppers=[],
|
325
|
+
yhat_lowers=[],
|
326
|
+
is_prophet_up=False,
|
327
|
+
)
|
302
328
|
return data
|
303
329
|
|
304
330
|
prophet_chart_data = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_generate_prophet_chart_data,
|
@@ -326,49 +352,11 @@ class MyProphet:
|
|
326
352
|
|
327
353
|
@staticmethod
|
328
354
|
def bulk_get_latest_data(tickers: List[str], refresh: bool) -> Dict[str, ProphetLatestData]:
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
results_from_redis = pipe.execute() # [val1, val2, ...]
|
335
|
-
|
336
|
-
final_results = {}
|
337
|
-
missing_tickers = []
|
338
|
-
|
339
|
-
# refresh=True 이면 기존 데이터 무시하고 다시 계산해야 하므로 모두 missing 처리
|
340
|
-
if refresh:
|
341
|
-
missing_tickers = tickers[:]
|
342
|
-
else:
|
343
|
-
# refresh=False 이면, Redis 값이 None인 티커만 다시 계산
|
344
|
-
for ticker, val in zip(tickers, results_from_redis):
|
345
|
-
if val is None:
|
346
|
-
missing_tickers.append(ticker)
|
347
|
-
else:
|
348
|
-
# Redis에 pickled 데이터가 있다면 언피클해서 담기
|
349
|
-
prophet_data = pickle.loads(val)
|
350
|
-
final_results[ticker] = prophet_data
|
351
|
-
|
352
|
-
# --- (2) 필요한 티커만 직접 연산 ---
|
353
|
-
newly_computed_data = {}
|
354
|
-
for ticker in missing_tickers:
|
355
|
-
data = MyProphet(ticker)._make_prophet_latest_data()
|
356
|
-
newly_computed_data[ticker] = data
|
357
|
-
|
358
|
-
# --- (3) 파이프라인 SET ---
|
359
|
-
if newly_computed_data:
|
360
|
-
pipe = myredis.Base.redis_client.pipeline()
|
361
|
-
for ticker, data in newly_computed_data.items():
|
362
|
-
redis_key = f"{ticker}_{MyProphet.REDIS_LATEST_DATA_SUFFIX}"
|
363
|
-
# ProphetLatestData 객체를 pickle로 직렬화
|
364
|
-
pickled_data = pickle.dumps(data)
|
365
|
-
# SET + expire_time
|
366
|
-
pipe.setex(redis_key, expire_time, pickled_data)
|
367
|
-
pipe.execute()
|
368
|
-
|
369
|
-
# 최종 결과 딕셔너리 (캐시에 있었던 것 + 새로 만든 것)
|
370
|
-
final_results.update(newly_computed_data)
|
371
|
-
return final_results
|
355
|
+
return myredis.Base.bulk_get_or_compute(
|
356
|
+
tickers,
|
357
|
+
lambda ticker: MyProphet(ticker)._make_prophet_latest_data(),
|
358
|
+
refresh=refresh,
|
359
|
+
)
|
372
360
|
|
373
361
|
|
374
362
|
class CorpProphet(MyProphet):
|
@@ -1,11 +1,11 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: analyser_hj3415
|
3
|
-
Version: 4.
|
3
|
+
Version: 4.2.0
|
4
4
|
Summary: Stock analyser and database processing programs
|
5
5
|
Requires-Python: >=3.6
|
6
6
|
Description-Content-Type: text/markdown
|
7
7
|
Requires-Dist: utils-hj3415>=3.0.10
|
8
|
-
Requires-Dist: db-hj3415>=4.
|
8
|
+
Requires-Dist: db-hj3415>=4.6.0
|
9
9
|
Requires-Dist: scikit-learn>=1.5.2
|
10
10
|
Requires-Dist: plotly>=5.24.1
|
11
11
|
Requires-Dist: yfinance>=0.2.44
|
@@ -5,13 +5,13 @@ analyser_hj3415/analyser/eval/__init__.py,sha256=IP1d0Q3nOCAD3zK1qxrC685MkJQfUh-
|
|
5
5
|
analyser_hj3415/analyser/eval/blue.py,sha256=p5JPwkQYoO0dsOe3VnfMV3pOWLzNsAFvLCUK56f85Xo,10782
|
6
6
|
analyser_hj3415/analyser/eval/common.py,sha256=sNXapoofShA43ww_SLjXmIjkrAr1AhAcezdaN_X_3Us,11443
|
7
7
|
analyser_hj3415/analyser/eval/growth.py,sha256=tlHxLx4u5h7bNG0T8ViJujX20QllfrSaBl-TBqFNkEs,6362
|
8
|
-
analyser_hj3415/analyser/eval/mil.py,sha256=
|
9
|
-
analyser_hj3415/analyser/eval/red.py,sha256=
|
8
|
+
analyser_hj3415/analyser/eval/mil.py,sha256=m1Ca7U7IR3A3tbRYAw8wG7SQsI-_LzwYdY9xfM9G8II,15426
|
9
|
+
analyser_hj3415/analyser/eval/red.py,sha256=iA4wpuayJalnlwn2SypsHuWPcthvxvNx2CleDraucFs,12168
|
10
10
|
analyser_hj3415/analyser/tsa/__init__.py,sha256=pg20ZQRABedTdaIoOr5t043RNKtJ7ji_WmnZrD1IhPg,147
|
11
11
|
analyser_hj3415/analyser/tsa/common.py,sha256=ZLUkifupOlLKsrPiqR3y6FaEN4M_loZhxCZXYxkX0us,1874
|
12
12
|
analyser_hj3415/analyser/tsa/lstm.py,sha256=oENuJyyo6U9MMn4UF4ZauGai51_dJisMSUNBiH8udXo,28998
|
13
|
-
analyser_hj3415/analyser/tsa/prophet.py,sha256=
|
14
|
-
analyser_hj3415-4.
|
15
|
-
analyser_hj3415-4.
|
16
|
-
analyser_hj3415-4.
|
17
|
-
analyser_hj3415-4.
|
13
|
+
analyser_hj3415/analyser/tsa/prophet.py,sha256=0xcZWk8wf-A7W_27dJErUZ9x3_Fei37KgDRwH0SUT_I,17142
|
14
|
+
analyser_hj3415-4.2.0.dist-info/entry_points.txt,sha256=ZfjPnJuH8SzvhE9vftIPMBIofsc65IAWYOhqOC_L5ck,65
|
15
|
+
analyser_hj3415-4.2.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
16
|
+
analyser_hj3415-4.2.0.dist-info/METADATA,sha256=LzOMuNTAdFxO0HV_7kI1HrVWqEm-AxQCR3RQky_i6lc,6777
|
17
|
+
analyser_hj3415-4.2.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|