analyser_hj3415 3.0.4__py3-none-any.whl → 3.1.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,6 +8,6 @@ if env_path is None:
8
8
  load_dotenv(env_path)
9
9
 
10
10
  from analyser_hj3415.analyser import eval
11
- from analyser_hj3415.analyser import score
11
+ from analyser_hj3415.analyser import compile
12
12
  from analyser_hj3415.analyser import tsa
13
13
 
@@ -0,0 +1,145 @@
1
+ import os
2
+ from collections import OrderedDict
3
+ from typing import Union
4
+
5
+ from db_hj3415 import myredis,mymongo
6
+ from utils_hj3415 import tools, setup_logger
7
+
8
+ from analyser_hj3415.analyser import tsa
9
+ from analyser_hj3415.analyser import eval
10
+
11
+ mylogger = setup_logger(__name__,'WARNING')
12
+ expire_time = tools.to_int(os.getenv('DEFAULT_EXPIRE_TIME_H', 48)) * 3600
13
+
14
+
15
+ class Compile:
16
+ def __init__(self, code: str, expect_earn=0.06):
17
+ assert tools.is_6digit(code), f'Invalid value : {code}'
18
+ self._code = code
19
+ self.name = mymongo.Corps.get_name(code)
20
+ self.red = eval.Red(code, expect_earn)
21
+ self.mil = eval.Mil(code)
22
+ self.prophet = tsa.CorpProphet(code)
23
+
24
+ @property
25
+ def code(self) -> str:
26
+ return self._code
27
+
28
+ @code.setter
29
+ def code(self, code: str):
30
+ assert tools.is_6digit(code), f'Invalid value : {code}'
31
+ mylogger.info(f'change code : {self.code} -> {code}')
32
+ self._code = code
33
+ self.name = mymongo.Corps.get_name(code)
34
+ self.red.code = code
35
+ self.mil.code = code
36
+ self.prophet.code = code
37
+
38
+ def get(self, refresh=False) -> dict:
39
+ print(f"{self.code}/{self.name}의 compiling을 시작합니다.")
40
+ redis_name = self.code + '_compile_scores'
41
+ print(
42
+ f"redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time/3600}h")
43
+
44
+ def fetch_compile_scores() -> dict:
45
+ mylogger.info("Red score 계산중..")
46
+ red_score = self.red.get(verbose=False).score
47
+
48
+ mylogger.info("Mil data 계산중..")
49
+ mil_data = self.mil.get(verbose=False)
50
+
51
+ mylogger.info("\tProphet 최근 데이터 조회중..")
52
+ trading_action, prophet_score = self.prophet.scoring()
53
+
54
+ return {
55
+ 'name': self.name,
56
+ 'red_score': red_score,
57
+ '이익지표': mil_data.이익지표,
58
+ '주주수익률': mil_data.주주수익률,
59
+ 'trading_action': trading_action,
60
+ 'prophet_score': prophet_score,
61
+ }
62
+ data_dict = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_compile_scores, timer=expire_time)
63
+ return data_dict
64
+
65
+ @staticmethod
66
+ def prophet_ranking(refresh=False, top: Union[int, str]='all') -> OrderedDict:
67
+
68
+ print("**** Start Compiling scores and sorting... ****")
69
+ redis_name = 'prophet_ranking'
70
+
71
+ print(
72
+ f"redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time/3600}h")
73
+
74
+ def fetch_ranking() -> dict:
75
+ data = {}
76
+ c = Compile('005930')
77
+ for code in myredis.Corps.list_all_codes():
78
+ try:
79
+ c.code = code
80
+ except ValueError:
81
+ mylogger.error(f'prophet ranking error : {code}')
82
+ continue
83
+ scores= c.get(refresh=refresh)
84
+ print(f'{code} compiled : {scores}')
85
+ data[code] = scores
86
+ return data
87
+
88
+ data_dict = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_ranking, timer=expire_time)
89
+
90
+ # prophet_score를 기준으로 정렬
91
+ ranking = OrderedDict(sorted(data_dict.items(), key=lambda x: x[1]['prophet_score'], reverse=True))
92
+
93
+ if top == 'all':
94
+ return ranking
95
+ else:
96
+ if isinstance(top, int):
97
+ return OrderedDict(list(ranking.items())[:top])
98
+ else:
99
+ raise ValueError("top 인자는 'all' 이나 int형 이어야 합니다.")
100
+
101
+ @staticmethod
102
+ def analyse_lstm_topn(refresh: bool, top=40):
103
+ ranking_topn = Compile.prophet_ranking(refresh=False, top=top)
104
+ mylogger.info(ranking_topn)
105
+ corp_lstm = tsa.CorpLSTM('005930')
106
+ print(f"*** LSTM prediction redis cashing top{top} items ***")
107
+ for i, (code, _) in enumerate(ranking_topn.items()):
108
+ corp_lstm.code = code
109
+ print(f"{i + 1}. {corp_lstm.code}/{corp_lstm.name}")
110
+ corp_lstm.initializing()
111
+ corp_lstm.get_final_predictions(refresh=refresh, num=5)
112
+
113
+ @staticmethod
114
+ def red_ranking(expect_earn: float = 0.06, refresh=False) -> OrderedDict:
115
+ # 이전 expect earn 과 비교하여 다르거나 없으면 강제 refresh 설정
116
+ redis_name = 'red_ranking_prev_expect_earn'
117
+ pee = tools.to_float(myredis.Base.get_value(redis_name))
118
+ if pee != expect_earn:
119
+ # expect earn의 이전 계산값이 없거나 이전 값과 다르면 새로 계산
120
+ mylogger.warning(
121
+ f"expect earn : {expect_earn} / prev expect earn : {pee} 두 값이 달라 refresh = True"
122
+ )
123
+ myredis.Base.set_value(redis_name, str(expect_earn))
124
+ refresh = True
125
+
126
+ print("**** Start red_ranking... ****")
127
+ redis_name = 'red_ranking'
128
+ print(
129
+ f"redisname: '{redis_name}' / expect_earn: {expect_earn} / refresh : {refresh} / expire_time : {expire_time / 3600}h")
130
+
131
+ def fetch_ranking(refresh_in: bool) -> dict:
132
+ data = {}
133
+ red = eval.Red(code='005930', expect_earn=expect_earn)
134
+ for i, code in enumerate(myredis.Corps.list_all_codes()):
135
+ red.code = code
136
+ red_score = red.get(refresh=refresh_in, verbose=False).score
137
+ if red_score > 0:
138
+ data[code] = red_score
139
+ print(f"{i}: {red} - {red_score}")
140
+ return data
141
+
142
+ data_dict = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_ranking, refresh, timer=expire_time)
143
+
144
+ return OrderedDict(sorted(data_dict.items(), key=lambda item: item[1], reverse=True))
145
+
@@ -9,6 +9,46 @@ mylogger = setup_logger(__name__,'WARNING')
9
9
 
10
10
 
11
11
  class Tools:
12
+ @staticmethod
13
+ def sigmoid_score(deviation, a=1.0, b=2.0):
14
+ """
15
+ Calculates a normalized score using a sigmoid function based on the provided deviation value.
16
+
17
+ This method applies the sigmoid function to a logarithmically transformed deviation value
18
+ to map it to a range between 0 and 100. The shape of the sigmoid curve can be adjusted
19
+ with parameters `a` and `b`.
20
+
21
+ Parameters:
22
+ deviation (float): The deviation value to be transformed. Must be a non-negative value.
23
+ a (float): The steepness of the sigmoid curve. Default is 1.0.
24
+ b (float): The x-offset for the sigmoid curve. Default is 2.0.
25
+
26
+ Returns:
27
+ float: A score between 0 and 100 derived from the provided deviation value.
28
+ """
29
+ # 예: x = log10(deviation + 1)
30
+ x = math.log10(deviation + 1)
31
+ s = 1 / (1 + math.exp(-a * (x - b))) # 0~1 범위
32
+ return s * 100 # 0~100 범위
33
+
34
+ @staticmethod
35
+ def log_score(deviation):
36
+ """
37
+ Compute and return the logarithmic score scaled by a constant factor.
38
+
39
+ This method takes a numerical deviation value, adds one to it, computes its
40
+ base-10 logarithm, and then multiplies the result by a constant factor of 33
41
+ to scale the resulting logarithmic score.
42
+
43
+ Parameters:
44
+ deviation (float): The numerical deviation value to calculate the
45
+ logarithmic score for. Should be a non-negative number.
46
+
47
+ Returns:
48
+ float: The scaled logarithmic score computed based on the input deviation.
49
+ """
50
+ return math.log10(deviation + 1) * 33
51
+
12
52
  @staticmethod
13
53
  def cal_deviation(v1: float, v2: float) -> float:
14
54
  """
@@ -1,6 +1,5 @@
1
1
  import os
2
2
  from dataclasses import dataclass, asdict
3
- from collections import OrderedDict
4
3
  from typing import Tuple
5
4
  import math
6
5
 
@@ -68,7 +67,7 @@ class RedData:
68
67
  발행주식수: int
69
68
 
70
69
  date: list
71
-
70
+ 주가: float
72
71
  red_price: float
73
72
  score: int
74
73
 
@@ -98,6 +97,7 @@ class Red:
98
97
  self.c103 = myredis.C103(code, 'c103재무상태표q')
99
98
 
100
99
  self.name = self.c101.get_name()
100
+ self.recent_price = tools.to_float(self.c101.get_recent()['주가'])
101
101
  self._code = code
102
102
 
103
103
  self.expect_earn = expect_earn
@@ -117,6 +117,7 @@ class Red:
117
117
  self.c103.code = code
118
118
 
119
119
  self.name = self.c101.get_name()
120
+ self.recent_price = tools.to_float(self.c101.get_recent()['주가'])
120
121
  self._code = code
121
122
 
122
123
  def _calc비유동부채(self, refresh: bool) -> Tuple[str, float]:
@@ -152,24 +153,23 @@ class Red:
152
153
  else:
153
154
  return d, 비유동부채
154
155
 
155
- def _score(self, red_price: int, refresh: bool) -> int:
156
+ def _score(self, red_price: int) -> int:
156
157
  """red price와 최근 주가의 괴리율 파악
157
158
 
158
159
  Returns:
159
160
  int : 주가와 red price 비교한 괴리율
160
161
  """
161
- try:
162
- recent_price = tools.to_float(self.c101.get_recent(refresh)['주가'])
163
- except KeyError:
162
+ if math.isnan(self.recent_price):
164
163
  return 0
165
164
 
166
- deviation = Tools.cal_deviation(recent_price, red_price)
167
- if red_price < 0 or (recent_price >= red_price):
168
- score = 0
169
- else:
170
- score = tools.to_int(math.log10(deviation + 1) * 33) # desmos그래프상 33이 제일 적당한듯(최대100점에 가깝게)
165
+ deviation = Tools.cal_deviation(self.recent_price, red_price)
171
166
 
172
- mylogger.debug(f"최근주가 : {recent_price} red가격 : {red_price} 괴리율 : {tools.to_int(deviation)} score : {score}")
167
+ score = tools.to_int(Tools.sigmoid_score(deviation))
168
+ #score = tools.to_int(Tools.log_score(deviation))
169
+ if self.recent_price >= red_price:
170
+ score = -score
171
+
172
+ mylogger.debug(f"최근주가 : {self.recent_price} red가격 : {red_price} 괴리율 : {tools.to_int(deviation)} score : {score}")
173
173
 
174
174
  return score
175
175
 
@@ -201,7 +201,7 @@ class Red:
201
201
  except (ZeroDivisionError, ValueError):
202
202
  red_price = math.nan
203
203
 
204
- score = self._score(red_price, refresh)
204
+ score = self._score(red_price)
205
205
 
206
206
  try:
207
207
  date_list = Tools.date_set(d1, d2, d3, d4)
@@ -224,6 +224,7 @@ class Red:
224
224
  발행주식수 = 발행주식수,
225
225
  date = date_list,
226
226
  red_price = red_price,
227
+ 주가 = self.recent_price,
227
228
  score = score,
228
229
  )
229
230
 
@@ -261,35 +262,3 @@ class Red:
261
262
 
262
263
  return RedData(**myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_generate_data, refresh, timer=expire_time))
263
264
 
264
- @classmethod
265
- def ranking(cls, expect_earn: float = 0.06, refresh = False) -> OrderedDict:
266
- # 이전 expect earn 과 비교하여 다르거나 없으면 강제 refresh 설정
267
- redis_name = 'red_ranking_prev_expect_earn'
268
- pee = tools.to_float(myredis.Base.get_value(redis_name))
269
- if pee != expect_earn:
270
- # expect earn의 이전 계산값이 없거나 이전 값과 다르면 새로 계산
271
- mylogger.warning(
272
- f"expect earn : {expect_earn} / prev expect earn : {pee} 두 값이 달라 refresh = True"
273
- )
274
- myredis.Base.set_value(redis_name, str(expect_earn))
275
- refresh = True
276
-
277
- print("**** Start red_ranking... ****")
278
- redis_name = 'red_ranking'
279
- print(f"redisname: '{redis_name}' / expect_earn: {expect_earn} / refresh : {refresh} / expire_time : {expire_time/3600}h")
280
-
281
- def fetch_ranking(refresh_in: bool) -> dict:
282
- data = {}
283
- red = Red(code='005930', expect_earn=expect_earn)
284
- for i, code in enumerate(myredis.Corps.list_all_codes()):
285
- red.code = code
286
- red_score = red.get(refresh=refresh_in, verbose=False).score
287
- if red_score > 0:
288
- data[code] = red_score
289
- print(f"{i}: {red} - {red_score}")
290
- return data
291
-
292
- data_dict = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_ranking, refresh, timer=expire_time)
293
-
294
- return OrderedDict(sorted(data_dict.items(), key=lambda item: item[1], reverse=True))
295
-
@@ -1,2 +1,7 @@
1
- from .lstm import *
2
- from .prophet import *
1
+ from analyser_hj3415.analyser.tsa.lstm import *
2
+ from analyser_hj3415.analyser.tsa.prophet import *
3
+
4
+ MIs = {
5
+ "wti": "CL=F",
6
+ "gold": "GC=F",
7
+ }
@@ -20,16 +20,15 @@ from dataclasses import dataclass
20
20
 
21
21
  from utils_hj3415 import tools, setup_logger
22
22
  from db_hj3415 import myredis
23
- from analyser_hj3415.analyser import score
24
23
 
25
24
 
26
- mylogger = setup_logger(__name__,'WARNING')
25
+ mylogger = setup_logger(__name__,'INFO')
27
26
  expire_time = tools.to_int(os.getenv('DEFAULT_EXPIRE_TIME_H', 48)) * 3600
28
27
 
29
28
 
30
29
  @dataclass
31
30
  class LSTMData:
32
- code: str
31
+ ticker: str
33
32
 
34
33
  data_2d: np.ndarray
35
34
  train_size: int
@@ -47,7 +46,7 @@ class LSTMGrade:
47
46
  """
48
47
  딥러닝 모델의 학습 결과를 평가하기 위해 사용하는 데이터 클래스
49
48
  """
50
- code: str
49
+ ticker: str
51
50
  train_mse: float
52
51
  train_mae: float
53
52
  train_r2: float
@@ -57,20 +56,17 @@ class LSTMGrade:
57
56
 
58
57
 
59
58
  class MyLSTM:
60
- """
61
- LSTM(Long Short-Term Memory)
62
- """
63
59
  # 미래 몇일을 예측할 것인가?
64
60
  future_days = 30
65
61
 
66
- def __init__(self, code: str):
67
- assert tools.is_6digit(code), f'Invalid value : {code}'
68
- self._code = code
69
- self.name = myredis.Corps(code, 'c101').get_name()
62
+ def __init__(self, ticker: str):
63
+ mylogger.info(f'set up ticker : {ticker}')
70
64
  self.scaler = MinMaxScaler(feature_range=(0, 1))
65
+ self._ticker = ticker
66
+
71
67
  self.raw_data = pd.DataFrame()
72
68
  self.lstm_data = LSTMData(
73
- code=self.code,
69
+ ticker=self.ticker,
74
70
  data_2d=np.array([]),
75
71
  train_size=0,
76
72
  train_data_2d=np.array([]),
@@ -82,20 +78,18 @@ class MyLSTM:
82
78
  )
83
79
 
84
80
  @property
85
- def code(self) -> str:
86
- return self._code
81
+ def ticker(self) -> str:
82
+ return self._ticker
87
83
 
88
- @code.setter
89
- def code(self, code: str):
90
- assert tools.is_6digit(code), f'Invalid value : {code}'
91
- mylogger.debug(f'change code : {self.code} -> {code}')
92
-
93
- self._code = code
94
- self.name = myredis.Corps(code, 'c101').get_name()
84
+ @ticker.setter
85
+ def ticker(self, ticker: str):
86
+ mylogger.info(f'change ticker : {self.ticker} -> {ticker}')
95
87
  self.scaler = MinMaxScaler(feature_range=(0, 1))
88
+ self._ticker = ticker
89
+
96
90
  self.raw_data = pd.DataFrame()
97
91
  self.lstm_data = LSTMData(
98
- code=self.code,
92
+ ticker=self.ticker,
99
93
  data_2d=np.array([]),
100
94
  train_size=0,
101
95
  train_data_2d=np.array([]),
@@ -108,6 +102,9 @@ class MyLSTM:
108
102
 
109
103
  def initializing(self):
110
104
  """
105
+ LSTM 분석을 위해 데이터를 준비하는 과정
106
+ get_final_predictions(refresh=True)를 시행하기전에 반드시 먼저 실행해줘아 한다.
107
+
111
108
  Fetches stock price data for the last four years from Yahoo Finance and prepares
112
109
  it for use in an LSTM model by normalizing, splitting into training and testing datasets,
113
110
  and reshaping the data.
@@ -126,7 +123,7 @@ class MyLSTM:
126
123
  f"Get raw data from yfinance - start: {four_years_ago.strftime('%Y-%m-%d')}, end: {today.strftime('%Y-%m-%d')}")
127
124
 
128
125
  df = yf.download(
129
- self.code + '.KS',
126
+ tickers=self.ticker,
130
127
  start=four_years_ago.strftime('%Y-%m-%d'),
131
128
  end=today.strftime('%Y-%m-%d')
132
129
  )
@@ -174,7 +171,7 @@ class MyLSTM:
174
171
  X_test_3d = X_test.reshape(X_test.shape[0], X_test.shape[1], 1)
175
172
  except IndexError:
176
173
  return LSTMData(
177
- code=self.code,
174
+ ticker=self.ticker,
178
175
  data_2d=np.array([]),
179
176
  train_size=0,
180
177
  train_data_2d=np.array([]),
@@ -191,7 +188,7 @@ class MyLSTM:
191
188
  f'len - X_train_3d : {len(X_train_3d)}, X_test_3d : {len(X_test_3d)}, y_train : {len(y_train_1d)}, y_test : {len(y_test_1d)}')
192
189
 
193
190
  return LSTMData(
194
- code=self.code,
191
+ ticker=self.ticker,
195
192
  data_2d=data_2d,
196
193
  train_size=train_size,
197
194
  train_data_2d=train_data_2d,
@@ -288,7 +285,7 @@ class MyLSTM:
288
285
  if len(train_predictions) == 0 or len(test_predictions) == 0:
289
286
  mylogger.warning("딥러닝 결과가 없어서 LSTMGrade 데이터를 비워서 반환합니다.")
290
287
  return LSTMGrade(
291
- code= self.code,
288
+ ticker=self.ticker,
292
289
  train_mse=float('nan'),
293
290
  train_mae=float('nan'),
294
291
  train_r2=float('nan'),
@@ -323,7 +320,7 @@ class MyLSTM:
323
320
  # 과적합에 대한 평가는 train 과 test를 비교하여 test가 너무 않좋으면 과적합 의심.
324
321
 
325
322
  return LSTMGrade(
326
- code=self.code,
323
+ ticker=self.ticker,
327
324
  train_mse=train_mse,
328
325
  train_mae=train_mae,
329
326
  train_r2=train_r2,
@@ -380,32 +377,77 @@ class MyLSTM:
380
377
 
381
378
  def get_final_predictions(self, refresh: bool, num=5) -> Tuple[dict, LSTMGrade]:
382
379
  """
383
- Fetches final predictions based on an ensemble method using deep learning models. This process averages the
384
- predictions to forecast future data, primarily for debugging purposes as it is challenging to utilize
385
- Redis cache for evaluation.
380
+ Fetch and process predictions for future data.
381
+
382
+ This function fetches predictions from a Redis cache or calculates predictions if the data is
383
+ not found or if a refresh is requested. Predictions are determined using an ensemble training
384
+ method which averages predictions to forecast future trends. Additionally, the function checks
385
+ and caches whether the predicted data demonstrates an increasing trend over time.
386
386
 
387
- Parameters:
388
- refresh (bool): Flag to indicate whether to refresh cached data.
389
- num (int, optional): Number of iterations for predictions. Defaults to 5.
387
+ Args:
388
+ refresh (bool): If True, forces recalculation and cache refresh of predictions.
389
+ num (int): Number of times to repeat ensemble training for more consistent predictions.
390
+ Defaults to 5.
390
391
 
391
392
  Returns:
392
- Tuple[dict, LSTMGrade]: A tuple containing a dictionary of future predictions and their corresponding
393
- evaluation grade.
393
+ Tuple[dict, LSTMGrade]: A tuple containing a dictionary of future date-price pairs and the
394
+ evaluation grade of the LSTM prediction model.
394
395
 
395
396
  Raises:
396
- AssertionError: If the lengths of future dates and predicted values do not match.
397
-
398
- Notes:
399
- - This function integrates ensemble training and caching of predictive data.
400
- - The future prediction keys correspond to the dates in "YYYY-MM-DD" format.
401
- - Makes use of Redis for data retrieval and caching mechanisms.
397
+ AssertionError: Raised if the lengths of 'future_dates' and 'final_future_predictions' do
398
+ not match during the data preparation.
402
399
  """
403
400
  print("**** Start get_final_predictions... ****")
404
- redis_name = f'{self.code}_mylstm_predictions'
401
+ redis_name = f'{self.ticker}_mylstm_predictions'
405
402
 
406
403
  print(
407
404
  f"redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time/3600}h")
408
405
 
406
+ def caching_is_lstm_up(future_data_in: dict):
407
+ """
408
+ 날짜(str)를 키, 수치(float)를 값으로 갖는 딕셔너리를
409
+ 선형회귀분석(최소제곱법)을 통해 추세가 우상향인지 판별.
410
+
411
+ Returns:
412
+ bool: 기울기가 양수이면 True, 아니면 False
413
+ """
414
+
415
+ print("**** Caching is_lstm_up ... ****")
416
+ redis_name = f'{self.ticker}_is_lstm_up'
417
+ print(f"redisname: '{redis_name}' / expire_time : {expire_time / 3600}h")
418
+
419
+
420
+ if not future_data_in:
421
+ # 데이터가 비어있으면 추세를 판단할 수 없음
422
+ return False
423
+
424
+ # 1) 날짜(키) 기준 오름차순 정렬
425
+ sorted_dates = sorted(future_data_in.keys())
426
+ values = [future_data_in[d] for d in sorted_dates]
427
+
428
+ # 2) x 축을 0,1,2... 형태로 부여 (날짜 간격을 동일하게 가정)
429
+ x = np.arange(len(values), dtype=float)
430
+ y = np.array(values, dtype=float)
431
+
432
+ # 3) 선형 회귀(최소제곱법)로 기울기(slope) 계산
433
+ x_mean = np.mean(x)
434
+ y_mean = np.mean(y)
435
+
436
+ # 분자: sum((xi - x_mean) * (yi - y_mean))
437
+ numerator = np.sum((x - x_mean) * (y - y_mean))
438
+ # 분모: sum((xi - x_mean)^2)
439
+ denominator = np.sum((x - x_mean) ** 2)
440
+
441
+ if denominator == 0:
442
+ # 데이터가 1개 이하인 경우 등
443
+ return False
444
+
445
+ slope = numerator / denominator
446
+
447
+ # 4) 기울기가 양수면 "우상향 추세"로 판별
448
+ is_up = slope > 0
449
+ myredis.Base.set_value(redis_name, is_up, expire_time)
450
+
409
451
  def fetch_final_predictions(num_in) -> tuple:
410
452
  """
411
453
  앙상블법으로 딥러닝을 모델을 반복해서 평균을 내서 미래를 예측한다. 평가는 래시스 캐시로 반환하기 어려워 일단 디버그 용도로만 사용하기로
@@ -443,7 +485,12 @@ class MyLSTM:
443
485
 
444
486
  return future_data, lstm_grade
445
487
 
446
- return myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_final_predictions, num, timer=expire_time)
488
+ future_data, lstm_grade = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_final_predictions, num, timer=expire_time)
489
+
490
+ # 증가 추세인지 아닌지 레디스 캐시에 저장
491
+ caching_is_lstm_up(future_data)
492
+
493
+ return future_data, lstm_grade
447
494
 
448
495
  def export(self, refresh=False, to="str", num=5) -> Optional[str]:
449
496
  """
@@ -558,11 +605,11 @@ class MyLSTM:
558
605
  return graph_html
559
606
  elif to == 'png':
560
607
  # 그래프를 PNG 파일로 저장
561
- fig.write_image(f"myLSTM_{self.code}.png")
608
+ fig.write_image(f"myLSTM_{self.ticker}.png")
562
609
  return None
563
610
  elif to == 'htmlfile':
564
611
  # 그래프를 HTML로 저장
565
- plot(fig, filename=f'myLSTM_{self.code}.html', auto_open=False)
612
+ plot(fig, filename=f'myLSTM_{self.ticker}.html', auto_open=False)
566
613
  return None
567
614
  else:
568
615
  Exception("to 인자가 맞지 않습니다.")
@@ -608,7 +655,7 @@ class MyLSTM:
608
655
  plt.xlabel('Date')
609
656
  plt.ylabel('Stock Price')
610
657
  plt.legend()
611
- plt.title(f'{self.name} Stock Price Prediction with LSTM')
658
+ plt.title(f'{self.ticker} Stock Price Prediction with LSTM')
612
659
  plt.show()
613
660
 
614
661
  """# 시각화2
@@ -621,50 +668,53 @@ class MyLSTM:
621
668
  plt.title('Stock Price Prediction with LSTM Ensemble')
622
669
  plt.show()"""
623
670
 
624
- def is_up(self) -> bool:
671
+ def is_lstm_up(self) -> bool:
625
672
  """
626
- Determines if the predicted data indicates an increasing trend.
673
+ Determines whether the LSTM model is active or not.
627
674
 
628
- This method evaluates the results of future predictions and checks if all the
629
- subsequent values in the prediction data increase compared to their predecessors.
675
+ This method checks the status of an LSTM model by querying a Redis
676
+ value identified by a specific code indicating LSTM activity.
630
677
 
631
678
  Returns:
632
- bool: True if all future predicted values increase in sequence, False otherwise.
633
-
634
- Raises:
635
- None: This method does not raise any exceptions.
636
- """
637
- # 튜플의 [0]은 날짜 [1]은 값 배열
638
- future_data, _ = self.get_final_predictions(refresh=False)
639
- # 데이터를 1D 배열로 변환
640
- flattened_data = list(future_data.values())
641
- mylogger.debug(f"flattened_data : {flattened_data}")
642
- # 증가 여부 확인
643
- return all(flattened_data[i] < flattened_data[i + 1] for i in range(len(flattened_data) - 1))
644
-
645
- @staticmethod
646
- def caching_based_on_prophet_ranking(refresh: bool, top=20):
647
- """
648
- This method utilizes a ranking system generated by the Score class for
649
- predictive caching.
650
-
651
- Parameters
652
- ----------
653
- refresh : bool
654
- Whether to refresh the predictions for the selected items.
655
- top : int, optional
656
- The number of top-ranked items to process, by default 20.
679
+ bool: True if the LSTM model is active (up), False otherwise.
657
680
  """
658
- ranking_topn = score.Score.ranking(refresh=False, top=top)
659
- mylogger.info(ranking_topn)
660
- mylstm = MyLSTM('005930')
661
- print(f"*** LSTM prediction redis cashing top{top} items ***")
662
- for i, (code, _) in enumerate(ranking_topn.items()):
663
- mylstm.code = code
664
- print(f"{i + 1}. {mylstm.code}/{mylstm.name}")
665
- mylstm.initializing()
666
- mylstm.get_final_predictions(refresh=refresh, num=5)
681
+ return myredis.Base.get_value(f'{self.ticker}_is_lstm_up')
682
+
683
+
684
+ class CorpLSTM(MyLSTM):
685
+ def __init__(self, code: str):
686
+ assert tools.is_6digit(code), f'Invalid value : {code}'
687
+ self._code = code
688
+ self.name = myredis.Corps(code, 'c101').get_name()
689
+ super().__init__(ticker=self.code + '.KS')
690
+
691
+ @property
692
+ def code(self) -> str:
693
+ return self._code
694
+
695
+ @code.setter
696
+ def code(self, code: str):
697
+ assert tools.is_6digit(code), f'Invalid value : {code}'
698
+ self._code = code
699
+ self.name = myredis.Corps(code, 'c101').get_name()
700
+ self.ticker = self.code + '.KS'
667
701
 
668
702
 
703
+ class MILSTM(MyLSTM):
704
+ def __init__(self, mi_type: str):
705
+ from analyser_hj3415.analyser.tsa import MIs
706
+ assert mi_type in MIs.keys(), f"Invalid MI type ({MIs.keys()})"
707
+ self._mi_type = mi_type
708
+ super().__init__(ticker=MIs[mi_type])
669
709
 
710
+ @property
711
+ def mi_type(self) -> str:
712
+ return self._mi_type
713
+
714
+ @mi_type.setter
715
+ def mi_type(self, mi_type: str):
716
+ from analyser_hj3415.analyser.tsa import MIs
717
+ assert mi_type in MIs.keys(), f"Invalid MI type ({MIs.keys()})"
718
+ self._mi_type = mi_type
719
+ self.ticker = MIs[mi_type]
670
720
 
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime, timedelta
2
- from typing import Optional
2
+ from typing import Optional, Tuple
3
3
  import yfinance as yf
4
4
  import pandas as pd
5
5
  from prophet import Prophet
@@ -17,28 +17,27 @@ mylogger = setup_logger(__name__,'WARNING')
17
17
 
18
18
 
19
19
  class MyProphet:
20
- def __init__(self, code: str):
21
- assert tools.is_6digit(code), f'Invalid value : {code}'
20
+ def __init__(self, ticker: str):
21
+ mylogger.info(f'set up ticker : {ticker}')
22
22
  self.scaler = StandardScaler()
23
-
24
23
  self.model = Prophet()
25
- self._code = code
26
- self.name = myredis.Corps(code, 'c101').get_name()
24
+ self._ticker = ticker
25
+
27
26
  self.raw_data = self._get_raw_data()
28
27
  self.df_real = self._preprocessing_for_prophet()
29
28
  self.df_forecast = self._make_forecast()
30
29
 
31
30
  @property
32
- def code(self) -> str:
33
- return self._code
31
+ def ticker(self) -> str:
32
+ return self._ticker
34
33
 
35
- @code.setter
36
- def code(self, code: str):
37
- assert tools.is_6digit(code), f'Invalid value : {code}'
38
- mylogger.info(f'change code : {self.code} -> {code}')
34
+ @ticker.setter
35
+ def ticker(self, ticker: str):
36
+ mylogger.info(f'change ticker : {self.ticker} -> {ticker}')
37
+ self.scaler = StandardScaler()
39
38
  self.model = Prophet()
40
- self._code = code
41
- self.name = myredis.Corps(code, 'c101').get_name()
39
+ self._ticker = ticker
40
+
42
41
  self.raw_data = self._get_raw_data()
43
42
  self.df_real = self._preprocessing_for_prophet()
44
43
  self.df_forecast = self._make_forecast()
@@ -65,7 +64,7 @@ class MyProphet:
65
64
  four_years_ago = today - timedelta(days=365 * 4)
66
65
 
67
66
  return yf.download(
68
- self.code + '.KS',
67
+ tickers=self.ticker,
69
68
  start=four_years_ago.strftime('%Y-%m-%d'),
70
69
  end=today.strftime('%Y-%m-%d')
71
70
  )
@@ -168,40 +167,103 @@ class MyProphet:
168
167
  return graph_html
169
168
  elif to == 'png':
170
169
  # 그래프를 PNG 파일로 저장
171
- fig.write_image(f"myprophet_{self.code}.png")
170
+ fig.write_image(f"myprophet_{self.ticker}.png")
172
171
  return None
173
172
  elif to == 'htmlfile':
174
173
  # 그래프를 HTML로 저장
175
- plot(fig, filename=f'myprophet_{self.code}.html', auto_open=False)
174
+ plot(fig, filename=f'myprophet_{self.ticker}.html', auto_open=False)
176
175
  return None
177
176
  else:
178
177
  Exception("to 인자가 맞지 않습니다.")
179
178
 
180
- def scoring(self) -> int:
179
+ def scoring(self) -> Tuple[str, int]:
181
180
  """
182
- prophet의 yhat_lower 예측치와 주가를 비교하여 주가가 낮으면 양의 점수를 높으면 음의 점수를 준다.
181
+ Calculate and return a trading action and associated score based on recent market data and forecasted values.
182
+
183
+ The method computes deviations between the recent price and predefined forecasted bounds (`yhat_lower` and `yhat_upper`)
184
+ to determine whether to buy, sell, or hold a financial instrument. The calculation involves generating a deviation score
185
+ that is transformed using a sigmoid function. The scores are then adjusted based on the position of the recent price relative
186
+ to the forecasted bounds. Logging is used extensively throughout the method to record the values and decisions for debugging
187
+ and traceability purposes.
183
188
 
184
189
  Returns:
185
- int: The calculated score based on the deviation between the recent price
186
- and the expected lower limit.
190
+ A tuple containing a string indicating the recommended trading action ('buy', 'sell', 'hold')
191
+ and an integer score associated with that action.
187
192
 
188
193
  Raises:
189
- AttributeError: Raised if the necessary attributes like `df_real` or methods like `get_yhat`
190
- are not correctly set or implemented.
191
- KeyError: Raised if the expected keys (`'yhat_lower'` or `'y'`) are not found in the data involved.
192
- ValueError: Raised if the format of data does not conform to expected structure for calculations.
194
+ KeyError: If required keys are missing in the `yhat_dict` or `last_real_data` dictionary-like structures.
193
195
  """
194
196
  last_real_data = self.df_real.iloc[-1]
195
197
  recent_price = last_real_data['y']
196
198
  recent_date = datetime.strftime(last_real_data['ds'], '%Y-%m-%d')
197
199
  yhat_dict = self.get_yhat()
198
200
  mylogger.info(f'recent_price: {recent_price}, yhat_dict: {yhat_dict}')
199
- yhat_lower = int(yhat_dict['yhat_lower'])
200
- deviation = int(eval.Tools.cal_deviation(recent_price, yhat_lower))
201
- if recent_price > yhat_lower:
202
- score = -deviation
201
+
202
+ yhat_lower = tools.to_int(yhat_dict['yhat_lower'])
203
+ yhat_upper = tools.to_int(yhat_dict['yhat_upper'])
204
+ #yhat = tools.to_int(yhat_dict['yhat'])
205
+
206
+ buying_deviation = eval.Tools.cal_deviation(recent_price, yhat_lower)
207
+
208
+ buying_score = tools.to_int(eval.Tools.sigmoid_score(buying_deviation))
209
+ # score = tools.to_int(Tools.log_score(deviation))
210
+
211
+ if recent_price >= yhat_lower:
212
+ buying_score = -buying_score
213
+
214
+ selling_deviation = eval.Tools.cal_deviation(recent_price, yhat_upper)
215
+
216
+ selling_score = tools.to_int(eval.Tools.sigmoid_score(selling_deviation))
217
+ # score = tools.to_int(Tools.log_score(deviation))
218
+
219
+ if recent_price <= yhat_upper:
220
+ selling_score = -selling_score
221
+
222
+ mylogger.info(f"{self.ticker} date: {recent_date} 가격: {recent_price}"
223
+ f" yhat_lower:{yhat_lower} yhat_upper:{yhat_upper}"
224
+ f" buying_score:{buying_score} selling_score:{selling_score}")
225
+
226
+ if buying_score > 0:
227
+ return 'buy', buying_score
228
+ elif selling_score > 0:
229
+ return 'sell', selling_score
203
230
  else:
204
- score = deviation
205
- mylogger.info(f"{self.code}/{self.name} date: {recent_date} 가격:{recent_price} 기대하한값:{yhat_lower} 편차:{deviation} score:{score}")
206
- return score
231
+ return 'hold', 0
232
+
233
+
234
+ class CorpProphet(MyProphet):
235
+ def __init__(self, code: str):
236
+ assert tools.is_6digit(code), f'Invalid value : {code}'
237
+ self._code = code
238
+ self.name = myredis.Corps(code, 'c101').get_name()
239
+ super().__init__(ticker=self.code + '.KS')
240
+
241
+ @property
242
+ def code(self) -> str:
243
+ return self._code
244
+
245
+ @code.setter
246
+ def code(self, code: str):
247
+ assert tools.is_6digit(code), f'Invalid value : {code}'
248
+ self._code = code
249
+ self.name = myredis.Corps(code, 'c101').get_name()
250
+ self.ticker = self.code + '.KS'
251
+
252
+
253
+ class MIProphet(MyProphet):
254
+ def __init__(self, mi_type: str):
255
+ from analyser_hj3415.analyser.tsa import MIs
256
+ assert mi_type in MIs.keys(), f"Invalid MI type ({MIs.keys()})"
257
+ self._mi_type = mi_type
258
+ super().__init__(ticker=MIs[mi_type])
259
+
260
+ @property
261
+ def mi_type(self) -> str:
262
+ return self._mi_type
207
263
 
264
+ @mi_type.setter
265
+ def mi_type(self, mi_type: str):
266
+ from analyser_hj3415.analyser.tsa import MIs
267
+ assert mi_type in MIs.keys(), f"Invalid MI type ({MIs.keys()})"
268
+ self._mi_type = mi_type
269
+ self.ticker = MIs[mi_type]
analyser_hj3415/cli.py CHANGED
@@ -2,31 +2,37 @@ import argparse
2
2
  import pprint
3
3
 
4
4
  from utils_hj3415 import tools
5
- from analyser_hj3415.analyser import eval, tsa, score
5
+ from analyser_hj3415.analyser import eval, tsa, compile
6
6
  from db_hj3415 import myredis, mymongo
7
7
 
8
8
 
9
9
  def analyser_manager():
10
+ from analyser_hj3415.analyser.tsa import MIs
10
11
  parser = argparse.ArgumentParser(description="Analyser Commands")
11
12
  type_subparsers = parser.add_subparsers(dest='type', help='분석 타입')
12
13
 
13
14
  # prophet 명령어 서브파서
14
15
  prophet_parser = type_subparsers.add_parser('prophet', help='MyProphet 타입')
15
16
  prophet_subparser = prophet_parser.add_subparsers(dest='command', help='prophet 관련된 명령')
16
- # ranking 파서
17
+ # prophet - ranking 파서
17
18
  ranking_parser = prophet_subparser.add_parser('ranking', help='prophet 랭킹 책정 및 레디스 저장')
18
19
  ranking_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
20
+ # prophet - score 파서
21
+ prophet_get_parser = prophet_subparser.add_parser('score', help='prophet score 계산')
22
+ prophet_get_parser.add_argument('target', type=str, help=f'종목코드 or {list(MIs.keys())}')
23
+ prophet_get_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
19
24
 
20
25
  # lstm 명령어 서브파서
21
26
  lstm_parser = type_subparsers.add_parser('lstm', help='MyLSTM 타입')
22
27
  lstm_subparser = lstm_parser.add_subparsers(dest='command', help='lstm 관련된 명령')
23
- # caching 파서
28
+ # lstm - caching 파서
24
29
  caching_parser = lstm_subparser.add_parser('caching', help='lstm 랭킹 책정 및 레디스 저장')
25
30
  caching_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
26
31
  caching_parser.add_argument('-t', '--top', type=int, help='prophet ranking 몇위까지 작업을 할지')
27
- # red - get 파서
28
- lstm_get_parser = lstm_subparser.add_parser('get', help='lstm get 책정 레디스 저장')
29
- lstm_get_parser.add_argument('code', type=str, help='종목코드')
32
+ # lstm - predict 파서
33
+ lstm_get_parser = lstm_subparser.add_parser('predict', help='lstm get final prediction 시행')
34
+ lstm_get_parser.add_argument('target', type=str, help=f'종목코드 or {list(MIs.keys())}')
35
+ lstm_get_parser.add_argument('-n', '--num', type=int, default=5, help='ensemble training 횟수 설정')
30
36
  lstm_get_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
31
37
 
32
38
  # red 명령어 서브파서
@@ -92,9 +98,9 @@ def analyser_manager():
92
98
  elif args.command == 'ranking':
93
99
  mymongo.Logs.save('cli','INFO', 'run >> analyser red ranking')
94
100
  if args.expect_earn is None:
95
- result = eval.Red.ranking(refresh=args.refresh)
101
+ result = compile.Compile.red_ranking(refresh=args.refresh)
96
102
  else:
97
- result = eval.Red.ranking(expect_earn=args.expect_earn, refresh=args.refresh)
103
+ result = compile.Compile.red_ranking(expect_earn=args.expect_earn, refresh=args.refresh)
98
104
  print(result)
99
105
 
100
106
  elif args.type == 'mil':
@@ -147,22 +153,37 @@ def analyser_manager():
147
153
 
148
154
  elif args.type == 'prophet':
149
155
  if args.command == 'ranking':
150
- result = score.Score.ranking(refresh=args.refresh)
156
+ result = compile.Compile.prophet_ranking(refresh=args.refresh)
151
157
  print(result)
152
158
  mymongo.Logs.save('cli','INFO', 'run >> analyser prophet ranking')
159
+ elif args.command == 'score':
160
+ if args.target in MIs.keys():
161
+ myprophet = tsa.MIProphet(args.target)
162
+ elif tools.is_6digit(args.target):
163
+ myprophet = tsa.CorpProphet(args.target)
164
+ else:
165
+ raise Exception("Invalid target")
166
+ print(myprophet.scoring())
167
+ # mymongo.Logs.save('cli','INFO', f'run >> analyser prophet get {args.target}')
153
168
 
154
169
  elif args.type == 'lstm':
155
170
  if args.command == 'caching':
156
171
  if args.top:
157
- tsa.MyLSTM.caching_based_on_prophet_ranking(refresh=args.refresh, top=args.top)
172
+ compile.Compile.analyse_lstm_topn(refresh=args.refresh, top=args.top)
158
173
  else:
159
- tsa.MyLSTM.caching_based_on_prophet_ranking(refresh=args.refresh)
174
+ compile.Compile.analyse_lstm_topn(refresh=args.refresh)
160
175
  mymongo.Logs.save('cli','INFO', f'run >> analyser lstm caching / top={args.top if args.top else 20}')
161
- elif args.command == 'get':
162
- assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
163
- mylstm = tsa.MyLSTM(args.code)
176
+ elif args.command == 'predict':
177
+ if args.target in MIs.keys():
178
+ mylstm = tsa.MILSTM(args.target)
179
+ elif tools.is_6digit(args.target):
180
+ mylstm = tsa.CorpLSTM(args.target)
181
+ else:
182
+ raise Exception("Invalid target")
164
183
  mylstm.initializing()
165
- mylstm.get_final_predictions(refresh=args.refresh, num=5)
166
- mymongo.Logs.save('cli','INFO', f'run >> analyser lstm get {args.code}')
184
+ future_data, grade = mylstm.get_final_predictions(refresh=args.refresh, num=args.num)
185
+ print(future_data)
186
+ print(grade)
187
+ # mymongo.Logs.save('cli','INFO', f'run >> analyser lstm get {args.target}')
167
188
  else:
168
189
  parser.print_help()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: analyser_hj3415
3
- Version: 3.0.4
3
+ Version: 3.1.0
4
4
  Summary: Stock analyser and database processing programs
5
5
  Requires-Python: >=3.6
6
6
  Description-Content-Type: text/markdown
@@ -0,0 +1,22 @@
1
+ analyser_hj3415/__init__.py,sha256=f2E9Neh7Nzkhvdj7HWWlgxZK2sB95rBtaIgWEHzxK9E,450
2
+ analyser_hj3415/cli.py,sha256=2lwbI8GD-AEXO89Ma2khWOOJWeL4HLxnDyW5fVZZvmM,10486
3
+ analyser_hj3415/analyser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ analyser_hj3415/analyser/compile.py,sha256=e3ziVf8SX0K6rk9XFPAhi7cpxTaFlKYCQ2dHACzLlPo,5763
5
+ analyser_hj3415/analyser/eval/__init__.py,sha256=IP1d0Q3nOCAD3zK1qxrC685MkJQfUh-qaXc7xptTxk8,80
6
+ analyser_hj3415/analyser/eval/blue.py,sha256=fq_eln7-EdwICNQ2sMXAfZKXGhQ29EaJwsGvn7xA29U,7632
7
+ analyser_hj3415/analyser/eval/common.py,sha256=wcWJg_jNgr02i1U62oZGgLt2iscdle9X-u4aMnZl89Q,14127
8
+ analyser_hj3415/analyser/eval/growth.py,sha256=OXuBuPzVw3q6kpelNigiX7xvJNUP1gv26DNetc4aews,3315
9
+ analyser_hj3415/analyser/eval/mil.py,sha256=wN1jPu5zLJjoDk3kL7piL-4jWhpsAMeQpsTZll0a2dw,10568
10
+ analyser_hj3415/analyser/eval/red.py,sha256=rJbidKlo2s2EJHBGn4HDbfCDcEGP5Rst8iQeJ6jlzNA,10988
11
+ analyser_hj3415/analyser/tsa/__init__.py,sha256=ZmhZmdhxKv3zfh4byDROZOIWl8jBmfJQMo8phJTXkSg,160
12
+ analyser_hj3415/analyser/tsa/lstm.py,sha256=6T_JzlcDV8l-8-delE9zlbl-JeWF6zO0gg-RRNV5noE,29972
13
+ analyser_hj3415/analyser/tsa/prophet.py,sha256=Xeb1GxvQQAacFtTFYN0fmzk6iYQkFSex2HDaY2Ek7OY,10154
14
+ analyser_hj3415/workroom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ analyser_hj3415/workroom/mysklearn.py,sha256=wJXKz5MqqTzADdG2mqRMMzc_G9RzwYjj5_j4gyOopxQ,2030
16
+ analyser_hj3415/workroom/mysklearn2.py,sha256=1lIy6EWEQHkOzDS-av8U0zQH6DuCLKWMI73dnJx5KRs,1495
17
+ analyser_hj3415/workroom/score.py,sha256=P6nHBJYmyhigGtT4qna4BmNtvt4B93b7SKyzdstJK24,17376
18
+ analyser_hj3415/workroom/trash.py,sha256=zF-W0piqkGr66UP6-iybo9EXh2gO0RP6R1FnIpsGkl8,12262
19
+ analyser_hj3415-3.1.0.dist-info/entry_points.txt,sha256=ZfjPnJuH8SzvhE9vftIPMBIofsc65IAWYOhqOC_L5ck,65
20
+ analyser_hj3415-3.1.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
21
+ analyser_hj3415-3.1.0.dist-info/METADATA,sha256=wSqUbHt-HM5qoipE-zictBm6Z2g3hfqugpEsLc-GzMw,6777
22
+ analyser_hj3415-3.1.0.dist-info/RECORD,,
@@ -1,157 +0,0 @@
1
- import os
2
- import datetime
3
- from collections import OrderedDict
4
-
5
- from db_hj3415 import myredis
6
- from utils_hj3415 import tools, setup_logger
7
-
8
- from analyser_hj3415.analyser import tsa
9
- from analyser_hj3415.analyser import eval
10
-
11
- mylogger = setup_logger(__name__,'WARNING')
12
- expire_time = tools.to_int(os.getenv('DEFAULT_EXPIRE_TIME_H', 48)) * 3600
13
-
14
-
15
- class Score:
16
- def __init__(self, code):
17
- self._code = code
18
- self.c101 = myredis.C101(code)
19
- self.name = self.c101.get_name()
20
- self.c108 = myredis.C108(code)
21
- self.dart = myredis.Dart(code)
22
- self.red = eval.Red(code)
23
- self.mil = eval.Mil(code)
24
- self.lstm = tsa.MyLSTM(code)
25
- self.prophet = tsa.MyProphet(code)
26
-
27
- @property
28
- def code(self) -> str:
29
- return self._code
30
-
31
- @code.setter
32
- def code(self, code: str):
33
- assert tools.is_6digit(code), f'Invalid value : {code}'
34
- mylogger.info(f'change code : {self.code} -> {code}')
35
- self._code = code
36
- self.c101.code = code
37
- self.name = self.c101.get_name()
38
- self.c108.code = code
39
- self.dart.code = code
40
- self.red.code = code
41
- self.mil.code = code
42
- self.lstm.code = code
43
- self.prophet.code = code
44
-
45
- def get(self, refresh=False) -> dict:
46
- """
47
- 한 종목의 각분야 평가를 모아서 딕셔너리 형태로 반환함.
48
- redis_name = self.code + '_score'
49
-
50
- Returns:
51
- dict: A dictionary containing the following key-value pairs:
52
- - 'name': str - 종목명
53
- - '시가총액': str - 시가총액
54
- - 'is_update_c108': bool - 최근 3일 이내에 c108이 없데이트 되었는가
55
- - 'red_score': float - Red score
56
- - '이익지표': float - Mil의 이익지표
57
- - '주주수익률': float - Mil의 주주수익률
58
- - 'is_lstm_up': Union[bool, None] - lstm 예측치가 상승인지 아닌지, returns None - 데이터가 없으면..
59
- - 'prophet_score': int - prophet score
60
- """
61
- print(f"{self.code}/{self.name}의 scoring을 시작합니다.")
62
- redis_name = self.code + '_score'
63
- print(
64
- f"redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time/3600}h")
65
-
66
- def fetch_score() -> dict:
67
- mylogger.info("시가총액 데이터 추출중..")
68
- 시가총액 = tools.format_large_number(int(self.c101.get_recent()['시가총액']))
69
-
70
- mylogger.info("C108 최근 데이터 추출중..")
71
- # c108이 최근에 업데이트 되었는지...
72
- c108_recent_date = self.c108.get_recent_date()
73
- # print('code - ', code, ' | c108 recent date - ', c108_recent_date.date())
74
- if c108_recent_date is None:
75
- is_update_c108 = False
76
- else:
77
- is_update_c108 = tools.is_within_last_n_days(c108_recent_date,3)
78
-
79
- mylogger.info("Red score 계산중..")
80
- red_score = self.red.get(verbose=False).score
81
-
82
- mylogger.info("Mil data 계산중..")
83
- mil_data = self.mil.get(verbose=False)
84
-
85
- mylogger.info("Lstm 최근 데이터 조회중..")
86
- if myredis.Base.exists(f'{self.code}_mylstm_predictions'):
87
- is_lstm_up = self.lstm.is_up()
88
- else:
89
- is_lstm_up = None
90
-
91
- mylogger.info("\tProphet 최근 데이터 조회중..")
92
- prophet_score = self.prophet.scoring()
93
-
94
- return {
95
- 'name': self.name,
96
- '시가총액': 시가총액,
97
- 'is_update_c108': is_update_c108,
98
- 'red_score': red_score,
99
- '이익지표': mil_data.이익지표,
100
- '주주수익률': mil_data.주주수익률,
101
- 'is_lstm_up': is_lstm_up,
102
- 'prophet_score': prophet_score,
103
- }
104
- data_dict = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_score, timer=expire_time)
105
- return data_dict
106
-
107
- @classmethod
108
- def ranking(self, refresh=False, top='all') -> OrderedDict:
109
- """
110
- prophet score 기준으로 정렬하여 ordered dict로 반환함
111
-
112
- Parameters:
113
- refresh (bool): Specifies whether to refresh the ranking data. Defaults
114
- to `False`.
115
- top (Union[str, int]): Determines how many top rankings to return.
116
- Defaults to `'all'`. If an integer is provided, it limits the
117
- ranking to the specified count.
118
-
119
- Returns:
120
- OrderedDict: A dictionary containing the rankings, sorted in
121
- descending order by `prophet_score`.
122
-
123
- Raises:
124
- ValueError: Raised if the parameter `top` is neither `'all'` nor an
125
- integer.
126
- """
127
- print("**** Start score_ranking... ****")
128
- redis_name = 'score_ranking'
129
-
130
- print(
131
- f"redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time/3600}h")
132
-
133
- def fetch_ranking() -> dict:
134
- data = {}
135
- s = Score('005930')
136
- for code in myredis.Corps.list_all_codes():
137
- try:
138
- s.code = code
139
- except ValueError:
140
- mylogger.error(f'score ranking error : {code}')
141
- continue
142
- score = s.get(refresh=refresh)
143
- data[code] = score
144
- return data
145
-
146
- data_dict = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_ranking, timer=expire_time)
147
-
148
- # prophet_score를 기준으로 정렬
149
- ranking = OrderedDict(sorted(data_dict.items(), key=lambda x: x[1]['prophet_score'], reverse=True))
150
-
151
- if top == 'all':
152
- return ranking
153
- else:
154
- if isinstance(top, int):
155
- return OrderedDict(list(ranking.items())[:top])
156
- else:
157
- raise ValueError("top 인자는 'all' 이나 int형 이어야 합니다.")
@@ -1,22 +0,0 @@
1
- analyser_hj3415/__init__.py,sha256=huF0tm8fFC96sODN9TmVRmPhDizQ0yBTKNOw8miOBh0,448
2
- analyser_hj3415/cli.py,sha256=RHMz1JYSVPVkWU6xdrZo-fdRB3tBW3oSC2XrpI8Q2nk,9248
3
- analyser_hj3415/analyser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- analyser_hj3415/analyser/score.py,sha256=RmHWp7fSBkpgKd68SeSyg7oY3TT-nvPQGK36I5P6dk0,6100
5
- analyser_hj3415/analyser/eval/__init__.py,sha256=IP1d0Q3nOCAD3zK1qxrC685MkJQfUh-qaXc7xptTxk8,80
6
- analyser_hj3415/analyser/eval/blue.py,sha256=fq_eln7-EdwICNQ2sMXAfZKXGhQ29EaJwsGvn7xA29U,7632
7
- analyser_hj3415/analyser/eval/common.py,sha256=JBTVvzO6YoeMjKl6X2MF7wgB6fvPTEToHLJSv-V7V4k,12425
8
- analyser_hj3415/analyser/eval/growth.py,sha256=OXuBuPzVw3q6kpelNigiX7xvJNUP1gv26DNetc4aews,3315
9
- analyser_hj3415/analyser/eval/mil.py,sha256=wN1jPu5zLJjoDk3kL7piL-4jWhpsAMeQpsTZll0a2dw,10568
10
- analyser_hj3415/analyser/eval/red.py,sha256=SegWvklGFCCFpzg9qwKDEU0-iSq-d_lM6XRNhth8VWM,12529
11
- analyser_hj3415/analyser/tsa/__init__.py,sha256=rPnn4b8aEym2O9l_sAaIRUpOeXJw6sDj2XfUzy-mpvg,42
12
- analyser_hj3415/analyser/tsa/lstm.py,sha256=jrJlugPTugSH9liNGpiRMaigr3mq3FeGxDNVvcll3N8,28302
13
- analyser_hj3415/analyser/tsa/prophet.py,sha256=zL6RNSiwFKKliQVsR3yOHz13S2SrCfuXvBj62lqP80Y,7984
14
- analyser_hj3415/workroom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- analyser_hj3415/workroom/mysklearn.py,sha256=wJXKz5MqqTzADdG2mqRMMzc_G9RzwYjj5_j4gyOopxQ,2030
16
- analyser_hj3415/workroom/mysklearn2.py,sha256=1lIy6EWEQHkOzDS-av8U0zQH6DuCLKWMI73dnJx5KRs,1495
17
- analyser_hj3415/workroom/score.py,sha256=P6nHBJYmyhigGtT4qna4BmNtvt4B93b7SKyzdstJK24,17376
18
- analyser_hj3415/workroom/trash.py,sha256=zF-W0piqkGr66UP6-iybo9EXh2gO0RP6R1FnIpsGkl8,12262
19
- analyser_hj3415-3.0.4.dist-info/entry_points.txt,sha256=ZfjPnJuH8SzvhE9vftIPMBIofsc65IAWYOhqOC_L5ck,65
20
- analyser_hj3415-3.0.4.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
21
- analyser_hj3415-3.0.4.dist-info/METADATA,sha256=bR36Tb4IyoDHDhTRx5sS7hQ7sIvfzLn39NeP2e6dxEg,6777
22
- analyser_hj3415-3.0.4.dist-info/RECORD,,