analyser_hj3415 2.0.2__py2.py3-none-any.whl → 2.1.0__py2.py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,9 @@
1
1
  import math
2
2
  from typing import Tuple
3
- from collections import OrderedDict
4
-
5
- from .mongo import C101, C103, C104, Corps
3
+ from db_hj3415.myredis import C101, C103, C104
6
4
 
7
5
  import logging
6
+
8
7
  logger = logging.getLogger(__name__)
9
8
  formatter = logging.Formatter('%(levelname)s: [%(name)s] %(message)s')
10
9
  ch = logging.StreamHandler()
@@ -13,24 +12,16 @@ logger.addHandler(ch)
13
12
  logger.setLevel(logging.WARNING)
14
13
 
15
14
 
16
- def extract_valid_one(*args):
15
+ def set_data(*args) -> list:
17
16
  """
18
- 유틸함수
19
- 딕셔너리 데이터를 입력받아 하나씩 pop 하여 빈데이터가 아닌 첫번째것을 반환한다.
17
+ 비유효한 내용 제거(None,nan)하고 중복된 항목 제거하고 리스트로 반환한다.
18
+ :param args:
19
+ :return:
20
20
  """
21
- logger.debug("In extract_valid_one func...")
22
- # 입력받은 데이터를 중복되는 것을 제하기 위해 집합으로 변환한다.
23
- d_set = {i for i in args}
24
- for i in d_set:
25
- logger.debug(i)
26
- # 하나씩 꺼내서 빈문자가 아니면 반환한다.
27
- if i != "" and i is not math.nan and i is not None:
28
- return i
29
- else:
30
- return None
21
+ return [i for i in {*args} if i != "" and i is not math.nan and i is not None]
31
22
 
32
23
 
33
- def calc당기순이익(client, code: str) -> Tuple[str, float]:
24
+ def calc당기순이익(code: str) -> Tuple[str, float]:
34
25
  """지배지분 당기순이익 계산
35
26
 
36
27
  일반적인 경우로는 직전 지배주주지분 당기순이익을 찾아서 반환한다.\n
@@ -38,25 +29,24 @@ def calc당기순이익(client, code: str) -> Tuple[str, float]:
38
29
  계산을 통해서 간접적으로 구한다.\n
39
30
  """
40
31
  logger.debug(f'In the calc당기순이익... code:{code}')
41
- c103q = C103(client, code, 'c103재무상태표q')
32
+ c103q = C103(code, 'c103재무상태표q')
42
33
  try:
43
- profit_dict = c103q.find(title='*(지배)당기순이익')
44
- logger.info(f'*(지배)당기순이익 : {profit_dict}')
45
- return c103q.latest_value('*(지배)당기순이익', nan_to_zero=True)
34
+ return c103q.latest_value_pop2('*(지배)당기순이익')
46
35
  except:
47
- # 금융관련은 재무상태표에 지배당기순이익이 없어서 손익계산서의 당기순이익에서 비지배당기순이익을 빼서 간접적으로 구한다.
36
+ logger.warning(f"{code} - (지배)당기순이익이 없는 종목. 수동으로 계산합니다(금융관련업종일 가능성있음).")
48
37
  c103q.page = 'c103손익계산서q'
49
- 최근당기순이익date, 최근당기순이익value = c103q.sum_recent_4q('당기순이익', nan_to_zero=True)
38
+ 최근당기순이익date, 최근당기순이익value = c103q.sum_recent_4q('당기순이익')
50
39
  c103q.page = 'c103재무상태표q'
51
- 비지배당기순이익date, 비지배당기순이익value= c103q.latest_value('*(비지배)당기순이익', nan_to_zero=True, allow_empty=True)
40
+ 비지배당기순이익date, 비지배당기순이익value = c103q.latest_value_pop2('*(비지배)당기순이익')
52
41
 
53
- date = extract_valid_one(최근당기순이익date, 비지배당기순이익date)
42
+ # 가변리스트 언패킹으로 하나의 날짜만 사용하고 나머지는 버린다.
43
+ date, *_ = set_data(최근당기순이익date, 비지배당기순이익date)
54
44
  계산된지배당기순이익value = 최근당기순이익value - 비지배당기순이익value
55
45
 
56
46
  return date, 계산된지배당기순이익value
57
47
 
58
48
 
59
- def calc유동자산(client, code: str) -> Tuple[str, float]:
49
+ def calc유동자산(code: str) -> Tuple[str, float]:
60
50
  """유효한 유동자산 계산
61
51
 
62
52
  일반적인 경우로 유동자산을 찾아서 반환한다.\n
@@ -64,29 +54,27 @@ def calc유동자산(client, code: str) -> Tuple[str, float]:
64
54
  Red와 Blue에서 사용한다.\n
65
55
  """
66
56
  logger.debug(f'In the calc유동자산... code:{code}')
67
- c103q = C103(client, code, 'c103재무상태표q')
57
+ c103q = C103(code, 'c103재무상태표q')
68
58
  try:
69
- asset_dict = c103q.find(title='유동자산')
70
- logger.info(f'유동자산 : {asset_dict}')
71
- return c103q.sum_recent_4q('유동자산', nan_to_zero=True)
59
+ return c103q.sum_recent_4q('유동자산')
72
60
  except:
73
- # 금융관련업종...
74
- d1, v1 = c103q.latest_value('현금및예치금', nan_to_zero=True, allow_empty=True)
75
- d2, v2 = c103q.latest_value('단기매매금융자산', nan_to_zero=True, allow_empty=True)
76
- d3, v3 = c103q.latest_value('매도가능금융자산', nan_to_zero=True, allow_empty=True)
77
- d4, v4 = c103q.latest_value('만기보유금융자산', nan_to_zero=True, allow_empty=True)
61
+ logger.warning(f"{code} - 유동자산이 없는 종목. 수동으로 계산합니다(금융관련업종일 가능성있음).")
62
+ d1, v1 = c103q.latest_value_pop2('현금및예치금')
63
+ d2, v2 = c103q.latest_value_pop2('단기매매금융자산')
64
+ d3, v3 = c103q.latest_value_pop2('매도가능금융자산')
65
+ d4, v4 = c103q.latest_value_pop2('만기보유금융자산')
78
66
  logger.debug(f'현금및예치금 : {d1}, {v1}')
79
67
  logger.debug(f'단기매매금융자산 : {d2}, {v2}')
80
68
  logger.debug(f'매도가능금융자산 : {d3}, {v3}')
81
69
  logger.debug(f'만기보유금융자산 : {d4}, {v4}')
82
70
 
83
- date = extract_valid_one(d1, d2, d3, d4)
71
+ date, *_ = set_data(d1, d2, d3, d4)
84
72
  계산된유동자산value = v1 + v2 + v3 + v4
85
73
 
86
74
  return date, 계산된유동자산value
87
75
 
88
76
 
89
- def calc유동부채(client, code: str) -> Tuple[str, float]:
77
+ def calc유동부채(code: str) -> Tuple[str, float]:
90
78
  """유효한 유동부채 계산
91
79
 
92
80
  일반적인 경우로 유동부채를 찾아서 반환한다.\n
@@ -94,29 +82,27 @@ def calc유동부채(client, code: str) -> Tuple[str, float]:
94
82
  Red와 Blue에서 사용한다.\n
95
83
  """
96
84
  logger.debug(f'In the calc유동부채... code:{code}')
97
- c103q = C103(client, code, 'c103재무상태표q')
85
+ c103q = C103(code, 'c103재무상태표q')
98
86
  try:
99
- debt_dict = c103q.find(title='유동부채')
100
- logger.debug(f'유동부채 : {debt_dict}')
101
- return c103q.sum_recent_4q('유동부채', nan_to_zero=True)
87
+ return c103q.sum_recent_4q('유동부채')
102
88
  except:
103
- # 금융관련업종...
104
- d1, v1 = c103q.latest_value('당기손익인식(지정)금융부채', nan_to_zero=True, allow_empty=True)
105
- d2, v2 = c103q.latest_value('당기손익-공정가치측정금융부채', nan_to_zero=True, allow_empty=True)
106
- d3, v3 = c103q.latest_value('매도파생결합증권', nan_to_zero=True, allow_empty=True)
107
- d4, v4 = c103q.latest_value('단기매매금융부채', nan_to_zero=True, allow_empty=True)
89
+ logger.warning(f"{code} - 유동부채가 없는 종목. 수동으로 계산합니다(금융관련업종일 가능성있음).")
90
+ d1, v1 = c103q.latest_value_pop2('당기손익인식(지정)금융부채')
91
+ d2, v2 = c103q.latest_value_pop2('당기손익-공정가치측정금융부채')
92
+ d3, v3 = c103q.latest_value_pop2('매도파생결합증권')
93
+ d4, v4 = c103q.latest_value_pop2('단기매매금융부채')
108
94
  logger.debug(f'당기손익인식(지정)금융부채 : {d1}, {v1}')
109
95
  logger.debug(f'당기손익-공정가치측정금융부채 : {d2}, {v2}')
110
96
  logger.debug(f'매도파생결합증권 : {d3}, {v3}')
111
97
  logger.debug(f'단기매매금융부채 : {d4}, {v4}')
112
98
 
113
- date = extract_valid_one(d1, d2, d3, d4)
99
+ date, *_ = set_data(d1, d2, d3, d4)
114
100
  계산된유동부채value = v1 + v2 + v3 + v4
115
101
 
116
102
  return date, 계산된유동부채value
117
103
 
118
104
 
119
- def calc비유동부채(client, code: str) -> Tuple[str, float]:
105
+ def calc비유동부채(code: str) -> Tuple[str, float]:
120
106
  """유효한 비유동부채 계산
121
107
 
122
108
  일반적인 경우로 비유동부채를 찾아서 반환한다.\n
@@ -124,46 +110,44 @@ def calc비유동부채(client, code: str) -> Tuple[str, float]:
124
110
  Red와 Blue에서 사용한다.\n
125
111
  """
126
112
  logger.debug(f'In the calc비유동부채... code:{code}')
127
- c103q = C103(client, code, 'c103재무상태표q')
113
+ c103q = C103(code, 'c103재무상태표q')
128
114
  try:
129
- debt_dict = c103q.find(title='비유동부채')
130
- logger.debug(f'비유동부채 : {debt_dict}')
131
- return c103q.sum_recent_4q('비유동부채', nan_to_zero=True)
115
+ return c103q.sum_recent_4q('비유동부채')
132
116
  except:
133
- # 금융관련업종...
117
+ logger.warning(f"{code} - 비유동부채가 없는 종목. 수동으로 계산합니다(금융관련업종일 가능성있음).")
134
118
  # 보험관련업종은 예수부채가 없는대신 보험계약부채가 있다...
135
- d1, v1 = c103q.latest_value('예수부채', nan_to_zero=True, allow_empty=True)
136
- d2, v2 = c103q.latest_value('보험계약부채(책임준비금)', nan_to_zero=True, allow_empty=True)
137
- d3, v3 = c103q.latest_value('차입부채', nan_to_zero=True, allow_empty=True)
138
- d4, v4 = c103q.latest_value('기타부채', nan_to_zero=True, allow_empty=True)
119
+ d1, v1 = c103q.latest_value_pop2('예수부채')
120
+ d2, v2 = c103q.latest_value_pop2('보험계약부채(책임준비금)')
121
+ d3, v3 = c103q.latest_value_pop2('차입부채')
122
+ d4, v4 = c103q.latest_value_pop2('기타부채')
139
123
  logger.debug(f'예수부채 : {d1}, {v1}')
140
124
  logger.debug(f'보험계약부채(책임준비금) : {d2}, {v2}')
141
125
  logger.debug(f'차입부채 : {d3}, {v3}')
142
126
  logger.debug(f'기타부채 : {d4}, {v4}')
143
127
 
144
- date = extract_valid_one(d1, d2, d3, d4)
128
+ date, *_ = set_data(d1, d2, d3, d4)
145
129
  계산된비유동부채value = v1 + v2 + v3 + v4
146
130
 
147
131
  return date, 계산된비유동부채value
148
132
 
149
133
 
150
- def calc유동비율(client, code: str, pop_count: int) -> Tuple[str, float]:
134
+ def calc유동비율(code: str, pop_count: int) -> Tuple[str, float]:
151
135
  """유동비율계산 - Blue에서 사용
152
136
 
153
137
  c104q에서 최근유동비율 찾아보고 유효하지 않거나 \n
154
138
  100이하인 경우에는수동으로 계산해서 다시 한번 평가해 본다.\n
155
139
  """
156
140
  logger.debug(f'In the calc유동비율... code:{code}')
157
- c104q = C104(client, code, 'c104q')
158
- 유동비율date, 유동비율value = c104q.latest_value('유동비율', pop_count=pop_count, allow_empty=True)
141
+ c104q = C104(code, 'c104q')
142
+ 유동비율date, 유동비율value = c104q.mymongo_c1034.latest_value('유동비율', pop_count=pop_count)
159
143
  logger.debug(f'{code} 유동비율 : {유동비율value}({유동비율date})')
160
144
 
161
145
  if math.isnan(유동비율value) or 유동비율value < 100:
162
146
  logger.warning('유동비율 is under 100 or nan..so we will recalculate..')
163
- 유동자산date, 유동자산value = calc유동자산(client, code)
164
- 유동부채date, 유동부채value = calc유동부채(client, code)
147
+ 유동자산date, 유동자산value = calc유동자산(code)
148
+ 유동부채date, 유동부채value = calc유동부채(code)
165
149
 
166
- c103q = C103(client, code, 'c103현금흐름표q')
150
+ c103q = C103(code, 'c103현금흐름표q')
167
151
  추정영업현금흐름date, 추정영업현금흐름value = c103q.sum_recent_4q('영업활동으로인한현금흐름')
168
152
  logger.debug(f'{code} 계산전 유동비율 : {유동비율value}({유동비율date})')
169
153
 
@@ -175,17 +159,36 @@ def calc유동비율(client, code: str, pop_count: int) -> Tuple[str, float]:
175
159
  계산된유동비율 = float('inf')
176
160
  finally:
177
161
  logger.debug(f'{code} 계산된 유동비율 : {계산된유동비율}')
178
- return extract_valid_one(유동자산date, 유동부채date, 추정영업현금흐름date), 계산된유동비율
162
+ date, *_ = set_data(유동자산date, 유동부채date, 추정영업현금흐름date)
163
+ return date, 계산된유동비율
179
164
  else:
180
165
  return 유동비율date, 유동비율value
181
166
 
182
167
 
183
- def findFCF(client, code: str) -> dict:
184
- """FCF 계산
168
+ """
169
+ FCF “Free Cash Flow”의 약자로, 한국어로는 “자유 현금 흐름”이라고 합니다. FCF는 기업이 운영 활동을 통해 창출한 현금 중에서 영업 및 자본적 지출을
170
+ 제외하고 남은 현금을 의미합니다. 이는 기업의 재무 건전성을 평가하는 중요한 지표로 사용됩니다. 자유 현금 흐름은 기업이 부채를 상환하고, 배당금을 지급하며,
171
+ 추가적인 투자를 할 수 있는 자금을 나타냅니다.
172
+
173
+ FCF의 중요성
185
174
 
186
- FCF = 영업활동현금흐름 - CAPEX\n
187
- 영업활동현금흐름에서 CAPEX 연도별로 빼주어 fcf 구하고 딕셔너리로 반환한다.\n
175
+ 1. 재무 건전성 평가: FCF 기업이 실제로 얼마나 많은 현금을 창출하고 있는지를 보여줍니다. 이는 기업의 재무 건전성을 평가하는 데 중요한 지표입니다.
176
+ 2. 투자 결정: 투자자들은 FCF통해 기업의 성장 가능성을 평가하고, 투자 결정을 내리는 데 참고합니다.
177
+ 3. 배당 지급 능력: FCF는 기업이 주주들에게 배당금을 지급할 수 있는 능력을 나타냅니다.
178
+ 4. 부채 상환: 기업은 FCF를 이용해 부채를 상환하고, 재무 구조를 개선할 수 있습니다.
188
179
 
180
+ CAPEX는 “Capital Expenditures”의 약자로, 한국어로는 “자본적 지출”이라고 합니다. CAPEX는 기업이 장기 자산을 구입하거나 유지하는 데 사용하는
181
+ 비용을 의미합니다. 이는 기업이 장기적인 성장을 위해 자산을 확장, 업그레이드 또는 유지하는 데 필요한 비용입니다. 이러한 자산에는 부동산, 건물, 기계,
182
+ 장비 등이 포함됩니다.
183
+
184
+ CAPEX가 거의 없거나 아예 없는 업종에서도 자유 현금 흐름(Free Cash Flow, FCF)을 계산할 수 있습니다. CAPEX가 없는 경우,
185
+ 계산식에서 CAPEX 부분을 0으로 처리하면 됩니다.
186
+ """
187
+
188
+
189
+ def findFCF(code: str) -> dict:
190
+ """
191
+ FCF 계산
189
192
  Returns:
190
193
  dict: 계산된 fcf 딕셔너리 또는 영업현금흐름 없는 경우 - {}
191
194
 
@@ -193,10 +196,10 @@ def findFCF(client, code: str) -> dict:
193
196
  CAPEX 가 없는 업종은 영업활동현금흐름을 그대로 사용한다.\n
194
197
 
195
198
  """
196
- c103y = C103(client, code, 'c103현금흐름표y')
197
- 영업활동현금흐름_dict = c103y.find(title='영업활동으로인한현금흐름', allow_empty=True)
199
+ c103y = C103(code, 'c103현금흐름표y')
200
+ _, 영업활동현금흐름_dict = c103y.find_without_yoy('영업활동으로인한현금흐름')
198
201
  c103y.page = 'c103재무상태표y'
199
- capex = c103y.find(title='*CAPEX', allow_empty=True)
202
+ _, capex = c103y.find_without_yoy('*CAPEX')
200
203
 
201
204
  logger.debug(f'영업활동현금흐름 {영업활동현금흐름_dict}')
202
205
  logger.debug(f'CAPEX {capex}')
@@ -206,6 +209,7 @@ def findFCF(client, code: str) -> dict:
206
209
 
207
210
  if len(capex) == 0:
208
211
  # CAPEX 가 없는 업종은 영업활동현금흐름을 그대로 사용한다.
212
+ logger.warning(f"{code} - CAPEX가 없는 업종으로 영업현금흐름을 그대로 사용합니다..")
209
213
  return 영업활동현금흐름_dict
210
214
 
211
215
  # 영업 활동으로 인한 현금 흐름에서 CAPEX 를 각 연도별로 빼주어 fcf 를 구하고 리턴값으로 fcf 딕셔너리를 반환한다.
@@ -223,8 +227,22 @@ def findFCF(client, code: str) -> dict:
223
227
  return dict(sorted(r_dict.items(), reverse=False))
224
228
 
225
229
 
226
- def findPFCF(client, code: str) -> dict:
227
- """Price to Free Cash Flow Ratio 계산
230
+ """
231
+ PFCF의 중요성
232
+ 1. 기업 가치 평가: PFCF는 기업이 창출하는 현금 흐름에 비해 주가가 적정한지 평가하는 데 사용됩니다. 낮은 PFCF는 주가가 상대적으로 저평가되었음을 나타낼
233
+ 수 있고, 높은 PFCF는 주가가 상대적으로 고평가되었음을 나타낼 수 있습니다.
234
+ 2. 투자 결정: 투자자들은 PFCF를 사용하여 현금 흐름 창출 능력 대비 주가가 매력적인지를 판단하고, 투자 결정을 내리는 데 참고합니다.
235
+ 3. 비교 분석: 같은 산업 내 다른 기업들과 비교하여, 어느 기업이 더 효율적으로 현금 흐름을 창출하는지를 평가할 수 있습니다.
236
+
237
+ PFCF의 한계
238
+
239
+ •산업 특성: PFCF는 산업마다 적정한 수준이 다를 수 있습니다. 예를 들어, 기술 산업과 제조 산업의 적정 PFCF는 다를 수 있습니다.
240
+ •일회성 항목: 특정 연도의 일회성 비용이나 수익이 FCF에 영향을 미칠 수 있으며, 이는 PFCF 계산에 왜곡을 가져올 수 있습니다.
241
+ """
242
+
243
+
244
+ def findPFCF(code: str) -> dict:
245
+ """Price to Free Cash Flow Ratio(주가 대비 자유 현금 흐름 비율)계산
228
246
 
229
247
  PFCF = 시가총액 / FCF
230
248
 
@@ -232,12 +250,12 @@ def findPFCF(client, code: str) -> dict:
232
250
  https://www.investopedia.com/terms/p/pricetofreecashflow.asp
233
251
  """
234
252
  # marketcap 계산 (fcf가 억 단위라 시가총액을 억으로 나눠서 단위를 맞춰 준다)
235
- marketcap억 = get_marketcap(client, code) / 100000000
253
+ marketcap억 = get_marketcap(code) / 100000000
236
254
  if math.isnan(marketcap억):
237
255
  return {}
238
256
 
239
257
  # pfcf 계산
240
- fcf_dict = findFCF(client, code)
258
+ fcf_dict = findFCF(code)
241
259
  logger.debug(f'fcf_dict : {fcf_dict}')
242
260
  pfcf_dict = {}
243
261
  for FCFdate, FCFvalue in fcf_dict.items():
@@ -249,9 +267,14 @@ def findPFCF(client, code: str) -> dict:
249
267
  return pfcf_dict
250
268
 
251
269
 
252
- def get_marketcap(client, code: str, nan_to_zero: bool = False) -> int:
253
- c101 = C101(client, code)
270
+ def get_marketcap(code: str) -> float:
271
+ """
272
+ 시가총액(원) 반환
273
+ :param code:
274
+ :return:
275
+ """
276
+ c101 = C101(code)
254
277
  try:
255
278
  return int(c101.get_recent()['시가총액'])
256
279
  except KeyError:
257
- return 0 if nan_to_zero else math.nan
280
+ return math.nan
@@ -0,0 +1,210 @@
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
@@ -1,15 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: analyser_hj3415
3
- Version: 2.0.2
3
+ Version: 2.1.0
4
4
  Summary: Stock analyser and database processing programs
5
5
  Author-email: Hyungjin Kim <hj3415@gmail.com>
6
6
  Description-Content-Type: text/markdown
7
7
  Classifier: License :: OSI Approved :: MIT License
8
- Requires-Dist: pandas>=2.2.2
9
- Requires-Dist: pymongo>=4.8.0
10
- Requires-Dist: sqlalchemy>=2.0.31
11
- Requires-Dist: utils-hj3415>=2.0.1
12
- Requires-Dist: scraper2-hj3415>=2.0.0
8
+ Requires-Dist: utils-hj3415>=2.6.0
9
+ Requires-Dist: db-hj3415>=3.3.2
13
10
  Project-URL: Home, https://www.hyungjin.kr
14
11
 
15
12
  ### analyser-hj3415
@@ -20,12 +17,11 @@ analyser_hj3415 manage the database.
20
17
  ---
21
18
  #### Requirements
22
19
 
23
- scrapy>=2.11.2
24
20
  pandas>=2.2.2
21
+ pymongo>=4.8.0
25
22
  sqlalchemy>=2.0.31
26
- selenium>=4.22.0
27
23
  utils-hj3415>=2.0.1
28
- analyser_hj3415>=0.3.5
24
+ scraper-hj3415>=2.0.0
29
25
 
30
26
  ---
31
27
  #### API
@@ -0,0 +1,14 @@
1
+ analyser_hj3415/.DS_Store,sha256=OQfTSOHL-zjUtnNyBpNRVUJUstR4j6I7jihKDFQQmME,6148
2
+ analyser_hj3415/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ analyser_hj3415/cli.py,sha256=qzRnpDRJvQnQevSKHBpKbTsBjmSWllZjzTV4z_alg2A,4891
4
+ analyser_hj3415/run.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ analyser_hj3415/tools.py,sha256=SNsrnL5CKmKAdFkmlwgREMIkWDRi6N9LngCdhhhop3Y,13606
6
+ analyser_hj3415/trash.py,sha256=vHrv8Q61QOkcwhmWfrj_yVdsdd5MoAxs9gXMOJEjMHM,8360
7
+ analyser_hj3415/analysers/eval.py,sha256=mlHi6EPc8l8O6vKnWyX4Cz1BaeGhUpWM8gVZRNhm-JU,13299
8
+ analyser_hj3415/analysers/report.py,sha256=whggmLXl7yF-BjQ6JKgxmhILT2T4uFP-rit_BSes9xM,9189
9
+ analyser_hj3415/analysers/score.py,sha256=DoLac-PXQrA-GfkEHRD52R-8FFJD-eYLCPTg3oEkNGA,16213
10
+ analyser_hj3415-2.1.0.dist-info/entry_points.txt,sha256=dHaCM3eOAGONmxTWuRVqo9Zyq2C7J5TZmpH0PD6FW5k,103
11
+ analyser_hj3415-2.1.0.dist-info/LICENSE,sha256=QVKTp0dTnB5xG8RLgG17LwSWCKNEzYoVVM6KjoCPKc0,1079
12
+ analyser_hj3415-2.1.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
13
+ analyser_hj3415-2.1.0.dist-info/METADATA,sha256=h7E4uyJhGWiRAOVfZ-ApChWxDfm8pOi8Iu-iz1Byqpg,6417
14
+ analyser_hj3415-2.1.0.dist-info/RECORD,,
Binary file