analyser_hj3415 2.4.0__py2.py3-none-any.whl → 2.5.1__py2.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.
@@ -1,364 +1,364 @@
1
- import math
2
- import numpy
3
- import pprint
4
- import copy
5
- from typing import Tuple
6
-
7
- from db_hj3415 import myredis, mymongo
8
- from analyser_hj3415.analysers import eval
9
- from utils_hj3415 import utils
10
-
11
- import logging
12
-
13
- logger = logging.getLogger(__name__)
14
- formatter = logging.Formatter('%(levelname)s: [%(name)s] %(message)s')
15
- ch = logging.StreamHandler()
16
- ch.setFormatter(formatter)
17
- logger.addHandler(ch)
18
- logger.setLevel(logging.WARNING)
19
-
20
-
21
- def cal_deviation(v1: float, v2: float) -> float:
22
- """
23
- 괴리율 구하는 공식
24
- :param v1:
25
- :param v2:
26
- :return:
27
- """
28
- try:
29
- deviation = abs((v1 - v2) / v1) * 100
30
- except ZeroDivisionError:
31
- deviation = math.nan
32
- return deviation
33
-
34
-
35
- def red(code: str) -> int:
36
- """red price와 최근 주가의 괴리율 파악
37
-
38
- Returns:
39
- int : 주가와 red price 비교한 괴리율
40
- """
41
- try:
42
- recent_price = utils.to_int(myredis.C101(code).get_recent()['주가'])
43
- except KeyError:
44
- recent_price = float('nan')
45
- return 0
46
-
47
- red_price = eval.red(code)['red_price']
48
- deviation = cal_deviation(recent_price, red_price)
49
- if red_price < 0 or (recent_price >= red_price):
50
- score = 0
51
- else:
52
- score = math.log10(deviation + 1) * 33 # desmos그래프상 33이 제일 적당한듯(최대100점에 가깝게)
53
-
54
- #print(f"최근주가 : {recent_price}", f"red가격 : {red_price}", f"괴리율 : {utils.to_int(deviation)}", f"score : {utils.to_int(score)}")
55
-
56
- return utils.to_int(score)
57
-
58
-
59
- def mil(code: str) -> Tuple[int, int, int, int]:
60
- """
61
- - 재무활동현금흐름이 마이너스라는 것은 배당급 지급했거나, 자사주 매입했거나, 부채를 상환한 상태임.
62
- - 반대는 채권자로 자금을 조달했거나 신주를 발행했다는 의미
63
- <주주수익률> - 재무활동현금흐름/시가총액 => 5%이상인가?
64
-
65
- 투하자본수익률(ROIC)가 30%이상인가
66
- ROE(자기자본이익률) 20%이상이면 아주 우수 다른 투자이익률과 비교해볼것 10%미만이면 별로...단, 부채비율을 확인해야함.
67
-
68
- 이익지표 ...영업현금흐름이 순이익보다 많은가 - 결과값이 음수인가..
69
-
70
- FCF는 영업현금흐름에서 자본적 지출(유·무형투자 비용)을 차감한 순수한 현금력이라 할 수 있다.
71
- 말 그대로 자유롭게(Free) 사용할 수 있는 여윳돈을 뜻한다.
72
- 잉여현금흐름이 플러스라면 미래의 투자나 채무상환에 쓸 재원이 늘어난 것이다.
73
- CAPEX(Capital expenditures)는 미래의 이윤을 창출하기 위해 지출된 비용을 말한다.
74
- 이는 기업이 고정자산을 구매하거나, 유효수명이 당회계년도를 초과하는 기존의 고정자산에 대한 투자에 돈이 사용될 때 발생한다.
75
-
76
- 잉여현금흐름이 마이너스일때는 설비투자가 많은 시기라 주가가 약세이며 이후 설비투자 마무리되면서 주가가 상승할수 있다.
77
- 주가는 잉여현금흐름이 증가할때 상승하는 경향이 있다.
78
- fcf = 영업현금흐름 - capex
79
-
80
- 가치지표평가
81
- price to fcf 계산
82
- https://www.investopedia.com/terms/p/pricetofreecashflow.asp
83
- pcr보다 정확하게 주식의 가치를 평가할수 있음. 10배이하 추천
84
-
85
- Returns:
86
- tuple: 주주수익률, 이익지표, 투자수익률, PFCF포인트
87
- """
88
- mil_dict = eval.mil(code)
89
-
90
- # print(pprint.pformat(mil_dict, width=200))
91
-
92
- # 주주수익률 평가
93
- if math.isnan(mil_dict['주주수익률']):
94
- score1 = 0
95
- else:
96
- 주주수익률평가 = math.ceil(mil_dict['주주수익률'] - (eval.EXPECT_EARN * 100))
97
- score1 = 0 if 0 > 주주수익률평가 else 주주수익률평가
98
-
99
- # 이익지표 평가
100
- score2 = 10 if mil_dict['이익지표'] < 0 else 0
101
-
102
- # 투자수익률 평가
103
- MAX3 = 20
104
- score3 = 0
105
- roic = mil_dict['투자수익률']['ROIC']
106
- roe = mil_dict['투자수익률']['ROE']
107
- if math.isnan(roic) or roic <= 0:
108
- # roic 가 비정상이라 평가할 수 없는 경우
109
- if 10 < roe <= 20:
110
- score3 += round(MAX3 * 0.333)
111
- elif 20 < roe:
112
- score3 += round(MAX3 * 0.666)
113
- elif 0 < roic:
114
- # roic 로 평가할 수 있는 경우
115
- if 0 < roic <= 15:
116
- score3 += round(MAX3 * 0.333)
117
- elif 15 < roic <= 30:
118
- score3 += round(MAX3 * 0.666)
119
- elif 30 < roic:
120
- score3 += MAX3
121
-
122
- # PFCF 평가
123
- pfcf_dict = mil_dict['가치지표']['PFCF']
124
- _, pfcf = mymongo.C1034.latest_dict_value(pfcf_dict)
125
-
126
- logger.debug(f'recent pfcf {_}, {pfcf}')
127
- try:
128
- p = round(-40 * math.log10(pfcf) + 40)
129
- except ValueError:
130
- p = 0
131
- score4 = 0 if 0 > p else p
132
-
133
- return score1, score2, score3, score4
134
-
135
-
136
- def blue(code: str) -> Tuple[int, int, int, int, int]:
137
- """회사의 안정성을 보는 지표들
138
-
139
- 0을 기준으로 상태가 좋치 않을 수록 마이너스 값을 가진다.
140
-
141
- Returns:
142
- tuple : 유동비율, 이자보상배율, 순부채비율, 순운전자본회전율, 재고자산회전율 평가 포인트
143
-
144
- Notes:
145
- <유동비율>
146
- 100미만이면 주의하나 현금흐름창출력이 좋으면 괜찮을수 있다.
147
- 만약 100%이하면 유동자산에 추정영업현금흐름을 더해서 다시계산해보아 기회를 준다.
148
- <이자보상배율>
149
- 이자보상배율 영업이익/이자비용으로 1이면 자금사정빡빡 5이상이면 양호
150
- <순운전자금회전율>
151
- 순운전자금 => 기업활동을 하기 위해 필요한 자금 (매출채권 + 재고자산 - 매입채무)
152
- 순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
153
- <재고자산회전율>
154
- 재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
155
- 재고자산회전율이 작아지면 재고가 쌓인다는뜻
156
- <순부채비율>
157
- 부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
158
- 순부채 비율이 30%이상이면 좋치 않다.
159
- """
160
- def _calc_point_with_std(data: dict) -> int:
161
- """표준편차를 통해 점수를 계산하는 내부 함수
162
-
163
- Args:
164
- data(dict): 재무재표상의 연/분기 딕셔너리 데이터
165
- """
166
- NEG_MAX = -5
167
- d_values = [i for i in data.values() if not math.isnan(i)]
168
- logger.debug(f'd_values : {d_values}')
169
- if len(d_values) == 0:
170
- p = NEG_MAX
171
- else:
172
- std = numpy.std(d_values)
173
- # 표준편차가 작을수록 데이터의 변환가 적다는 의미임.
174
- logger.debug(f'표준편차 : {std}')
175
- p = NEG_MAX if float(std) > -NEG_MAX else -math.floor(float(std))
176
-
177
- return int(p)
178
-
179
- c104y = myredis.C104(code, 'c104y')
180
-
181
- blue_dict = eval.blue(code)
182
-
183
- # print(pprint.pformat(blue_dict, width=200))
184
-
185
- def 유동비율평가(유동비율: float) -> int:
186
- # 채점은 0을 기준으로 마이너스 해간다. 즉 0이 제일 좋은 상태임.
187
- # 유동비율 평가 - 100 이하는 문제 있음
188
- NEG_MAX = -10
189
- if math.isnan(유동비율) or 유동비율 <= 0:
190
- p = NEG_MAX
191
- elif math.isinf(유동비율):
192
- p = 0
193
- else:
194
- p = 0 if 100 < round(유동비율) else NEG_MAX + round(유동비율/10)
195
- logger.debug(f'유동비율평가 point : {p}')
196
- return int(p)
197
-
198
- p1 = 유동비율평가(blue_dict['유동비율'])
199
-
200
- def 이자보상배율평가(이자보상배율: tuple) -> int:
201
- # 이자보상배율평가 : 1이면 자금사정 빡빡 5이상이면 양호
202
- NEG_MAX = -5
203
- 최근이자보상배율q, dict_y = 이자보상배율
204
-
205
- if math.isnan(최근이자보상배율q) or 최근이자보상배율q <= 1:
206
- # 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
207
-
208
- _, 최근이자보상배율y = mymongo.C1034.latest_dict_value(dict_y)
209
- c104y.page = 'c104y'
210
- 전년대비 = c104y.find_yoy(title='이자보상배율')
211
-
212
- if math.isnan(최근이자보상배율y) or 최근이자보상배율y <= 1 or math.isnan(전년대비) or 전년대비 < 0:
213
- p = NEG_MAX
214
- else:
215
- p = 0 if 5 < 최근이자보상배율y else NEG_MAX + round(최근이자보상배율y)
216
- else:
217
- p = 0 if 5 < 최근이자보상배율q else NEG_MAX + round(최근이자보상배율q)
218
- logger.debug(f'이자보상배율평가 point : {p}')
219
- return int(p)
220
-
221
- p2 = 이자보상배율평가(blue_dict['이자보상배율'])
222
-
223
- def 순부채비율평가(순부채비율: tuple) -> int:
224
- # 부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
225
- # 순부채 비율이 30%이상이면 좋치 않다.
226
- NEG_MAX = -5
227
- 최근순부채비율q, dict_y = 순부채비율
228
-
229
- if math.isnan(최근순부채비율q) or 최근순부채비율q >= 80:
230
- # 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
231
- _, 최근순부채비율y = mymongo.C1034.latest_dict_value(dict_y)
232
- c104y.page = 'c104y'
233
- 전년대비 = c104y.find_yoy(title='순부채비율')
234
- if math.isnan(최근순부채비율y) or 최근순부채비율y >= 80 or math.isnan(전년대비) or 전년대비 > 0:
235
- p = NEG_MAX
236
- else:
237
- p = 0 if 최근순부채비율y < 30 else round((30 - 최근순부채비율y) / 10)
238
- else:
239
- p = 0 if 최근순부채비율q < 30 else round((30 - 최근순부채비율q) / 10)
240
- logger.debug(f'순부채비율평가 point : {p}')
241
- return int(p)
242
-
243
- p3 = 순부채비율평가(blue_dict['순부채비율'])
244
-
245
- def 순운전자본회전율평가(순운전자본회전율: tuple) -> int:
246
- # 순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
247
- _, dict_y = 순운전자본회전율
248
- p = _calc_point_with_std(data=dict_y)
249
- logger.debug(f'순운전자본회전율평가 point : {p}')
250
- return p
251
-
252
- p4 = 순운전자본회전율평가(blue_dict['순운전자본회전율'])
253
-
254
- def 재고자산회전율평가(재고자산회전율: tuple) -> int:
255
- # 재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
256
- # 재고자산회전율이 작아지면 재고가 쌓인다는뜻
257
- _, dict_y = 재고자산회전율
258
- p = _calc_point_with_std(data=dict_y)
259
- logger.debug(f'재고자산회전율평가 point : {p}')
260
- return p
261
-
262
- p5 = 재고자산회전율평가(blue_dict['재고자산회전율'])
263
-
264
- return p1, p2, p3, p4, p5
265
-
266
-
267
- def growth(code: str) -> Tuple[int, int]:
268
- """회사의 성장성을 보는 지표들
269
-
270
- <매출액>
271
- 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
272
- <영업이익률>
273
- 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
274
-
275
- Returns:
276
- tuple : 매출액증가율, 영업이익률 평가 포인트
277
- """
278
- growth_dict = eval.growth(code)
279
-
280
- logger.debug(pprint.pformat(growth_dict, width=200))
281
-
282
- def 매출액증가율평가(매출액증가율: tuple) -> int:
283
- # 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
284
- MAX = 20
285
- 최근매출액증가율q, dict_y = 매출액증가율
286
- _, 최근매출액증가율y = mymongo.C1034.latest_dict_value(dict_y)
287
-
288
- # 최근 자료가 성장하는 중인지 판단
289
- if math.isnan(최근매출액증가율q):
290
- 최근매출액증가율q = 최근매출액증가율y
291
-
292
- sp1 = 0
293
- if math.isnan(최근매출액증가율y):
294
- pass
295
- elif 0 < 최근매출액증가율y and 0 < 최근매출액증가율q:
296
- # 최근에 마이너스 성장이 아닌경우 MAX/10점 보너스
297
- sp1 += MAX / 10
298
- if 최근매출액증가율y < 최근매출액증가율q:
299
- # 최근에 이전보다 더 성장중이면 MAX/10점 보너스
300
- sp1 += MAX / 10
301
- # 나머지는 성장률 기반 점수 배정
302
- sp1 += MAX / 2 if 최근매출액증가율q > MAX else 최근매출액증가율q / 2
303
- elif 최근매출액증가율y <= 0 < 최근매출액증가율q:
304
- # 직전에 마이너스였다가 최근에 회복된 경우 MAX/10점 보너스
305
- sp1 += MAX / 10
306
- # 나머지는 성장률 기반 점수 배정
307
- sp1 += MAX / 2 if 최근매출액증가율q > MAX else 최근매출액증가율q / 2
308
- else:
309
- # 최근 자료가 마이너스인 경우 마이너스만큼 점수를 차감한다.
310
- sp1 += -(MAX / 2) if 최근매출액증가율q < -MAX else 최근매출액증가율q / 2
311
-
312
- # 평균매출액증가율 구하기
313
- d_values = [i for i in dict_y.values() if not math.isnan(i)]
314
- logger.debug(f'평균매출액증가율 d_values : {d_values}')
315
-
316
- if len(d_values) == 0:
317
- 평균매출액증가율 = float('nan')
318
- else:
319
- 평균매출액증가율 = float(numpy.mean(d_values))
320
- logger.debug(f'평균 : {평균매출액증가율}')
321
-
322
- sp2 = 0
323
- if math.isnan(평균매출액증가율):
324
- sp2 += -(MAX/2)
325
- elif 평균매출액증가율 <= 0:
326
- # 평균매출액증가율이 마이너스인 경우 마이너스만큼 점수를 차감한다.
327
- sp2 += -(MAX / 2) if 평균매출액증가율 < -MAX else 평균매출액증가율 / 2
328
- else:
329
- sp2 = MAX / 2 if 평균매출액증가율 > MAX else 평균매출액증가율 / 2
330
-
331
- logger.debug(f'매출액증가율평가 point : {sp1 + sp2}')
332
-
333
- return int(sp1 + sp2)
334
-
335
- p1 = 매출액증가율평가(growth_dict['매출액증가율'])
336
-
337
- def 영업이익률평가(영업이익률: dict) -> int:
338
- # 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
339
- 영업이익률 = copy.deepcopy(영업이익률)
340
- name = myredis.Corps.get_name(code)
341
-
342
- p = 0
343
- try:
344
- myprofit = utils.to_float(영업이익률.pop(name))
345
- except KeyError:
346
- logger.warning(f'{name} 영업이익률 does not exist.')
347
- return 0
348
- logger.debug(f'종목영업이익률 : {myprofit}')
349
-
350
- for profit in 영업이익률.values():
351
- profit = utils.to_float(profit)
352
- if math.isnan(profit):
353
- continue
354
- elif myprofit > profit:
355
- p += 1
356
- else:
357
- continue
358
-
359
- logger.debug(f'영업이익률평가 point : {p}')
360
- return p
361
-
362
- p2 = 영업이익률평가(growth_dict['영업이익률'])
363
-
364
- return p1, p2
1
+ import math
2
+ import numpy
3
+ import pprint
4
+ import copy
5
+ from typing import Tuple
6
+
7
+ from db_hj3415 import myredis, mymongo
8
+ from analyser_hj3415.analysers import eval
9
+ from utils_hj3415 import utils
10
+
11
+ import logging
12
+
13
+ logger = logging.getLogger(__name__)
14
+ formatter = logging.Formatter('%(levelname)s: [%(name)s] %(message)s')
15
+ ch = logging.StreamHandler()
16
+ ch.setFormatter(formatter)
17
+ logger.addHandler(ch)
18
+ logger.setLevel(logging.WARNING)
19
+
20
+
21
+ def cal_deviation(v1: float, v2: float) -> float:
22
+ """
23
+ 괴리율 구하는 공식
24
+ :param v1:
25
+ :param v2:
26
+ :return:
27
+ """
28
+ try:
29
+ deviation = abs((v1 - v2) / v1) * 100
30
+ except ZeroDivisionError:
31
+ deviation = math.nan
32
+ return deviation
33
+
34
+
35
+ def red(code: str) -> int:
36
+ """red price와 최근 주가의 괴리율 파악
37
+
38
+ Returns:
39
+ int : 주가와 red price 비교한 괴리율
40
+ """
41
+ try:
42
+ recent_price = utils.to_int(myredis.C101(code).get_recent()['주가'])
43
+ except KeyError:
44
+ recent_price = float('nan')
45
+ return 0
46
+
47
+ red_price = eval.red(code)['red_price']
48
+ deviation = cal_deviation(recent_price, red_price)
49
+ if red_price < 0 or (recent_price >= red_price):
50
+ score = 0
51
+ else:
52
+ score = math.log10(deviation + 1) * 33 # desmos그래프상 33이 제일 적당한듯(최대100점에 가깝게)
53
+
54
+ #print(f"최근주가 : {recent_price}", f"red가격 : {red_price}", f"괴리율 : {utils.to_int(deviation)}", f"score : {utils.to_int(score)}")
55
+
56
+ return utils.to_int(score)
57
+
58
+
59
+ def mil(code: str) -> Tuple[int, int, int, int]:
60
+ """
61
+ - 재무활동현금흐름이 마이너스라는 것은 배당급 지급했거나, 자사주 매입했거나, 부채를 상환한 상태임.
62
+ - 반대는 채권자로 자금을 조달했거나 신주를 발행했다는 의미
63
+ <주주수익률> - 재무활동현금흐름/시가총액 => 5%이상인가?
64
+
65
+ 투하자본수익률(ROIC)가 30%이상인가
66
+ ROE(자기자본이익률) 20%이상이면 아주 우수 다른 투자이익률과 비교해볼것 10%미만이면 별로...단, 부채비율을 확인해야함.
67
+
68
+ 이익지표 ...영업현금흐름이 순이익보다 많은가 - 결과값이 음수인가..
69
+
70
+ FCF는 영업현금흐름에서 자본적 지출(유·무형투자 비용)을 차감한 순수한 현금력이라 할 수 있다.
71
+ 말 그대로 자유롭게(Free) 사용할 수 있는 여윳돈을 뜻한다.
72
+ 잉여현금흐름이 플러스라면 미래의 투자나 채무상환에 쓸 재원이 늘어난 것이다.
73
+ CAPEX(Capital expenditures)는 미래의 이윤을 창출하기 위해 지출된 비용을 말한다.
74
+ 이는 기업이 고정자산을 구매하거나, 유효수명이 당회계년도를 초과하는 기존의 고정자산에 대한 투자에 돈이 사용될 때 발생한다.
75
+
76
+ 잉여현금흐름이 마이너스일때는 설비투자가 많은 시기라 주가가 약세이며 이후 설비투자 마무리되면서 주가가 상승할수 있다.
77
+ 주가는 잉여현금흐름이 증가할때 상승하는 경향이 있다.
78
+ fcf = 영업현금흐름 - capex
79
+
80
+ 가치지표평가
81
+ price to fcf 계산
82
+ https://www.investopedia.com/terms/p/pricetofreecashflow.asp
83
+ pcr보다 정확하게 주식의 가치를 평가할수 있음. 10배이하 추천
84
+
85
+ Returns:
86
+ tuple: 주주수익률, 이익지표, 투자수익률, PFCF포인트
87
+ """
88
+ mil_dict = eval.mil(code)
89
+
90
+ # print(pprint.pformat(mil_dict, width=200))
91
+
92
+ # 주주수익률 평가
93
+ if math.isnan(mil_dict['주주수익률']):
94
+ score1 = 0
95
+ else:
96
+ 주주수익률평가 = math.ceil(mil_dict['주주수익률'] - (eval.EXPECT_EARN * 100))
97
+ score1 = 0 if 0 > 주주수익률평가 else 주주수익률평가
98
+
99
+ # 이익지표 평가
100
+ score2 = 10 if mil_dict['이익지표'] < 0 else 0
101
+
102
+ # 투자수익률 평가
103
+ MAX3 = 20
104
+ score3 = 0
105
+ roic = mil_dict['투자수익률']['ROIC']
106
+ roe = mil_dict['투자수익률']['ROE']
107
+ if math.isnan(roic) or roic <= 0:
108
+ # roic 가 비정상이라 평가할 수 없는 경우
109
+ if 10 < roe <= 20:
110
+ score3 += round(MAX3 * 0.333)
111
+ elif 20 < roe:
112
+ score3 += round(MAX3 * 0.666)
113
+ elif 0 < roic:
114
+ # roic 로 평가할 수 있는 경우
115
+ if 0 < roic <= 15:
116
+ score3 += round(MAX3 * 0.333)
117
+ elif 15 < roic <= 30:
118
+ score3 += round(MAX3 * 0.666)
119
+ elif 30 < roic:
120
+ score3 += MAX3
121
+
122
+ # PFCF 평가
123
+ pfcf_dict = mil_dict['가치지표']['PFCF']
124
+ _, pfcf = mymongo.C1034.latest_dict_value(pfcf_dict)
125
+
126
+ logger.debug(f'recent pfcf {_}, {pfcf}')
127
+ try:
128
+ p = round(-40 * math.log10(pfcf) + 40)
129
+ except ValueError:
130
+ p = 0
131
+ score4 = 0 if 0 > p else p
132
+
133
+ return score1, score2, score3, score4
134
+
135
+
136
+ def blue(code: str) -> Tuple[int, int, int, int, int]:
137
+ """회사의 안정성을 보는 지표들
138
+
139
+ 0을 기준으로 상태가 좋치 않을 수록 마이너스 값을 가진다.
140
+
141
+ Returns:
142
+ tuple : 유동비율, 이자보상배율, 순부채비율, 순운전자본회전율, 재고자산회전율 평가 포인트
143
+
144
+ Notes:
145
+ <유동비율>
146
+ 100미만이면 주의하나 현금흐름창출력이 좋으면 괜찮을수 있다.
147
+ 만약 100%이하면 유동자산에 추정영업현금흐름을 더해서 다시계산해보아 기회를 준다.
148
+ <이자보상배율>
149
+ 이자보상배율 영업이익/이자비용으로 1이면 자금사정빡빡 5이상이면 양호
150
+ <순운전자금회전율>
151
+ 순운전자금 => 기업활동을 하기 위해 필요한 자금 (매출채권 + 재고자산 - 매입채무)
152
+ 순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
153
+ <재고자산회전율>
154
+ 재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
155
+ 재고자산회전율이 작아지면 재고가 쌓인다는뜻
156
+ <순부채비율>
157
+ 부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
158
+ 순부채 비율이 30%이상이면 좋치 않다.
159
+ """
160
+ def _calc_point_with_std(data: dict) -> int:
161
+ """표준편차를 통해 점수를 계산하는 내부 함수
162
+
163
+ Args:
164
+ data(dict): 재무재표상의 연/분기 딕셔너리 데이터
165
+ """
166
+ NEG_MAX = -5
167
+ d_values = [i for i in data.values() if not math.isnan(i)]
168
+ logger.debug(f'd_values : {d_values}')
169
+ if len(d_values) == 0:
170
+ p = NEG_MAX
171
+ else:
172
+ std = numpy.std(d_values)
173
+ # 표준편차가 작을수록 데이터의 변환가 적다는 의미임.
174
+ logger.debug(f'표준편차 : {std}')
175
+ p = NEG_MAX if float(std) > -NEG_MAX else -math.floor(float(std))
176
+
177
+ return int(p)
178
+
179
+ c104y = myredis.C104(code, 'c104y')
180
+
181
+ blue_dict = eval.blue(code)
182
+
183
+ # print(pprint.pformat(blue_dict, width=200))
184
+
185
+ def 유동비율평가(유동비율: float) -> int:
186
+ # 채점은 0을 기준으로 마이너스 해간다. 즉 0이 제일 좋은 상태임.
187
+ # 유동비율 평가 - 100 이하는 문제 있음
188
+ NEG_MAX = -10
189
+ if math.isnan(유동비율) or 유동비율 <= 0:
190
+ p = NEG_MAX
191
+ elif math.isinf(유동비율):
192
+ p = 0
193
+ else:
194
+ p = 0 if 100 < round(유동비율) else NEG_MAX + round(유동비율/10)
195
+ logger.debug(f'유동비율평가 point : {p}')
196
+ return int(p)
197
+
198
+ p1 = 유동비율평가(blue_dict['유동비율'])
199
+
200
+ def 이자보상배율평가(이자보상배율: tuple) -> int:
201
+ # 이자보상배율평가 : 1이면 자금사정 빡빡 5이상이면 양호
202
+ NEG_MAX = -5
203
+ 최근이자보상배율q, dict_y = 이자보상배율
204
+
205
+ if math.isnan(최근이자보상배율q) or 최근이자보상배율q <= 1:
206
+ # 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
207
+
208
+ _, 최근이자보상배율y = mymongo.C1034.latest_dict_value(dict_y)
209
+ c104y.page = 'c104y'
210
+ 전년대비 = c104y.find_yoy(title='이자보상배율')
211
+
212
+ if math.isnan(최근이자보상배율y) or 최근이자보상배율y <= 1 or math.isnan(전년대비) or 전년대비 < 0:
213
+ p = NEG_MAX
214
+ else:
215
+ p = 0 if 5 < 최근이자보상배율y else NEG_MAX + round(최근이자보상배율y)
216
+ else:
217
+ p = 0 if 5 < 최근이자보상배율q else NEG_MAX + round(최근이자보상배율q)
218
+ logger.debug(f'이자보상배율평가 point : {p}')
219
+ return int(p)
220
+
221
+ p2 = 이자보상배율평가(blue_dict['이자보상배율'])
222
+
223
+ def 순부채비율평가(순부채비율: tuple) -> int:
224
+ # 부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
225
+ # 순부채 비율이 30%이상이면 좋치 않다.
226
+ NEG_MAX = -5
227
+ 최근순부채비율q, dict_y = 순부채비율
228
+
229
+ if math.isnan(최근순부채비율q) or 최근순부채비율q >= 80:
230
+ # 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
231
+ _, 최근순부채비율y = mymongo.C1034.latest_dict_value(dict_y)
232
+ c104y.page = 'c104y'
233
+ 전년대비 = c104y.find_yoy(title='순부채비율')
234
+ if math.isnan(최근순부채비율y) or 최근순부채비율y >= 80 or math.isnan(전년대비) or 전년대비 > 0:
235
+ p = NEG_MAX
236
+ else:
237
+ p = 0 if 최근순부채비율y < 30 else round((30 - 최근순부채비율y) / 10)
238
+ else:
239
+ p = 0 if 최근순부채비율q < 30 else round((30 - 최근순부채비율q) / 10)
240
+ logger.debug(f'순부채비율평가 point : {p}')
241
+ return int(p)
242
+
243
+ p3 = 순부채비율평가(blue_dict['순부채비율'])
244
+
245
+ def 순운전자본회전율평가(순운전자본회전율: tuple) -> int:
246
+ # 순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
247
+ _, dict_y = 순운전자본회전율
248
+ p = _calc_point_with_std(data=dict_y)
249
+ logger.debug(f'순운전자본회전율평가 point : {p}')
250
+ return p
251
+
252
+ p4 = 순운전자본회전율평가(blue_dict['순운전자본회전율'])
253
+
254
+ def 재고자산회전율평가(재고자산회전율: tuple) -> int:
255
+ # 재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
256
+ # 재고자산회전율이 작아지면 재고가 쌓인다는뜻
257
+ _, dict_y = 재고자산회전율
258
+ p = _calc_point_with_std(data=dict_y)
259
+ logger.debug(f'재고자산회전율평가 point : {p}')
260
+ return p
261
+
262
+ p5 = 재고자산회전율평가(blue_dict['재고자산회전율'])
263
+
264
+ return p1, p2, p3, p4, p5
265
+
266
+
267
+ def growth(code: str) -> Tuple[int, int]:
268
+ """회사의 성장성을 보는 지표들
269
+
270
+ <매출액>
271
+ 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
272
+ <영업이익률>
273
+ 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
274
+
275
+ Returns:
276
+ tuple : 매출액증가율, 영업이익률 평가 포인트
277
+ """
278
+ growth_dict = eval.growth(code)
279
+
280
+ logger.debug(pprint.pformat(growth_dict, width=200))
281
+
282
+ def 매출액증가율평가(매출액증가율: tuple) -> int:
283
+ # 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
284
+ MAX = 20
285
+ 최근매출액증가율q, dict_y = 매출액증가율
286
+ _, 최근매출액증가율y = mymongo.C1034.latest_dict_value(dict_y)
287
+
288
+ # 최근 자료가 성장하는 중인지 판단
289
+ if math.isnan(최근매출액증가율q):
290
+ 최근매출액증가율q = 최근매출액증가율y
291
+
292
+ sp1 = 0
293
+ if math.isnan(최근매출액증가율y):
294
+ pass
295
+ elif 0 < 최근매출액증가율y and 0 < 최근매출액증가율q:
296
+ # 최근에 마이너스 성장이 아닌경우 MAX/10점 보너스
297
+ sp1 += MAX / 10
298
+ if 최근매출액증가율y < 최근매출액증가율q:
299
+ # 최근에 이전보다 더 성장중이면 MAX/10점 보너스
300
+ sp1 += MAX / 10
301
+ # 나머지는 성장률 기반 점수 배정
302
+ sp1 += MAX / 2 if 최근매출액증가율q > MAX else 최근매출액증가율q / 2
303
+ elif 최근매출액증가율y <= 0 < 최근매출액증가율q:
304
+ # 직전에 마이너스였다가 최근에 회복된 경우 MAX/10점 보너스
305
+ sp1 += MAX / 10
306
+ # 나머지는 성장률 기반 점수 배정
307
+ sp1 += MAX / 2 if 최근매출액증가율q > MAX else 최근매출액증가율q / 2
308
+ else:
309
+ # 최근 자료가 마이너스인 경우 마이너스만큼 점수를 차감한다.
310
+ sp1 += -(MAX / 2) if 최근매출액증가율q < -MAX else 최근매출액증가율q / 2
311
+
312
+ # 평균매출액증가율 구하기
313
+ d_values = [i for i in dict_y.values() if not math.isnan(i)]
314
+ logger.debug(f'평균매출액증가율 d_values : {d_values}')
315
+
316
+ if len(d_values) == 0:
317
+ 평균매출액증가율 = float('nan')
318
+ else:
319
+ 평균매출액증가율 = float(numpy.mean(d_values))
320
+ logger.debug(f'평균 : {평균매출액증가율}')
321
+
322
+ sp2 = 0
323
+ if math.isnan(평균매출액증가율):
324
+ sp2 += -(MAX/2)
325
+ elif 평균매출액증가율 <= 0:
326
+ # 평균매출액증가율이 마이너스인 경우 마이너스만큼 점수를 차감한다.
327
+ sp2 += -(MAX / 2) if 평균매출액증가율 < -MAX else 평균매출액증가율 / 2
328
+ else:
329
+ sp2 = MAX / 2 if 평균매출액증가율 > MAX else 평균매출액증가율 / 2
330
+
331
+ logger.debug(f'매출액증가율평가 point : {sp1 + sp2}')
332
+
333
+ return int(sp1 + sp2)
334
+
335
+ p1 = 매출액증가율평가(growth_dict['매출액증가율'])
336
+
337
+ def 영업이익률평가(영업이익률: dict) -> int:
338
+ # 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
339
+ 영업이익률 = copy.deepcopy(영업이익률)
340
+ name = myredis.Corps.get_name(code)
341
+
342
+ p = 0
343
+ try:
344
+ myprofit = utils.to_float(영업이익률.pop(name))
345
+ except KeyError:
346
+ logger.warning(f'{name} 영업이익률 does not exist.')
347
+ return 0
348
+ logger.debug(f'종목영업이익률 : {myprofit}')
349
+
350
+ for profit in 영업이익률.values():
351
+ profit = utils.to_float(profit)
352
+ if math.isnan(profit):
353
+ continue
354
+ elif myprofit > profit:
355
+ p += 1
356
+ else:
357
+ continue
358
+
359
+ logger.debug(f'영업이익률평가 point : {p}')
360
+ return p
361
+
362
+ p2 = 영업이익률평가(growth_dict['영업이익률'])
363
+
364
+ return p1, p2