analyser_hj3415 3.4.2__py3-none-any.whl → 4.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,342 +0,0 @@
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 utils_hj3415 import utils
9
-
10
- import logging
11
-
12
- logger = logging.getLogger(__name__)
13
- formatter = logging.Formatter('%(levelname)s: [%(name)s] %(message)s')
14
- ch = logging.StreamHandler()
15
- ch.setFormatter(formatter)
16
- logger.addHandler(ch)
17
- logger.setLevel(logging.WARNING)
18
-
19
-
20
-
21
- def mil(code: str, expect_earn: float) -> Tuple[int, int, int, int]:
22
- """
23
- 이익 지표 일반적인 기준 설정
24
-
25
- 1. 비율이 10% 이상일 경우 주의:
26
- • 이 비율이 10%를 넘을 경우, 이는 기업의 순이익이 현금 흐름과 비교하여 너무 높아졌음을 나타냅니다.
27
- • 순이익이 과도하게 부풀려졌을 가능성이 있으며, 회계적인 항목(예: 미수금, 재고자산의 평가차익 등)이나 비현금성 이익이 이익에 크게 기여하고 있을 수 있습니다. 이런 경우, 현금 흐름이 제대로 발생하지 않고 있음에도 순이익만 높게 기록되는 상황이 발생할 수 있어 이익의 질이 낮다고 평가됩니다.
28
- • 특히, 비율이 지속적으로 높다면 재무 건전성에 문제가 있을 수 있습니다.
29
- 2. 비율이 5% 이상 ~ 10% 미만일 때:
30
- • 비율이 5% ~ 10% 사이라면, 기업의 이익이 여전히 현금 흐름과 괴리가 있지만, 업종에 따라 일정 부분은 허용될 수 있습니다.
31
- • 예를 들어, 기술 기업이나 고성장 산업에서는 초기 투자나 R&D 비용이 많기 때문에 순이익과 현금 흐름의 차이가 다소 발생할 수 있습니다. 이런 경우 비율이 5%를 넘더라도 성장성을 고려한 투자 관점에서 용인될 수 있습니다.
32
- • 다만, 일반적인 제조업 등 현금 창출이 중요한 업종에서는 5% 이상의 괴리도 주의 깊게 볼 필요가 있습니다.
33
- 3. 비율이 5% 미만일 때:
34
- • 비율이 5% 미만이라면, 기업의 순이익과 현금 흐름 간의 차이가 크지 않으므로, 이익의 질이 높고 재무 건전성이 양호하다고 평가할 수 있습니다.
35
- • 특히 음수일 경우, 영업활동에서 벌어들인 현금이 순이익을 초과한다는 것을 의미하므로, 이는 매우 긍정적인 신호입니다. 기업이 순이익보다도 더 많은 현금을 창출하고 있는 상태로, 이익이 실제 현금 흐름으로 뒷받침되고 있음을 나타냅니다.
36
-
37
- 참고할 수 있는 기준
38
- 1. 경고 신호: 10% 이상
39
- • 비율이 10% 이상이라면, 기업의 이익이 실제 현금 흐름과 크게 괴리되어 있음을 의미하며, 이익의 질이 낮고 재무 건전성이 떨어질 가능성이 큽니다. 특히, 이 비율이 지속적으로 높다면, 주의가 필요합니다.
40
- 2. 주의: 5% ~ 10%
41
- • 이 비율은 어느 정도 괴리가 있는 상태이며, 업종 특성에 따라 감안할 수 있지만, 일반적인 기업의 경우 주의를 요합니다.
42
- 3. 안정적: 5% 미만
43
- • 5% 미만은 비교적 안정적인 상태로 평가될 수 있습니다. 이익과 현금 흐름 간의 괴리가 크지 않기 때문에, 기업의 재무 상태와 이익의 질이 건전하다고 볼 수 있습니다.
44
- 4. 매우 긍정적: 음수
45
- • 비율이 음수일 경우, 현금 창출 능력이 매우 우수한 상태입니다. 현금 흐름이 순이익보다 더 크므로, 이는 매우 긍정적인 신호로 평가됩니다.
46
-
47
- 사례별 해석
48
-
49
- 1. 비율이 15%:
50
- • 회사는 순이익이 영업활동현금흐름에 비해 과도하게 높습니다. 이는 비현금성 이익이 많이 포함되어 있거나, 회계적인 처리로 인해 순이익이 부풀려졌을 가능성이 큽니다. 기업의 이익 질에 의문을 가져야 하며, 장기적으로 재무 구조에 문제가 생길 수 있습니다.
51
- 2. 비율이 7%:
52
- • 괴리가 존재하지만, 일부 산업에서는 용인될 수 있는 범위입니다. 다만, 현금 흐름이 안정적인지 추가적인 분석이 필요합니다.
53
- 3. 비율이 -3%:
54
- • 매우 긍정적인 상황입니다. 회사는 순이익보다 더 많은 현금을 벌어들이고 있어, 이익의 질이 매우 높으며, 주주들에게도 더 많은 이익을 돌려줄 가능성이 큽니다.
55
-
56
- 기타 고려 사항
57
-
58
- • 업종별 차이: 기술 기업, 스타트업 등은 고정자산 투자나 R&D 비용이 많아 현금 흐름과 이익이 괴리될 수 있습니다. 이 경우 이익의 질이 낮다고 단정할 수는 없으므로 산업 특성을 고려해야 합니다.
59
- • 기업의 성장 단계: 빠르게 성장하는 기업일수록 현금 흐름보다 순이익이 클 수 있지만, 시간이 지나면서 현금 창출 능력이 증가할 수 있습니다.
60
- • 비현금성 항목 분석: 감가상각, 미수금 등의 비현금성 항목이 이익에 얼마나 영향을 미치는지 분석하여 이익의 질을 파악하는 것이 중요합니다.
61
- ROIC 평가 기준
62
-
63
- 1. 10% 이상:
64
- • 일반적으로 ROIC가 10% 이상이면 매우 우수한 기업으로 평가됩니다.
65
- • 이는 기업이 투자된 자본으로 높은 수익을 창출하고 있음을 의미하며, 자본의 사용 효율성이 매우 높다는 것을 나타냅니다.
66
- 2. 7% ~ 10%:
67
- • ROIC가 7%에서 10% 사이라면, 기업은 자본을 비교적 효율적으로 사용하고 있는 상태입니다.
68
- • 자본비용(WACC)이 이보다 낮다면, 투자자는 기업이 가치 창출을 하고 있다고 판단할 수 있습니다.
69
- 3. 5% ~ 7%:
70
- • 이 범위에서는 보통 수준의 효율성을 보여줍니다. ROIC가 자본비용보다 낮지 않다면 투자자에게 긍정적일 수 있지만, 이 수익률이 경쟁사 대비 낮다면 경쟁력이 떨어질 수 있음을 시사할 수 있습니다.
71
- 4. 5% 미만:
72
- • ROIC가 5% 미만이라면, 기업은 자본을 비효율적으로 사용하고 있음을 의미할 수 있습니다.
73
- • 자본을 투입해 수익을 내지 못하거나, 투자된 자본으로부터 적절한 수익을 창출하지 못하고 있는 상태일 수 있습니다.
74
- :param code:
75
- :param expect_earn:
76
- :return:
77
- """
78
-
79
- mil_dict = eval.mil(code)
80
-
81
- # print(pprint.pformat(mil_dict, width=200))
82
-
83
- # 주주수익률 평가
84
- if math.isnan(mil_dict['주주수익률']):
85
- score1 = 0
86
- else:
87
- 주주수익률평가 = math.ceil(mil_dict['주주수익률'] - (expect_earn * 100))
88
- score1 = 0 if 0 > 주주수익률평가 else 주주수익률평가
89
-
90
- # 이익지표 평가
91
- score2 = 10 if mil_dict['이익지표'] < 0 else 0
92
-
93
- # 투자수익률 평가
94
- MAX3 = 20
95
- score3 = 0
96
- roic = mil_dict['투자수익률']['ROIC']
97
- roe = mil_dict['투자수익률']['ROE']
98
- if math.isnan(roic) or roic <= 0:
99
- # roic 가 비정상이라 평가할 수 없는 경우
100
- if 10 < roe <= 20:
101
- score3 += round(MAX3 * 0.333)
102
- elif 20 < roe:
103
- score3 += round(MAX3 * 0.666)
104
- elif 0 < roic:
105
- # roic 로 평가할 수 있는 경우
106
- if 0 < roic <= 15:
107
- score3 += round(MAX3 * 0.333)
108
- elif 15 < roic <= 30:
109
- score3 += round(MAX3 * 0.666)
110
- elif 30 < roic:
111
- score3 += MAX3
112
-
113
- # PFCF 평가
114
- pfcf_dict = mil_dict['가치지표']['PFCF']
115
- _, pfcf = mymongo.C1034.latest_dict_value(pfcf_dict)
116
-
117
- logger.debug(f'recent pfcf {_}, {pfcf}')
118
- try:
119
- p = round(-40 * math.log10(pfcf) + 40)
120
- except ValueError:
121
- p = 0
122
- score4 = 0 if 0 > p else p
123
-
124
- return score1, score2, score3, score4
125
-
126
-
127
- def blue(code: str) -> Tuple[int, int, int, int, int]:
128
- """회사의 안정성을 보는 지표들
129
-
130
- 0을 기준으로 상태가 좋치 않을 수록 마이너스 값을 가진다.
131
-
132
- Returns:
133
- tuple : 유동비율, 이자보상배율, 순부채비율, 순운전자본회전율, 재고자산회전율 평가 포인트
134
-
135
- Notes:
136
- """
137
- def _calc_point_with_std(data: dict) -> int:
138
- """표준편차를 통해 점수를 계산하는 내부 함수
139
-
140
- Args:
141
- data(dict): 재무재표상의 연/분기 딕셔너리 데이터
142
- """
143
- NEG_MAX = -5
144
- d_values = [i for i in data.values() if not math.isnan(i)]
145
- logger.debug(f'd_values : {d_values}')
146
- if len(d_values) == 0:
147
- p = NEG_MAX
148
- else:
149
- std = numpy.std(d_values)
150
- # 표준편차가 작을수록 데이터의 변환가 적다는 의미임.
151
- logger.debug(f'표준편차 : {std}')
152
- p = NEG_MAX if float(std) > -NEG_MAX else -math.floor(float(std))
153
-
154
- return int(p)
155
-
156
- c104y = myredis.C104(code, 'c104y')
157
-
158
- blue_dict = eval.blue(code)
159
-
160
- # print(pprint.pformat(blue_dict, width=200))
161
-
162
- def 유동비율평가(유동비율: float) -> int:
163
- # 채점은 0을 기준으로 마이너스 해간다. 즉 0이 제일 좋은 상태임.
164
- # 유동비율 평가 - 100 이하는 문제 있음
165
- NEG_MAX = -10
166
- if math.isnan(유동비율) or 유동비율 <= 0:
167
- p = NEG_MAX
168
- elif math.isinf(유동비율):
169
- p = 0
170
- else:
171
- p = 0 if 100 < round(유동비율) else NEG_MAX + round(유동비율/10)
172
- logger.debug(f'유동비율평가 point : {p}')
173
- return int(p)
174
-
175
- p1 = 유동비율평가(blue_dict['유동비율'])
176
-
177
- def 이자보상배율평가(이자보상배율: tuple) -> int:
178
- # 이자보상배율평가 : 1이면 자금사정 빡빡 5이상이면 양호
179
- NEG_MAX = -5
180
- 최근이자보상배율q, dict_y = 이자보상배율
181
-
182
- if math.isnan(최근이자보상배율q) or 최근이자보상배율q <= 1:
183
- # 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
184
-
185
- _, 최근이자보상배율y = mymongo.C1034.latest_dict_value(dict_y)
186
- c104y.page = 'c104y'
187
- 전년대비 = c104y.find_yoy(title='이자보상배율')
188
-
189
- if math.isnan(최근이자보상배율y) or 최근이자보상배율y <= 1 or math.isnan(전년대비) or 전년대비 < 0:
190
- p = NEG_MAX
191
- else:
192
- p = 0 if 5 < 최근이자보상배율y else NEG_MAX + round(최근이자보상배율y)
193
- else:
194
- p = 0 if 5 < 최근이자보상배율q else NEG_MAX + round(최근이자보상배율q)
195
- logger.debug(f'이자보상배율평가 point : {p}')
196
- return int(p)
197
-
198
- p2 = 이자보상배율평가(blue_dict['이자보상배율'])
199
-
200
- def 순부채비율평가(순부채비율: tuple) -> int:
201
- # 부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
202
- # 순부채 비율이 30%이상이면 좋치 않다.
203
- NEG_MAX = -5
204
- 최근순부채비율q, dict_y = 순부채비율
205
-
206
- if math.isnan(최근순부채비율q) or 최근순부채비율q >= 80:
207
- # 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
208
- _, 최근순부채비율y = mymongo.C1034.latest_dict_value(dict_y)
209
- c104y.page = 'c104y'
210
- 전년대비 = c104y.find_yoy(title='순부채비율')
211
- if math.isnan(최근순부채비율y) or 최근순부채비율y >= 80 or math.isnan(전년대비) or 전년대비 > 0:
212
- p = NEG_MAX
213
- else:
214
- p = 0 if 최근순부채비율y < 30 else round((30 - 최근순부채비율y) / 10)
215
- else:
216
- p = 0 if 최근순부채비율q < 30 else round((30 - 최근순부채비율q) / 10)
217
- logger.debug(f'순부채비율평가 point : {p}')
218
- return int(p)
219
-
220
- p3 = 순부채비율평가(blue_dict['순부채비율'])
221
-
222
- def 순운전자본회전율평가(순운전자본회전율: tuple) -> int:
223
- # 순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
224
- _, dict_y = 순운전자본회전율
225
- p = _calc_point_with_std(data=dict_y)
226
- logger.debug(f'순운전자본회전율평가 point : {p}')
227
- return p
228
-
229
- p4 = 순운전자본회전율평가(blue_dict['순운전자본회전율'])
230
-
231
- def 재고자산회전율평가(재고자산회전율: tuple) -> int:
232
- # 재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
233
- # 재고자산회전율이 작아지면 재고가 쌓인다는뜻
234
- _, dict_y = 재고자산회전율
235
- p = _calc_point_with_std(data=dict_y)
236
- # 라이벌기업과 비교점수 추가
237
- logger.debug(f'재고자산회전율평가 point : {p}')
238
- return p
239
-
240
- p5 = 재고자산회전율평가(blue_dict['재고자산회전율'])
241
-
242
- return p1, p2, p3, p4, p5
243
-
244
-
245
- def growth(code: str) -> Tuple[int, int]:
246
- """회사의 성장성을 보는 지표들
247
-
248
- <매출액>
249
- 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
250
- <영업이익률>
251
- 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
252
-
253
- Returns:
254
- tuple : 매출액증가율, 영업이익률 평가 포인트
255
- """
256
- growth_dict = eval.growth(code)
257
-
258
- logger.debug(pprint.pformat(growth_dict, width=200))
259
-
260
- def 매출액증가율평가(매출액증가율: tuple) -> int:
261
- # 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
262
- MAX = 20
263
- 최근매출액증가율q, dict_y = 매출액증가율
264
- _, 최근매출액증가율y = mymongo.C1034.latest_dict_value(dict_y)
265
-
266
- # 최근 자료가 성장하는 중인지 판단
267
- if math.isnan(최근매출액증가율q):
268
- 최근매출액증가율q = 최근매출액증가율y
269
-
270
- sp1 = 0
271
- if math.isnan(최근매출액증가율y):
272
- pass
273
- elif 0 < 최근매출액증가율y and 0 < 최근매출액증가율q:
274
- # 최근에 마이너스 성장이 아닌경우 MAX/10점 보너스
275
- sp1 += MAX / 10
276
- if 최근매출액증가율y < 최근매출액증가율q:
277
- # 최근에 이전보다 더 성장중이면 MAX/10점 보너스
278
- sp1 += MAX / 10
279
- # 나머지는 성장률 기반 점수 배정
280
- sp1 += MAX / 2 if 최근매출액증가율q > MAX else 최근매출액증가율q / 2
281
- elif 최근매출액증가율y <= 0 < 최근매출액증가율q:
282
- # 직전에 마이너스였다가 최근에 회복된 경우 MAX/10점 보너스
283
- sp1 += MAX / 10
284
- # 나머지는 성장률 기반 점수 배정
285
- sp1 += MAX / 2 if 최근매출액증가율q > MAX else 최근매출액증가율q / 2
286
- else:
287
- # 최근 자료가 마이너스인 경우 마이너스만큼 점수를 차감한다.
288
- sp1 += -(MAX / 2) if 최근매출액증가율q < -MAX else 최근매출액증가율q / 2
289
-
290
- # 평균매출액증가율 구하기
291
- d_values = [i for i in dict_y.values() if not math.isnan(i)]
292
- logger.debug(f'평균매출액증가율 d_values : {d_values}')
293
-
294
- if len(d_values) == 0:
295
- 평균매출액증가율 = float('nan')
296
- else:
297
- 평균매출액증가율 = float(numpy.mean(d_values))
298
- logger.debug(f'평균 : {평균매출액증가율}')
299
-
300
- sp2 = 0
301
- if math.isnan(평균매출액증가율):
302
- sp2 += -(MAX/2)
303
- elif 평균매출액증가율 <= 0:
304
- # 평균매출액증가율이 마이너스인 경우 마이너스만큼 점수를 차감한다.
305
- sp2 += -(MAX / 2) if 평균매출액증가율 < -MAX else 평균매출액증가율 / 2
306
- else:
307
- sp2 = MAX / 2 if 평균매출액증가율 > MAX else 평균매출액증가율 / 2
308
-
309
- logger.debug(f'매출액증가율평가 point : {sp1 + sp2}')
310
-
311
- return int(sp1 + sp2)
312
-
313
- p1 = 매출액증가율평가(growth_dict['매출액증가율'])
314
-
315
- def 영업이익률평가(영업이익률: dict) -> int:
316
- # 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
317
- 영업이익률 = copy.deepcopy(영업이익률)
318
- name = myredis.Corps.get_name(code)
319
-
320
- p = 0
321
- try:
322
- myprofit = utils.to_float(영업이익률.pop(name))
323
- except KeyError:
324
- logger.warning(f'{name} 영업이익률 does not exist.')
325
- return 0
326
- logger.debug(f'종목영업이익률 : {myprofit}')
327
-
328
- for profit in 영업이익률.values():
329
- profit = utils.to_float(profit)
330
- if math.isnan(profit):
331
- continue
332
- elif myprofit > profit:
333
- p += 1
334
- else:
335
- continue
336
-
337
- logger.debug(f'영업이익률평가 point : {p}')
338
- return p
339
-
340
- p2 = 영업이익률평가(growth_dict['영업이익률'])
341
-
342
- return p1, p2
@@ -1,289 +0,0 @@
1
-
2
-
3
- def _make_df_part(db_addr, codes: list, q):
4
- def make_record(my_client, my_code: str) -> dict:
5
- # 장고에서 사용할 eval 테이블을 만들기 위해 각각의 레코드를 구성하는 함수
6
- c101 = mongo.C101(my_client, my_code).get_recent()
7
-
8
- red_dict = red(my_client, my_code)
9
- mil_dict = mil(my_client, my_code)
10
- growth_dict = growth(my_client, my_code)
11
-
12
- mil_date = mil_dict['date']
13
- red_date = red_dict['date']
14
- growth_date = growth_dict['date']
15
-
16
- return {
17
- 'code': c101['코드'],
18
- '종목명': c101['종목명'],
19
- '주가': utils.to_int(c101['주가']),
20
- 'PER': utils.to_float(c101['PER']),
21
- 'PBR': utils.to_float(c101['PBR']),
22
- '시가총액': utils.to_float(c101['시가총액']),
23
- 'RED': utils.to_int(red_dict['red_price']),
24
- '주주수익률': utils.to_float(mil_dict['주주수익률']),
25
- '이익지표': utils.to_float(mil_dict['이익지표']),
26
- 'ROIC': utils.to_float(mil_dict['투자수익률']['ROIC']),
27
- 'ROE': utils.to_float(mil_dict['투자수익률']['ROE']),
28
- 'PFCF': utils.to_float(mongo.Corps.latest_value(mil_dict['가치지표']['PFCF'])[1]),
29
- 'PCR': utils.to_float(mongo.Corps.latest_value(mil_dict['가치지표']['PCR'])[1]),
30
- '매출액증가율': utils.to_float(growth_dict['매출액증가율'][0]),
31
- 'date': list(set(mil_date + red_date + growth_date))
32
- }
33
- # 각 코어별로 디비 클라이언트를 만들어야만 한다. 안그러면 에러발생
34
- client = mongo.connect_mongo(db_addr)
35
- t = len(codes)
36
- d = []
37
- for i, code in enumerate(codes):
38
- print(f'{i+1}/{t} {code}')
39
- try:
40
- d.append(make_record(client, code))
41
- except:
42
- logger.error(f'error on {code}')
43
- continue
44
- df = pd.DataFrame(d)
45
- logger.info(df)
46
- q.put(df)
47
-
48
-
49
- def make_today_eval_df(client, refresh: bool = False) -> pd.DataFrame:
50
- """ 멀티프로세싱을 사용하여 전체 종목의 eval 을 데이터프레임으로 만들어 반환
51
-
52
- 기본값으로 refresh 는 False 로 설정되어 당일자의 저장된 데이터프레임이 있으면 새로 생성하지 않고 mongo DB를 이용한다.
53
- """
54
- today_str = datetime.datetime.today().strftime('%Y%m%d')
55
- df = mongo.EvalByDate(client, today_str).load_df()
56
- if refresh or len(df) == 0:
57
- codes_in_db = mongo.Corps.get_all_codes(client)
58
-
59
- print('*' * 25, f"Eval all using multiprocess(refresh={refresh})", '*' * 25)
60
- print(f'Total {len(codes_in_db)} items..')
61
- logger.debug(codes_in_db)
62
- n, divided_list = utils.code_divider_by_cpu_core(codes_in_db)
63
-
64
- addr = mongo.extract_addr_from_client(client)
65
-
66
- start_time = time.time()
67
- q = Queue()
68
- ths = []
69
- for i in range(n):
70
- ths.append(Process(target=_make_df_part, args=(addr, divided_list[i], q)))
71
- for i in range(n):
72
- ths[i].start()
73
-
74
- df_list = []
75
- for i in range(n):
76
- df_list.append(q.get())
77
- # 부분데이터프레임들을 하나로 합침
78
- final_df = pd.concat(df_list, ignore_index=True)
79
-
80
- for i in range(n):
81
- ths[i].join()
82
-
83
- print(f'Total spent time : {round(time.time() - start_time, 2)} sec.')
84
- logger.debug(final_df)
85
- print(f"Save to mongo db(db: eval col: {today_str})")
86
- mongo.EvalByDate(client, today_str).save_df(final_df)
87
- else:
88
- print(f"Use saved dataframe from mongo db..")
89
- final_df = df
90
- return final_df
91
-
92
-
93
- def yield_valid_spac(client) -> tuple:
94
- """
95
- 전체 스팩주의 현재가를 평가하여 2000원 이하인 경우 yield한다.
96
-
97
- Returns:
98
- tuple: (code, name, price)
99
- """
100
- codes = mongo.Corps.get_all_codes(client)
101
- logger.debug(f'len(codes) : {len(codes)}')
102
- print('<<< Finding valuable SPAC >>>')
103
- for i, code in enumerate(codes):
104
- name = mongo.Corps.get_name(client, code)
105
- logger.debug(f'code : {code} name : {name}')
106
- if '스팩' in str(name):
107
- logger.debug(f'>>> spac - code : {code} name : {name}')
108
- price, _, _ = utils.get_price_now(code=code)
109
- if price <= 2000:
110
- logger.warning(f'현재가:{price}')
111
- print(f"code: {code} name: {name}, price: {price}")
112
- yield code, name, price
113
-
114
-
115
-
116
- class GetDFTest(unittest.TestCase):
117
- def test_make_df_part(self):
118
- codes = ['025320', '000040', '060280', '003240']
119
- from multiprocessing import Queue
120
- q = Queue()
121
- eval._make_df_part(addr, codes, q)
122
-
123
- def test_get_df(self):
124
- print(eval.make_today_eval_df(client, refresh=True))
125
- print(eval.make_today_eval_df(client, refresh=False))
126
-
127
-
128
- class SpacTest(unittest.TestCase):
129
- def test_valid_spac(self):
130
- for code, name, price in eval.yield_valid_spac(client):
131
- print(code, name, price)
132
-
133
-
134
-
135
-
136
- def mil(code: str) -> Tuple[int, int, int, int]:
137
- """
138
- - 재무활동현금흐름이 마이너스라는 것은 배당급 지급했거나, 자사주 매입했거나, 부채를 상환한 상태임.
139
- - 반대는 채권자로 자금을 조달했거나 신주를 발행했다는 의미
140
- <주주수익률> - 재무활동현금흐름/시가총액 => 5%이상인가?
141
-
142
- 투하자본수익률(ROIC)가 30%이상인가
143
- ROE(자기자본이익률) 20%이상이면 아주 우수 다른 투자이익률과 비교해볼것 10%미만이면 별로...단, 부채비율을 확인해야함.
144
-
145
- 이익지표 ...영업현금흐름이 순이익보다 많은가 - 결과값이 음수인가..
146
-
147
- FCF는 영업현금흐름에서 자본적 지출(유·무형투자 비용)을 차감한 순수한 현금력이라 할 수 있다.
148
- 말 그대로 자유롭게(Free) 사용할 수 있는 여윳돈을 뜻한다.
149
- 잉여현금흐름이 플러스라면 미래의 투자나 채무상환에 쓸 재원이 늘어난 것이다.
150
- CAPEX(Capital expenditures)는 미래의 이윤을 창출하기 위해 지출된 비용을 말한다.
151
- 이는 기업이 고정자산을 구매하거나, 유효수명이 당회계년도를 초과하는 기존의 고정자산에 대한 투자에 돈이 사용될 때 발생한다.
152
-
153
- 잉여현금흐름이 마이너스일때는 설비투자가 많은 시기라 주가가 약세이며 이후 설비투자 마무리되면서 주가가 상승할수 있다.
154
- 주가는 잉여현금흐름이 증가할때 상승하는 경향이 있다.
155
- fcf = 영업현금흐름 - capex
156
-
157
- 가치지표평가
158
- price to fcf 계산
159
- https://www.investopedia.com/terms/p/pricetofreecashflow.asp
160
- pcr보다 정확하게 주식의 가치를 평가할수 있음. 10배이하 추천
161
-
162
- Returns:
163
- tuple: 주주수익률, 이익지표, 투자수익률, PFCF포인트
164
- """
165
- mil_dict = eval.mil(code)
166
-
167
- print(pprint.pformat(mil_dict, width=200))
168
-
169
- # 주주수익률 평가
170
- if math.isnan(mil_dict['주주수익률']):
171
- score1 = 0
172
- else:
173
- 주주수익률평가 = math.ceil(mil_dict['주주수익률'] - (eval.EXPECT_EARN * 100))
174
- score1 = 0 if 0 > 주주수익률평가 else 주주수익률평가
175
-
176
- # 이익지표 평가
177
- score2 = 10 if mil_dict['이익지표'] < 0 else 0
178
-
179
- # 투자수익률 평가
180
- MAX3 = 20
181
- score3 = 0
182
- roic = mil_dict['투자수익률']['ROIC']
183
- roe = mil_dict['투자수익률']['ROE']
184
- if math.isnan(roic) or roic <= 0:
185
- # roic 가 비정상이라 평가할 수 없는 경우
186
- if 10 < roe <= 20:
187
- score3 += round(MAX3 * 0.333)
188
- elif 20 < roe:
189
- score3 += round(MAX3 * 0.666)
190
- elif 0 < roic:
191
- # roic 로 평가할 수 있는 경우
192
- if 0 < roic <= 15:
193
- score3 += round(MAX3 * 0.333)
194
- elif 15 < roic <= 30:
195
- score3 += round(MAX3 * 0.666)
196
- elif 30 < roic:
197
- score3 += MAX3
198
-
199
- # PFCF 평가
200
- pfcf_dict = mil_dict['가치지표']['PFCF']
201
- _, pfcf = mongo.Corps.latest_value(pfcf_dict)
202
-
203
- logger.debug(f'recent pfcf {_}, {pfcf}')
204
- try:
205
- p = round(-40 * math.log10(pfcf) + 40)
206
- except ValueError:
207
- p = 0
208
- score4 = 0 if 0 > p else p
209
-
210
- return score1, score2, score3, score4
211
-
212
-
213
-
214
-
215
- def dbmanager():
216
- cmd = ['repair', 'sync', 'eval', 'update']
217
- parser = argparse.ArgumentParser()
218
- parser.add_argument('cmd', help=f"Command - {cmd}")
219
- parser.add_argument('target', help="Target for scraping (type 6digit code or 'all' or 'parts')")
220
- parser.add_argument('-d', '--db_path', help="Set mongo database path")
221
-
222
- args = parser.parse_args()
223
-
224
- db_path = args.db_path if args.db_path else "mongodb://192.168.0.173:27017"
225
- client = mongo.connect_mongo(db_path)
226
-
227
- if args.cmd in cmd:
228
- if args.cmd == 'repair':
229
- if args.target == 'all' or utils.is_6digit(args.target):
230
- need_for_repair_codes = chk_db.chk_integrity_corps(client, args.target)
231
- # repair dict 예시 - {'343510': ['c106', 'c104', 'c103'], '298000': ['c104'], '091810': ['c104']}
232
- print(f"Need for repairing codes :{need_for_repair_codes}")
233
- if need_for_repair_codes:
234
- # x = input("Do you want to try to repair db by scraping? (y/N)")
235
- # if x == 'y' or x == 'Y':
236
- for code, failed_page_list in need_for_repair_codes.items():
237
- for page in failed_page_list:
238
- if page == 'c101':
239
- nfsrun.c101([code, ], db_path)
240
- elif page == 'c103':
241
- nfsrun.c103([code, ], db_path)
242
- elif page == 'c104':
243
- nfsrun.c104([code, ], db_path)
244
- elif page == 'c106':
245
- nfsrun.c106([code, ], db_path)
246
- recheck_result = chk_db.chk_integrity_corps(client, code)
247
- if recheck_result:
248
- # 다시 스크랩해도 오류가 지속되는 경우
249
- print(f"The db integrity failure persists..{recheck_result}")
250
- # x = input(f"Do you want to delete {code} on DB? (y/N)")
251
- # if x == 'y' or x == 'Y':
252
- # mongo.Corps.del_db(client, code)
253
- # else:
254
- # print("Canceled.")
255
- mongo.Corps.del_db(client, code)
256
- # else:
257
- # print("Done.")
258
- else:
259
- print("Done.")
260
- else:
261
- print(f"Invalid target option : {args.target}")
262
- elif args.cmd == 'update':
263
- if args.target == 'all' or utils.is_6digit(args.target):
264
- need_for_update_codes = list(chk_db.chk_modifying_corps(client, args.target).keys())
265
- # need_for_update_codes 예시 - [codes....]
266
- print(f"Need for updating codes :{need_for_update_codes}")
267
- if need_for_update_codes:
268
- nfsrun.c103(need_for_update_codes, db_path)
269
- nfsrun.c104(need_for_update_codes, db_path)
270
- nfsrun.c106(need_for_update_codes, db_path)
271
- elif args.target == 'parts':
272
- pass
273
- else:
274
- print(f"Invalid target option : {args.target}")
275
- elif args.cmd == 'sync':
276
- if args.target == 'all':
277
- chk_db.sync_mongo_with_krx(client)
278
- else:
279
- print(f"The target should be 'all' in sync command.")
280
- elif args.cmd == 'eval':
281
- if args.target == 'all':
282
- # eval을 평가해서 데이터베이스에 저장한다.
283
- eval.make_today_eval_df(client, refresh=True)
284
- else:
285
- print(f"The target should be 'all' in sync command.")
286
- else:
287
- print(f"The command should be in {cmd}")
288
-
289
- client.close()
@@ -1,23 +0,0 @@
1
- analyser_hj3415/__init__.py,sha256=jqHEUoBeihYOMaS0bPOe3nRVXBufZ0clxc6M6jxPY0o,320
2
- analyser_hj3415/cli.py,sha256=dnmhYBucWWNYsnyf0xVHcx87j7rciG7T2omD3_oGiYk,12300
3
- analyser_hj3415/analyser/__init__.py,sha256=N0XyBfWJNpDS_6JYziKETWePO_jtFB1m7E8Qbwt1w0Q,1096
4
- analyser_hj3415/analyser/compile.py,sha256=wMTuVadcSODHjjTP5ma_GuygujqCeYDQkad2rAsCprw,12535
5
- analyser_hj3415/analyser/eval/__init__.py,sha256=IP1d0Q3nOCAD3zK1qxrC685MkJQfUh-qaXc7xptTxk8,80
6
- analyser_hj3415/analyser/eval/blue.py,sha256=p9_ddqLMJGq5HSn6NApuLhrX29qD--AASig9F71eb8I,10952
7
- analyser_hj3415/analyser/eval/common.py,sha256=sNXapoofShA43ww_SLjXmIjkrAr1AhAcezdaN_X_3Us,11443
8
- analyser_hj3415/analyser/eval/growth.py,sha256=sfJ7h06efrTfL4ylhUCV525IzUzilbun1ya9r5SVCtU,6526
9
- analyser_hj3415/analyser/eval/mil.py,sha256=mFMiFCuCBvlQrhQcM5hMg8U4zF32TS1GnUmk8fPd950,15178
10
- analyser_hj3415/analyser/eval/red.py,sha256=8aJPpiVzLOZtt6kILCzqcfL8BrEVgIld1iI3StGvg8A,12193
11
- analyser_hj3415/analyser/tsa/__init__.py,sha256=pg20ZQRABedTdaIoOr5t043RNKtJ7ji_WmnZrD1IhPg,147
12
- analyser_hj3415/analyser/tsa/common.py,sha256=OnsZ_cFYmNzmk0tV5qSqVW-5jJyrwMWHguWdS2Z6fvY,979
13
- analyser_hj3415/analyser/tsa/lstm.py,sha256=P8peqg6ZUpCSNKupjFlyba3xbPZoYtpd2UihXib_4Do,28548
14
- analyser_hj3415/analyser/tsa/prophet.py,sha256=AlDU6YmjSf094diTVpMe0w06Z-FZ_2KAsDm2rshzxpA,16957
15
- analyser_hj3415/workroom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- analyser_hj3415/workroom/mysklearn.py,sha256=wJXKz5MqqTzADdG2mqRMMzc_G9RzwYjj5_j4gyOopxQ,2030
17
- analyser_hj3415/workroom/mysklearn2.py,sha256=1lIy6EWEQHkOzDS-av8U0zQH6DuCLKWMI73dnJx5KRs,1495
18
- analyser_hj3415/workroom/score.py,sha256=P6nHBJYmyhigGtT4qna4BmNtvt4B93b7SKyzdstJK24,17376
19
- analyser_hj3415/workroom/trash.py,sha256=zF-W0piqkGr66UP6-iybo9EXh2gO0RP6R1FnIpsGkl8,12262
20
- analyser_hj3415-3.4.2.dist-info/entry_points.txt,sha256=ZfjPnJuH8SzvhE9vftIPMBIofsc65IAWYOhqOC_L5ck,65
21
- analyser_hj3415-3.4.2.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
22
- analyser_hj3415-3.4.2.dist-info/METADATA,sha256=F3_EIBCZD-sDpwathQJp9cCa_yF46fgHDegSF_FPeB8,6777
23
- analyser_hj3415-3.4.2.dist-info/RECORD,,