analyser_hj3415 2.10.6__py3-none-any.whl → 3.0.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ from dotenv import load_dotenv
2
+ from utils_hj3415.tools import get_env_path
3
+ from utils_hj3415.logger import mylogger
4
+
5
+ env_path = get_env_path()
6
+ if env_path is None:
7
+ mylogger.warning(f"환경변수 파일(.env)를 찾을수 없습니다. 기본 설정값으로 프로그램을 실행합니다.")
8
+ load_dotenv(env_path)
9
+
10
+ from analyser_hj3415.analyser import eval
11
+ from analyser_hj3415.analyser import score
12
+ from analyser_hj3415.analyser import tsa
13
+
File without changes
@@ -0,0 +1,4 @@
1
+ from .red import *
2
+ from .mil import *
3
+ from .blue import *
4
+ from .growth import *
@@ -0,0 +1,187 @@
1
+ import os
2
+ from dataclasses import dataclass, asdict
3
+ from typing import Tuple
4
+ import math
5
+
6
+ from utils_hj3415 import tools, setup_logger
7
+ from db_hj3415 import myredis
8
+
9
+ from analyser_hj3415.analyser.eval.common import Tools
10
+
11
+
12
+ mylogger = setup_logger(__name__,'WARNING')
13
+ expire_time = tools.to_int(os.getenv('DEFAULT_EXPIRE_TIME_H', 48)) * 3600
14
+
15
+
16
+ @dataclass()
17
+ class BlueData:
18
+ code: str
19
+ name: str
20
+
21
+ 유동비율: float
22
+
23
+ 이자보상배율_r: float
24
+ 이자보상배율_dict: dict
25
+
26
+ 순운전자본회전율_r: float
27
+ 순운전자본회전율_dict: dict
28
+
29
+ 재고자산회전율_r: float
30
+ 재고자산회전율_dict: dict
31
+ 재고자산회전율_c106: dict
32
+
33
+ 순부채비율_r: float
34
+ 순부채비율_dict: dict
35
+
36
+ score: list
37
+ date: list
38
+
39
+
40
+ class Blue:
41
+ def __init__(self, code: str):
42
+ assert tools.is_6digit(code), f'Invalid value : {code}'
43
+ mylogger.debug(f"Blue : 종목코드 ({code})")
44
+
45
+ self.c101 = myredis.C101(code)
46
+ self.c103 = myredis.C103(code, 'c103재무상태표q')
47
+ self.c104 = myredis.C104(code, 'c104q')
48
+
49
+ self.name = self.c101.get_name()
50
+ self._code = code
51
+
52
+ def __str__(self):
53
+ return f"Blue({self.code}/{self.name})"
54
+
55
+ @property
56
+ def code(self) -> str:
57
+ return self._code
58
+
59
+ @code.setter
60
+ def code(self, code: str):
61
+ assert tools.is_6digit(code), f'Invalid value : {code}'
62
+ mylogger.debug(f"Blue : 종목코드 변경({self.code} -> {code})")
63
+
64
+ self.c101.code = code
65
+ self.c103.code = code
66
+ self.c104.code = code
67
+
68
+ self.name = self.c101.get_name()
69
+ self._code = code
70
+
71
+ def _calc유동비율(self, pop_count: int, refresh: bool) -> Tuple[str, float]:
72
+ """유동비율계산 - Blue에서 사용
73
+
74
+ c104q에서 최근유동비율 찾아보고 유효하지 않거나 \n
75
+ 100이하인 경우에는수동으로 계산해서 다시 한번 평가해 본다.\n
76
+ """
77
+ mylogger.info(f'In the calc유동비율... refresh : {refresh}')
78
+ self.c104.page = 'c104q'
79
+
80
+ 유동비율date, 유동비율value = self.c104.latest_value('유동비율', pop_count=pop_count)
81
+ mylogger.info(f'{self} 유동비율 : {유동비율value}/({유동비율date})')
82
+
83
+ if math.isnan(유동비율value) or 유동비율value < 100:
84
+ 유동자산date, 유동자산value = Tools.calc유동자산(self.c103, refresh)
85
+ 유동부채date, 유동부채value = Tools.calc유동부채(self.c103, refresh)
86
+
87
+ self.c103.page = 'c103현금흐름표q'
88
+ 추정영업현금흐름date, 추정영업현금흐름value = self.c103.sum_recent_4q('영업활동으로인한현금흐름', refresh)
89
+ mylogger.debug(f'{self} 계산전 유동비율 : {유동비율value} / ({유동비율date})')
90
+
91
+ 계산된유동비율 = 0
92
+ try:
93
+ 계산된유동비율 = round(((유동자산value + 추정영업현금흐름value) / 유동부채value) * 100, 2)
94
+ except ZeroDivisionError:
95
+ mylogger.info(f'유동자산: {유동자산value} + 추정영업현금흐름: {추정영업현금흐름value} / 유동부채: {유동부채value}')
96
+ 계산된유동비율 = float('inf')
97
+ finally:
98
+ mylogger.debug(f'{self} 계산된 유동비율 : {계산된유동비율}')
99
+
100
+ try:
101
+ date, *_ = Tools.date_set(유동자산date, 유동부채date, 추정영업현금흐름date)
102
+ except ValueError:
103
+ # 날짜 데이터가 없는경우
104
+ date = ''
105
+ mylogger.warning(f'{self} 유동비율 이상(100 이하 또는 nan) : {유동비율value} -> 재계산 : {계산된유동비율}')
106
+ return date, 계산된유동비율
107
+ else:
108
+ return 유동비율date, 유동비율value
109
+
110
+ def _score(self) -> list:
111
+ return [0 ,]
112
+
113
+ def _generate_data(self, refresh: bool) -> BlueData:
114
+ d1, 유동비율 = self._calc유동비율(pop_count=3, refresh=refresh)
115
+ mylogger.info(f'유동비율 {유동비율} / [{d1}]')
116
+
117
+ 재고자산회전율_c106 = myredis.C106.make_like_c106(self.code, 'c104q', '재고자산회전율', refresh)
118
+
119
+ self.c104.page = 'c104y'
120
+ _, 이자보상배율_dict = self.c104.find('이자보상배율', remove_yoy=True, refresh=refresh)
121
+ _, 순운전자본회전율_dict = self.c104.find('순운전자본회전율', remove_yoy=True, refresh=refresh)
122
+ _, 재고자산회전율_dict = self.c104.find('재고자산회전율', remove_yoy=True, refresh=refresh)
123
+ _, 순부채비율_dict = self.c104.find('순부채비율', remove_yoy=True, refresh=refresh)
124
+
125
+ self.c104.page = 'c104q'
126
+ d6, 이자보상배율_r = self.c104.latest_value_pop2('이자보상배율', refresh)
127
+ d7, 순운전자본회전율_r = self.c104.latest_value_pop2('순운전자본회전율', refresh)
128
+ d8, 재고자산회전율_r = self.c104.latest_value_pop2('재고자산회전율', refresh)
129
+ d9, 순부채비율_r = self.c104.latest_value_pop2('순부채비율', refresh)
130
+
131
+ if len(이자보상배율_dict) == 0:
132
+ mylogger.warning(f'empty dict - 이자보상배율 : {이자보상배율_r} / {이자보상배율_dict}')
133
+
134
+ if len(순운전자본회전율_dict) == 0:
135
+ mylogger.warning(f'empty dict - 순운전자본회전율 : {순운전자본회전율_r} / {순운전자본회전율_dict}')
136
+
137
+ if len(재고자산회전율_dict) == 0:
138
+ mylogger.warning(f'empty dict - 재고자산회전율 : {재고자산회전율_r} / {재고자산회전율_dict}')
139
+
140
+ if len(순부채비율_dict) == 0:
141
+ mylogger.warning(f'empty dict - 순부채비율 : {순부채비율_r} / {순부채비율_dict}')
142
+
143
+ score = self._score()
144
+
145
+ try:
146
+ date_list = Tools.date_set(d1, d6, d7, d8, d9)
147
+ except ValueError:
148
+ # 날짜 데이터가 없는경우
149
+ date_list = ['' ,]
150
+
151
+ return BlueData(
152
+ code= self.code,
153
+ name= self.name,
154
+ 유동비율= 유동비율,
155
+ 이자보상배율_r= 이자보상배율_r,
156
+ 이자보상배율_dict= 이자보상배율_dict,
157
+
158
+ 순운전자본회전율_r= 순운전자본회전율_r,
159
+ 순운전자본회전율_dict= 순운전자본회전율_dict,
160
+
161
+ 재고자산회전율_r= 재고자산회전율_r,
162
+ 재고자산회전율_dict= 재고자산회전율_dict,
163
+ 재고자산회전율_c106= 재고자산회전율_c106,
164
+
165
+ 순부채비율_r= 순부채비율_r,
166
+ 순부채비율_dict= 순부채비율_dict,
167
+
168
+ score= score,
169
+ date= date_list,
170
+ )
171
+
172
+ def get(self, refresh = False, verbose = True) -> BlueData:
173
+ """
174
+ BlueData 형식의 데이터를 계산하여 리턴하고 레디스 캐시에 저장한다.
175
+ :param refresh:
176
+ :return:
177
+ """
178
+ redis_name = f"{self.code}_blue"
179
+ mylogger.info(f"{self} BlueData를 레디스캐시에서 가져오거나 새로 생성합니다.. refresh : {refresh}")
180
+ if verbose:
181
+ print(f"{self} redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time /3600}h")
182
+
183
+ def fetch_generate_data(refresh_in: bool) -> dict:
184
+ return asdict(self._generate_data(refresh_in))
185
+
186
+ return BlueData \
187
+ (**myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_generate_data, refresh, timer=expire_time))
@@ -0,0 +1,267 @@
1
+ import math
2
+ from typing import Tuple
3
+
4
+ from db_hj3415 import myredis
5
+ from utils_hj3415.tools import nan_to_zero
6
+ from utils_hj3415 import setup_logger
7
+
8
+ mylogger = setup_logger(__name__,'WARNING')
9
+
10
+
11
+ class Tools:
12
+ @staticmethod
13
+ def cal_deviation(v1: float, v2: float) -> float:
14
+ """
15
+ Calculates the percentage deviation between two values.
16
+
17
+ This method computes the percentage deviation of the second value
18
+ from the first value based on the formula:
19
+ deviation = abs((v1 - v2) / v1) * 100. In the event the first value is
20
+ zero (division by zero), the function will return NaN to signify
21
+ an invalid computation.
22
+
23
+ Parameters:
24
+ v1 (float): The reference value. It represents the base for the relative
25
+ deviation calculation.
26
+ v2 (float): The value to compare against the reference.
27
+
28
+ Returns:
29
+ float: The computed percentage deviation. Returns NaN if the reference
30
+ value (v1) is zero.
31
+ """
32
+ try:
33
+ deviation = abs((v1 - v2) / v1) * 100
34
+ except ZeroDivisionError:
35
+ deviation = math.nan
36
+ return deviation
37
+
38
+ @staticmethod
39
+ def date_set(*args) -> list:
40
+ """
41
+ 인자로 받은 값의 비유효한 내용 제거(None,nan)하고 중복된 항목 제거하고 리스트로 반환한다.
42
+
43
+ 여기서 set의 의미는 집합을 뜻함
44
+
45
+ Filters and returns a list of unique non-null, non-empty values from
46
+ the provided arguments.
47
+
48
+ This static method processes the input arguments to retain only unique
49
+ values that are not empty strings, NaN values, or None. The result is
50
+ returned as a list.
51
+
52
+ Args:
53
+ *args: Arbitrary positional arguments to be filtered.
54
+
55
+ Returns:
56
+ list: A list of unique values after filtering out invalid entries.
57
+ """
58
+ return [i for i in {*args} if i != "" and i is not math.nan and i is not None]
59
+
60
+ @staticmethod
61
+ def calc당기순이익(c103: myredis.C103, refresh: bool) -> Tuple[str, float]:
62
+ """
63
+ 지배지분 당기순이익 계산
64
+
65
+ 일반적인 경우로는 직전 지배주주지분 당기순이익을 찾아서 반환한다.
66
+
67
+ 금융기관의 경우는 지배당기순이익이 없기 때문에 계산을 통해서 간접적으로 구한다.
68
+
69
+ Calculates "지배당기순이익" (Controlling Comprehensive Income) based on the given
70
+ financial data. The method retrieves or computes the value utilizing methods from
71
+ the `myredis.C103` class. It handles missing or 'Not-a-Number' conditions by
72
+ manually calculating from quarterly and annual financial figures. Logs the process
73
+ at various stages for debugging and auditing.
74
+
75
+ Args:
76
+ c103 (myredis.C103): An instance containing financial data and utilities to
77
+ access specific data points for the targeted calculation.
78
+ refresh (bool): A flag to determine whether or not to refresh the data
79
+ while accessing or computing financial values.
80
+
81
+ Returns:
82
+ Tuple[str, float]: A tuple where the first item is the most relevant date for
83
+ the calculated or retrieved value, and the second item is the calculated
84
+ or retrieved "지배당기순이익" (Controlling Comprehensive Income).
85
+ """
86
+ name = myredis.Corps(c103.code, 'c101').get_name(refresh=refresh)
87
+
88
+ mylogger.info(f'{c103.code} / {name} Tools : 당기순이익 계산.. refresh : {refresh}')
89
+ c103.page = 'c103재무상태표q'
90
+
91
+ d1, 지배당기순이익 = c103.latest_value_pop2('*(지배)당기순이익', refresh)
92
+ mylogger.debug(f"*(지배)당기순이익: {지배당기순이익}")
93
+
94
+ if math.isnan(지배당기순이익):
95
+ mylogger.warning(f"{c103.code} / {name} - (지배)당기순이익이 없는 종목. 수동으로 계산합니다.")
96
+ c103.page = 'c103손익계산서q'
97
+ d2, 최근4분기당기순이익 = c103.sum_recent_4q('당기순이익', refresh)
98
+ mylogger.debug(f"{c103.code} / {name} - 최근4분기당기순이익 : {최근4분기당기순이익}")
99
+ c103.page = 'c103재무상태표y'
100
+ d3, 비지배당기순이익 = c103.latest_value_pop2('*(비지배)당기순이익', refresh)
101
+ mylogger.debug(f"{c103.code} / {name} - 비지배당기순이익y : {비지배당기순이익}")
102
+ # 가변리스트 언패킹으로 하나의 날짜만 사용하고 나머지는 버린다.
103
+ # 여기서 *_는 “나머지 값을 다 무시하겠다”는 의미
104
+ mylogger.debug(f"d2:{d2}, d3: {d3}")
105
+ try:
106
+ date, *_ = Tools.date_set(d2, d3)
107
+ except ValueError:
108
+ # 날짜 데이터가 없는경우
109
+ date = ''
110
+ 계산된지배당기순이익 = round(최근4분기당기순이익 - nan_to_zero(비지배당기순이익), 1)
111
+ mylogger.debug(f"{c103.code} / {name} - 계산된 지배당기순이익 : {계산된지배당기순이익}")
112
+ return date, 계산된지배당기순이익
113
+ else:
114
+ return d1, 지배당기순이익
115
+
116
+ @staticmethod
117
+ def calc유동자산(c103: myredis.C103, refresh: bool) -> Tuple[str, float]:
118
+ """
119
+ 유효한 유동자산 계산
120
+
121
+ 일반적인 경우로 유동자산을 찾아서 반환한다.
122
+
123
+ 금융기관의 경우는 간접적으로 계산한다.
124
+
125
+ Calculates the current assets for a given company code.
126
+
127
+ For a specified company, the function calculates the recent 4-quarter
128
+ sum of current assets if available. If the data is not available or
129
+ contains invalid values, it attempts to calculate the current assets
130
+ manually using financial asset data such as cash equivalents, trading
131
+ securities, available-for-sale securities, and held-to-maturity securities.
132
+
133
+ Logs relevant information and warnings during the calculation process,
134
+ including any cases where data is unavailable or a manual calculation
135
+ is required.
136
+
137
+ Parameters:
138
+ c103 (myredis.C103): The instance representing financial data of a
139
+ specific company. This includes methods to extract and calculate
140
+ various data points.
141
+ refresh (bool): Indicator flag to determine whether to refresh the
142
+ underlying data before performing calculations.
143
+
144
+ Returns:
145
+ Tuple[str, float]: A tuple containing the date associated with the
146
+ financial data and the calculated or retrieved value of current
147
+ assets. If dates are not available, the date field may be empty.
148
+ """
149
+
150
+ name = myredis.Corps(c103.code, 'c101').get_name(refresh=refresh)
151
+
152
+ mylogger.info(f'{c103.code} / {name} Tools : 유동자산계산... refresh : {refresh}')
153
+ c103.page = 'c103재무상태표q'
154
+
155
+ d, 유동자산 = c103.sum_recent_4q('유동자산', refresh)
156
+ if math.isnan(유동자산):
157
+ mylogger.warning(f"{c103.code} / {name} - 유동자산이 없는 종목. 수동으로 계산합니다(금융관련업종일 가능성있음).")
158
+ d1, v1 = c103.latest_value_pop2('현금및예치금', refresh)
159
+ d2, v2 = c103.latest_value_pop2('단기매매금융자산', refresh)
160
+ d3, v3 = c103.latest_value_pop2('매도가능금융자산', refresh)
161
+ d4, v4 = c103.latest_value_pop2('만기보유금융자산', refresh)
162
+ mylogger.debug(f'{c103.code} / {name} 현금및예치금 : {d1}, {v1}')
163
+ mylogger.debug(f'{c103.code} / {name} 단기매매금융자산 : {d2}, {v2}')
164
+ mylogger.debug(f'{c103.code} / {name} 매도가능금융자산 : {d3}, {v3}')
165
+ mylogger.debug(f'{c103.code} / {name} 만기보유금융자산 : {d4}, {v4}')
166
+
167
+ try:
168
+ date, *_ = Tools.date_set(d1, d2, d3, d4)
169
+ except ValueError:
170
+ # 날짜 데이터가 없는경우
171
+ date = ''
172
+ 계산된유동자산value = round(
173
+ nan_to_zero(v1) + nan_to_zero(v2) + nan_to_zero(v3) + nan_to_zero(v4), 1)
174
+
175
+ mylogger.info(f"{c103.code} / {name} - 계산된 유동자산 : {계산된유동자산value}")
176
+ return date, 계산된유동자산value
177
+ else:
178
+ return d, 유동자산
179
+
180
+ @staticmethod
181
+ def calc유동부채(c103: myredis.C103, refresh: bool) -> Tuple[str, float]:
182
+ """
183
+ 유효한 유동부채 계산
184
+
185
+ 일반적인 경우로 유동부채를 찾아서 반환한다.
186
+
187
+ 금융기관의 경우는 간접적으로 계산한다.
188
+
189
+ Calculate '유동부채' (Current Liabilities) based on financial data of a specific entity.
190
+
191
+ This static method computes the recent '유동부채' value either from the sum of recent four
192
+ quarters using predefined keys or calculates manually if no valid data is available.
193
+ It includes logging for intermediate steps and supports handling missing values by logging
194
+ warnings and attempting a composed manual computation using alternative financial terms.
195
+
196
+ Args:
197
+ c103 (myredis.C103): The object containing financial data and operations for obtaining the required data.
198
+ refresh (bool): A flag to indicate whether to fetch the latest data forcibly.
199
+
200
+ Returns:
201
+ Tuple[str, float]: A tuple containing the `date` of financial data and the computed '유동부채' value.
202
+ """
203
+
204
+ name = myredis.Corps(c103.code, 'c101').get_name(refresh=refresh)
205
+
206
+ mylogger.info(f'{c103.code} / {name} Tools : 유동부채계산... refresh : {refresh}')
207
+ c103.page = 'c103재무상태표q'
208
+
209
+ d, 유동부채 = c103.sum_recent_4q('유동부채', refresh)
210
+ if math.isnan(유동부채):
211
+ mylogger.warning(f"{c103.code} / {name} - 유동부채가 없는 종목. 수동으로 계산합니다.")
212
+ d1, v1 = c103.latest_value_pop2('당기손익인식(지정)금융부채', refresh)
213
+ d2, v2 = c103.latest_value_pop2('당기손익-공정가치측정금융부채', refresh)
214
+ d3, v3 = c103.latest_value_pop2('매도파생결합증권', refresh)
215
+ d4, v4 = c103.latest_value_pop2('단기매매금융부채', refresh)
216
+ mylogger.debug(f'{c103.code} / {name} 당기손익인식(지정)금융부채 : {d1}, {v1}')
217
+ mylogger.debug(f'{c103.code} / {name} 당기손익-공정가치측정금융부채 : {d2}, {v2}')
218
+ mylogger.debug(f'{c103.code} / {name} 매도파생결합증권 : {d3}, {v3}')
219
+ mylogger.debug(f'{c103.code} / {name} 단기매매금융부채 : {d4}, {v4}')
220
+
221
+ try:
222
+ date, *_ = Tools.date_set(d1, d2, d3, d4)
223
+ except ValueError:
224
+ # 날짜 데이터가 없는경우
225
+ date = ''
226
+ 계산된유동부채value = round(
227
+ nan_to_zero(v1) + nan_to_zero(v2) + nan_to_zero(v3) + nan_to_zero(v4), 1)
228
+
229
+ mylogger.info(f"{c103.code} / {name} - 계산된 유동부채 : {계산된유동부채value}")
230
+ return date, 계산된유동부채value
231
+ else:
232
+ return d, 유동부채
233
+
234
+
235
+ """
236
+ - 각분기의 합이 연이 아닌 타이틀(즉 sum_4q를 사용하면 안됨)
237
+ '*(지배)당기순이익'
238
+ '*(비지배)당기순이익'
239
+ '장기차입금'
240
+ '현금및예치금'
241
+ '매도가능금융자산'
242
+ '매도파생결합증권'
243
+ '만기보유금융자산'
244
+ '당기손익-공정가치측정금융부채'
245
+ '당기손익인식(지정)금융부채'
246
+ '단기매매금융자산'
247
+ '단기매매금융부채'
248
+ '예수부채'
249
+ '차입부채'
250
+ '기타부채'
251
+ '보험계약부채(책임준비금)'
252
+ '*CAPEX'
253
+ 'ROE'
254
+ """
255
+
256
+ """
257
+ - sum_4q를 사용해도 되는 타이틀
258
+ '자산총계'
259
+ '당기순이익'
260
+ '유동자산'
261
+ '유동부채'
262
+ '비유동부채'
263
+
264
+ '영업활동으로인한현금흐름'
265
+ '재무활동으로인한현금흐름'
266
+ 'ROIC'
267
+ """
@@ -0,0 +1,110 @@
1
+ import os
2
+ from dataclasses import dataclass, asdict
3
+
4
+ from utils_hj3415 import tools, setup_logger
5
+ from db_hj3415 import myredis
6
+
7
+ from analyser_hj3415.analyser.eval.common import Tools
8
+
9
+
10
+ mylogger = setup_logger(__name__,'WARNING')
11
+ expire_time = tools.to_int(os.getenv('DEFAULT_EXPIRE_TIME_H', 48)) * 3600
12
+
13
+
14
+ @dataclass()
15
+ class GrowthData:
16
+ code: str
17
+ name: str
18
+
19
+ 매출액증가율_r: float
20
+ 매출액증가율_dict: dict
21
+
22
+ 영업이익률_c106: dict
23
+
24
+ score: list
25
+ date: list
26
+
27
+
28
+ class Growth:
29
+ def __init__(self, code: str):
30
+ assert tools.is_6digit(code), f'Invalid value : {code}'
31
+ mylogger.debug(f"Growth : 종목코드 ({code})")
32
+
33
+ self.c101 = myredis.C101(code)
34
+ self.c104 = myredis.C104(code, 'c104q')
35
+ self.c106 = myredis.C106(code, 'c106q')
36
+
37
+ self.name = self.c101.get_name()
38
+ self._code = code
39
+
40
+ def __str__(self):
41
+ return f"Growth({self.code}/{self.name})"
42
+
43
+ @property
44
+ def code(self) -> str:
45
+ return self._code
46
+
47
+ @code.setter
48
+ def code(self, code: str):
49
+ assert tools.is_6digit(code), f'Invalid value : {code}'
50
+ mylogger.debug(f"Growth : 종목코드 변경({self.code} -> {code})")
51
+
52
+ self.c101.code = code
53
+ self.c104.code = code
54
+ self.c106.code = code
55
+
56
+ self.name = self.c101.get_name()
57
+ self._code = code
58
+
59
+ def _score(self) -> list:
60
+ return [0,]
61
+
62
+ def _generate_data(self, refresh=False) -> GrowthData:
63
+ self.c104.page = 'c104y'
64
+ _, 매출액증가율_dict = self.c104.find('매출액증가율', remove_yoy=True, refresh=refresh)
65
+
66
+ self.c104.page = 'c104q'
67
+ d2, 매출액증가율_r = self.c104.latest_value_pop2('매출액증가율')
68
+
69
+ mylogger.info(f'매출액증가율 : {매출액증가율_r} {매출액증가율_dict}')
70
+
71
+ # c106 에서 타 기업과 영업이익률 비교
72
+ self.c106.page = 'c106y'
73
+ 영업이익률_c106 = self.c106.find('영업이익률', refresh)
74
+
75
+ score = self._score()
76
+
77
+ try:
78
+ date_list = Tools.date_set(d2)
79
+ except ValueError:
80
+ # 날짜 데이터가 없는경우
81
+ date_list = ['', ]
82
+
83
+ return GrowthData(
84
+ code= self.code,
85
+ name= self.name,
86
+
87
+ 매출액증가율_r= 매출액증가율_r,
88
+ 매출액증가율_dict= 매출액증가율_dict,
89
+
90
+ 영업이익률_c106= 영업이익률_c106,
91
+
92
+ score= score,
93
+ date= date_list,
94
+ )
95
+
96
+ def get(self, refresh = False, verbose = True) -> GrowthData:
97
+ """
98
+ GrowthData 형식의 데이터를 계산하여 리턴하고 레디스 캐시에 저장한다.
99
+ :param refresh:
100
+ :return:
101
+ """
102
+ redis_name = f"{self.code}_growth"
103
+ mylogger.info(f"{self} GrowthData를 레디스캐시에서 가져오거나 새로 생성합니다.. refresh : {refresh}")
104
+ if verbose:
105
+ print(f"{self} redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time/3600}h")
106
+
107
+ def fetch_generate_data(refresh_in: bool) -> dict:
108
+ return asdict(self._generate_data(refresh_in))
109
+
110
+ return GrowthData(**myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_generate_data, refresh, timer=expire_time))