analyser_hj3415 2.5.0__py2.py3-none-any.whl → 2.5.1__py2.py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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