analyser_hj3415 2.0.0__py2.py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- analyser_hj3415/.DS_Store +0 -0
- analyser_hj3415/__init__.py +0 -0
- analyser_hj3415/analyser/.DS_Store +0 -0
- analyser_hj3415/analyser/__init__.py +0 -0
- analyser_hj3415/analyser/cli.py +109 -0
- analyser_hj3415/analyser/db/.DS_Store +0 -0
- analyser_hj3415/analyser/db/__init__.py +0 -0
- analyser_hj3415/analyser/db/chk_db.py +240 -0
- analyser_hj3415/analyser/db/evaltools.py +257 -0
- analyser_hj3415/analyser/db/mongo.py +934 -0
- analyser_hj3415/analyser/eval.py +382 -0
- analyser_hj3415/analyser/report.py +218 -0
- analyser_hj3415/analyser/score.py +369 -0
- analyser_hj3415-2.0.0.dist-info/LICENSE +21 -0
- analyser_hj3415-2.0.0.dist-info/METADATA +232 -0
- analyser_hj3415-2.0.0.dist-info/RECORD +18 -0
- analyser_hj3415-2.0.0.dist-info/WHEEL +5 -0
- analyser_hj3415-2.0.0.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,369 @@
|
|
1
|
+
import math
|
2
|
+
import numpy
|
3
|
+
import pprint
|
4
|
+
import copy
|
5
|
+
from typing import Tuple
|
6
|
+
|
7
|
+
from .db import mongo
|
8
|
+
from .eval import EXPECT_EARN, red as eval_red, mil as eval_mil, blue as eval_blue, growth as eval_growth
|
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 red(client, code: str) -> Tuple[int, float]:
|
22
|
+
"""red price와 최근 주가의 괴리율 파악
|
23
|
+
|
24
|
+
양수면 주가가 고평가되어 있는 상태, 음수면 저평가
|
25
|
+
음수가 현재 주가가 싸다는 의미
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
tuple : 괴리율을 기반으로한 포인트, 주가와 red price 비교한 괴리율
|
29
|
+
"""
|
30
|
+
c101 = mongo.C101(client, code)
|
31
|
+
|
32
|
+
logger.debug(f'c101 {c101.get_recent()}')
|
33
|
+
|
34
|
+
try:
|
35
|
+
recent_price = utils.to_int(c101.get_recent()['주가'])
|
36
|
+
except KeyError:
|
37
|
+
recent_price = float('nan')
|
38
|
+
red_price = eval_red(client, code)['red_price']
|
39
|
+
|
40
|
+
logger.debug(f"recent_price : {recent_price}\tred_price : {red_price}")
|
41
|
+
|
42
|
+
try:
|
43
|
+
# 괴리율 구하는 공식이 두가지임. 어떤걸 사용해도 동일함
|
44
|
+
f1 = round((recent_price / red_price - 1) * 100, 2)
|
45
|
+
f2 = round((recent_price - red_price) / red_price * 100, 2)
|
46
|
+
logger.debug(f'f1 : {f1}, f2 : {f2}')
|
47
|
+
괴리율 = round((recent_price / red_price - 1) * 100, 2)
|
48
|
+
except ZeroDivisionError:
|
49
|
+
괴리율 = float('nan')
|
50
|
+
|
51
|
+
logger.debug(f'괴리율 : {괴리율}')
|
52
|
+
|
53
|
+
if math.isnan(괴리율) or red_price <= 0:
|
54
|
+
return 0, float('nan')
|
55
|
+
else:
|
56
|
+
try:
|
57
|
+
p = round(100*math.log10(-괴리율+31.622777)-150)
|
58
|
+
return p if p > 0 else 0, 괴리율
|
59
|
+
except ValueError:
|
60
|
+
# 괴리율+31.622777이 0이하인 경우 ValueError 발생함.
|
61
|
+
return 0, 괴리율
|
62
|
+
|
63
|
+
|
64
|
+
def mil(client, code: str) -> Tuple[int, int, int, int]:
|
65
|
+
"""
|
66
|
+
- 재무활동현금흐름이 마이너스라는 것은 배당급 지급했거나, 자사주 매입했거나, 부채를 상환한 상태임.
|
67
|
+
- 반대는 채권자로 자금을 조달했거나 신주를 발행했다는 의미
|
68
|
+
<주주수익률> - 재무활동현금흐름/시가총액 => 5%이상인가?
|
69
|
+
|
70
|
+
투하자본수익률(ROIC)가 30%이상인가
|
71
|
+
ROE(자기자본이익률) 20%이상이면 아주 우수 다른 투자이익률과 비교해볼것 10%미만이면 별로...단, 부채비율을 확인해야함.
|
72
|
+
|
73
|
+
이익지표 ...영업현금흐름이 순이익보다 많은가 - 결과값이 음수인가..
|
74
|
+
|
75
|
+
FCF는 영업현금흐름에서 자본적 지출(유·무형투자 비용)을 차감한 순수한 현금력이라 할 수 있다.
|
76
|
+
말 그대로 자유롭게(Free) 사용할 수 있는 여윳돈을 뜻한다.
|
77
|
+
잉여현금흐름이 플러스라면 미래의 투자나 채무상환에 쓸 재원이 늘어난 것이다.
|
78
|
+
CAPEX(Capital expenditures)는 미래의 이윤을 창출하기 위해 지출된 비용을 말한다.
|
79
|
+
이는 기업이 고정자산을 구매하거나, 유효수명이 당회계년도를 초과하는 기존의 고정자산에 대한 투자에 돈이 사용될 때 발생한다.
|
80
|
+
|
81
|
+
잉여현금흐름이 마이너스일때는 설비투자가 많은 시기라 주가가 약세이며 이후 설비투자 마무리되면서 주가가 상승할수 있다.
|
82
|
+
주가는 잉여현금흐름이 증가할때 상승하는 경향이 있다.
|
83
|
+
fcf = 영업현금흐름 - capex
|
84
|
+
|
85
|
+
가치지표평가
|
86
|
+
price to fcf 계산
|
87
|
+
https://www.investopedia.com/terms/p/pricetofreecashflow.asp
|
88
|
+
pcr보다 정확하게 주식의 가치를 평가할수 있음. 10배이하 추천
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
tuple: 주주수익률, 이익지표, 투자수익률, PFCF포인트
|
92
|
+
"""
|
93
|
+
mil_dict = eval_mil(client, code)
|
94
|
+
|
95
|
+
logger.debug(pprint.pformat(mil_dict, width=200))
|
96
|
+
|
97
|
+
# 주주수익률 평가
|
98
|
+
if math.isnan(mil_dict['주주수익률']):
|
99
|
+
p1 = 0
|
100
|
+
else:
|
101
|
+
주주수익률평가 = math.ceil(mil_dict['주주수익률'] - (EXPECT_EARN * 100))
|
102
|
+
p1 = 0 if 0 > 주주수익률평가 else 주주수익률평가
|
103
|
+
|
104
|
+
# 이익지표 평가
|
105
|
+
p2 = 10 if mil_dict['이익지표'] < 0 else 0
|
106
|
+
|
107
|
+
# 투자수익률 평가
|
108
|
+
MAX3 = 20
|
109
|
+
p3 = 0
|
110
|
+
roic = mil_dict['투자수익률']['ROIC']
|
111
|
+
roe = mil_dict['투자수익률']['ROE']
|
112
|
+
if math.isnan(roic) or roic <= 0:
|
113
|
+
# roic 가 비정상이라 평가할 수 없는 경우
|
114
|
+
if 10 < roe <= 20:
|
115
|
+
p3 += round(MAX3 * 0.333)
|
116
|
+
elif 20 < roe:
|
117
|
+
p3 += round(MAX3 * 0.666)
|
118
|
+
elif 0 < roic:
|
119
|
+
# roic 로 평가할 수 있는 경우
|
120
|
+
if 0 < roic <= 15:
|
121
|
+
p3 += round(MAX3 * 0.333)
|
122
|
+
elif 15 < roic <= 30:
|
123
|
+
p3 += round(MAX3 * 0.666)
|
124
|
+
elif 30 < roic:
|
125
|
+
p3 += MAX3
|
126
|
+
|
127
|
+
# PFCF 평가
|
128
|
+
pfcf_dict = mil_dict['가치지표']['PFCF']
|
129
|
+
_, pfcf = mongo.Corps.latest_value(pfcf_dict)
|
130
|
+
|
131
|
+
logger.debug(f'recent pfcf {_}, {pfcf}')
|
132
|
+
try:
|
133
|
+
p = round(-40 * math.log10(pfcf) + 40)
|
134
|
+
except ValueError:
|
135
|
+
p = 0
|
136
|
+
p4 = 0 if 0 > p else p
|
137
|
+
|
138
|
+
return p1, p2, p3, p4
|
139
|
+
|
140
|
+
|
141
|
+
def blue(client, code: str) -> Tuple[int, int, int, int, int]:
|
142
|
+
"""회사의 안정성을 보는 지표들
|
143
|
+
|
144
|
+
0을 기준으로 상태가 좋치 않을 수록 마이너스 값을 가진다.
|
145
|
+
|
146
|
+
Returns:
|
147
|
+
tuple : 유동비율, 이자보상배율, 순부채비율, 순운전자본회전율, 재고자산회전율 평가 포인트
|
148
|
+
|
149
|
+
Notes:
|
150
|
+
<유동비율>
|
151
|
+
100미만이면 주의하나 현금흐름창출력이 좋으면 괜찮을수 있다.
|
152
|
+
만약 100%이하면 유동자산에 추정영업현금흐름을 더해서 다시계산해보아 기회를 준다.
|
153
|
+
<이자보상배율>
|
154
|
+
이자보상배율 영업이익/이자비용으로 1이면 자금사정빡빡 5이상이면 양호
|
155
|
+
<순운전자금회전율>
|
156
|
+
순운전자금 => 기업활동을 하기 위해 필요한 자금 (매출채권 + 재고자산 - 매입채무)
|
157
|
+
순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
|
158
|
+
<재고자산회전율>
|
159
|
+
재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
|
160
|
+
재고자산회전율이 작아지면 재고가 쌓인다는뜻
|
161
|
+
<순부채비율>
|
162
|
+
부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
|
163
|
+
순부채 비율이 30%이상이면 좋치 않다.
|
164
|
+
"""
|
165
|
+
def _calc_point_with_std(data: dict) -> int:
|
166
|
+
"""표준편차를 통해 점수를 계산하는 내부 함수
|
167
|
+
|
168
|
+
Args:
|
169
|
+
data(dict): 재무재표상의 연/분기 딕셔너리 데이터
|
170
|
+
"""
|
171
|
+
NEG_MAX = -5
|
172
|
+
d_values = [i for i in data.values() if not math.isnan(i)]
|
173
|
+
logger.debug(f'd_values : {d_values}')
|
174
|
+
if len(d_values) == 0:
|
175
|
+
p = NEG_MAX
|
176
|
+
else:
|
177
|
+
std = numpy.std(d_values)
|
178
|
+
# 표준편차가 작을수록 데이터의 변환가 적다는 의미임.
|
179
|
+
logger.debug(f'표준편차 : {std}')
|
180
|
+
p = NEG_MAX if float(std) > -NEG_MAX else -math.floor(float(std))
|
181
|
+
|
182
|
+
return int(p)
|
183
|
+
|
184
|
+
c104y = mongo.C104(client, code, 'c104y')
|
185
|
+
|
186
|
+
blue_dict = eval_blue(client, code)
|
187
|
+
|
188
|
+
logger.debug(pprint.pformat(blue_dict, width=200))
|
189
|
+
|
190
|
+
def 유동비율평가(유동비율: float) -> int:
|
191
|
+
# 채점은 0을 기준으로 마이너스 해간다. 즉 0이 제일 좋은 상태임.
|
192
|
+
# 유동비율 평가 - 100 이하는 문제 있음
|
193
|
+
NEG_MAX = -10
|
194
|
+
if math.isnan(유동비율) or 유동비율 <= 0:
|
195
|
+
p = NEG_MAX
|
196
|
+
elif math.isinf(유동비율):
|
197
|
+
p = 0
|
198
|
+
else:
|
199
|
+
p = 0 if 100 < round(유동비율) else NEG_MAX + round(유동비율/10)
|
200
|
+
logger.debug(f'유동비율평가 point : {p}')
|
201
|
+
return int(p)
|
202
|
+
|
203
|
+
p1 = 유동비율평가(blue_dict['유동비율'])
|
204
|
+
|
205
|
+
def 이자보상배율평가(이자보상배율: tuple) -> int:
|
206
|
+
# 이자보상배율평가 : 1이면 자금사정 빡빡 5이상이면 양호
|
207
|
+
NEG_MAX = -5
|
208
|
+
최근이자보상배율q, dict_y = 이자보상배율
|
209
|
+
|
210
|
+
if math.isnan(최근이자보상배율q) or 최근이자보상배율q <= 1:
|
211
|
+
# 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
|
212
|
+
|
213
|
+
_, 최근이자보상배율y = mongo.Corps.latest_value(dict_y)
|
214
|
+
c104y.page = 'c104y'
|
215
|
+
_, 전년대비 = c104y.find_증감율(title='이자보상배율').popitem()
|
216
|
+
|
217
|
+
if math.isnan(최근이자보상배율y) or 최근이자보상배율y <= 1 or math.isnan(전년대비) or 전년대비 < 0:
|
218
|
+
p = NEG_MAX
|
219
|
+
else:
|
220
|
+
p = 0 if 5 < 최근이자보상배율y else NEG_MAX + round(최근이자보상배율y)
|
221
|
+
else:
|
222
|
+
p = 0 if 5 < 최근이자보상배율q else NEG_MAX + round(최근이자보상배율q)
|
223
|
+
logger.debug(f'이자보상배율평가 point : {p}')
|
224
|
+
return int(p)
|
225
|
+
|
226
|
+
p2 = 이자보상배율평가(blue_dict['이자보상배율'])
|
227
|
+
|
228
|
+
def 순부채비율평가(순부채비율: tuple) -> int:
|
229
|
+
# 부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
|
230
|
+
# 순부채 비율이 30%이상이면 좋치 않다.
|
231
|
+
NEG_MAX = -5
|
232
|
+
최근순부채비율q, dict_y = 순부채비율
|
233
|
+
|
234
|
+
if math.isnan(최근순부채비율q) or 최근순부채비율q >= 80:
|
235
|
+
# 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
|
236
|
+
_, 최근순부채비율y = mongo.Corps.latest_value(dict_y)
|
237
|
+
c104y.page = 'c104y'
|
238
|
+
_, 전년대비 = c104y.find_증감율(title='순부채비율').popitem()
|
239
|
+
if math.isnan(최근순부채비율y) or 최근순부채비율y >= 80 or math.isnan(전년대비) or 전년대비 > 0:
|
240
|
+
p = NEG_MAX
|
241
|
+
else:
|
242
|
+
p = 0 if 최근순부채비율y < 30 else round((30 - 최근순부채비율y) / 10)
|
243
|
+
else:
|
244
|
+
p = 0 if 최근순부채비율q < 30 else round((30 - 최근순부채비율q) / 10)
|
245
|
+
logger.debug(f'순부채비율평가 point : {p}')
|
246
|
+
return int(p)
|
247
|
+
|
248
|
+
p3 = 순부채비율평가(blue_dict['순부채비율'])
|
249
|
+
|
250
|
+
def 순운전자본회전율평가(순운전자본회전율: tuple) -> int:
|
251
|
+
# 순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
|
252
|
+
_, dict_y = 순운전자본회전율
|
253
|
+
p = _calc_point_with_std(data=dict_y)
|
254
|
+
logger.debug(f'순운전자본회전율평가 point : {p}')
|
255
|
+
return p
|
256
|
+
|
257
|
+
p4 = 순운전자본회전율평가(blue_dict['순운전자본회전율'])
|
258
|
+
|
259
|
+
def 재고자산회전율평가(재고자산회전율: tuple) -> int:
|
260
|
+
# 재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
|
261
|
+
# 재고자산회전율이 작아지면 재고가 쌓인다는뜻
|
262
|
+
_, dict_y = 재고자산회전율
|
263
|
+
p = _calc_point_with_std(data=dict_y)
|
264
|
+
logger.debug(f'재고자산회전율평가 point : {p}')
|
265
|
+
return p
|
266
|
+
|
267
|
+
p5 = 재고자산회전율평가(blue_dict['재고자산회전율'])
|
268
|
+
|
269
|
+
return p1, p2, p3, p4, p5
|
270
|
+
|
271
|
+
|
272
|
+
def growth(client, code: str) -> Tuple[int, int]:
|
273
|
+
"""회사의 성장성을 보는 지표들
|
274
|
+
|
275
|
+
<매출액>
|
276
|
+
매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
|
277
|
+
<영업이익률>
|
278
|
+
영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
|
279
|
+
|
280
|
+
Returns:
|
281
|
+
tuple : 매출액증가율, 영업이익률 평가 포인트
|
282
|
+
"""
|
283
|
+
growth_dict = eval_growth(client, code)
|
284
|
+
|
285
|
+
logger.debug(pprint.pformat(growth_dict, width=200))
|
286
|
+
|
287
|
+
def 매출액증가율평가(매출액증가율: tuple) -> int:
|
288
|
+
# 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
|
289
|
+
MAX = 20
|
290
|
+
최근매출액증가율q, dict_y = 매출액증가율
|
291
|
+
_, 최근매출액증가율y = mongo.Corps.latest_value(dict_y)
|
292
|
+
|
293
|
+
# 최근 자료가 성장하는 중인지 판단
|
294
|
+
if math.isnan(최근매출액증가율q):
|
295
|
+
최근매출액증가율q = 최근매출액증가율y
|
296
|
+
|
297
|
+
sp1 = 0
|
298
|
+
if math.isnan(최근매출액증가율y):
|
299
|
+
pass
|
300
|
+
elif 0 < 최근매출액증가율y and 0 < 최근매출액증가율q:
|
301
|
+
# 최근에 마이너스 성장이 아닌경우 MAX/10점 보너스
|
302
|
+
sp1 += MAX / 10
|
303
|
+
if 최근매출액증가율y < 최근매출액증가율q:
|
304
|
+
# 최근에 이전보다 더 성장중이면 MAX/10점 보너스
|
305
|
+
sp1 += MAX / 10
|
306
|
+
# 나머지는 성장률 기반 점수 배정
|
307
|
+
sp1 += MAX / 2 if 최근매출액증가율q > MAX else 최근매출액증가율q / 2
|
308
|
+
elif 최근매출액증가율y <= 0 < 최근매출액증가율q:
|
309
|
+
# 직전에 마이너스였다가 최근에 회복된 경우 MAX/10점 보너스
|
310
|
+
sp1 += MAX / 10
|
311
|
+
# 나머지는 성장률 기반 점수 배정
|
312
|
+
sp1 += MAX / 2 if 최근매출액증가율q > MAX else 최근매출액증가율q / 2
|
313
|
+
else:
|
314
|
+
# 최근 자료가 마이너스인 경우 마이너스만큼 점수를 차감한다.
|
315
|
+
sp1 += -(MAX / 2) if 최근매출액증가율q < -MAX else 최근매출액증가율q / 2
|
316
|
+
|
317
|
+
# 평균매출액증가율 구하기
|
318
|
+
d_values = [i for i in dict_y.values() if not math.isnan(i)]
|
319
|
+
logger.debug(f'평균매출액증가율 d_values : {d_values}')
|
320
|
+
|
321
|
+
if len(d_values) == 0:
|
322
|
+
평균매출액증가율 = float('nan')
|
323
|
+
else:
|
324
|
+
평균매출액증가율 = float(numpy.mean(d_values))
|
325
|
+
logger.debug(f'평균 : {평균매출액증가율}')
|
326
|
+
|
327
|
+
sp2 = 0
|
328
|
+
if math.isnan(평균매출액증가율):
|
329
|
+
sp2 += -(MAX/2)
|
330
|
+
elif 평균매출액증가율 <= 0:
|
331
|
+
# 평균매출액증가율이 마이너스인 경우 마이너스만큼 점수를 차감한다.
|
332
|
+
sp2 += -(MAX / 2) if 평균매출액증가율 < -MAX else 평균매출액증가율 / 2
|
333
|
+
else:
|
334
|
+
sp2 = MAX / 2 if 평균매출액증가율 > MAX else 평균매출액증가율 / 2
|
335
|
+
|
336
|
+
logger.debug(f'매출액증가율평가 point : {sp1 + sp2}')
|
337
|
+
|
338
|
+
return int(sp1 + sp2)
|
339
|
+
|
340
|
+
p1 = 매출액증가율평가(growth_dict['매출액증가율'])
|
341
|
+
|
342
|
+
def 영업이익률평가(영업이익률: dict) -> int:
|
343
|
+
# 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
|
344
|
+
영업이익률 = copy.deepcopy(영업이익률)
|
345
|
+
name = mongo.Corps.get_name(client, code)
|
346
|
+
|
347
|
+
p = 0
|
348
|
+
try:
|
349
|
+
myprofit = utils.to_float(영업이익률.pop(name))
|
350
|
+
except KeyError:
|
351
|
+
logger.warning(f'{name} 영업이익률 does not exist.')
|
352
|
+
return 0
|
353
|
+
logger.debug(f'종목영업이익률 : {myprofit}')
|
354
|
+
|
355
|
+
for profit in 영업이익률.values():
|
356
|
+
profit = utils.to_float(profit)
|
357
|
+
if math.isnan(profit):
|
358
|
+
continue
|
359
|
+
elif myprofit > profit:
|
360
|
+
p += 1
|
361
|
+
else:
|
362
|
+
continue
|
363
|
+
|
364
|
+
logger.debug(f'영업이익률평가 point : {p}')
|
365
|
+
return p
|
366
|
+
|
367
|
+
p2 = 영업이익률평가(growth_dict['영업이익률'])
|
368
|
+
|
369
|
+
return p1, p2
|
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Hyungjin Kim
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
@@ -0,0 +1,232 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: analyser_hj3415
|
3
|
+
Version: 2.0.0
|
4
|
+
Summary: Stock analyser and database processing programs
|
5
|
+
Author-email: Hyungjin Kim <hj3415@gmail.com>
|
6
|
+
Description-Content-Type: text/markdown
|
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>=0.4.0
|
13
|
+
Project-URL: Home, https://www.hyungjin.kr
|
14
|
+
|
15
|
+
### analyser-hj3415
|
16
|
+
|
17
|
+
#### Introduction
|
18
|
+
analyser_hj3415 manage the database.
|
19
|
+
|
20
|
+
---
|
21
|
+
#### Requirements
|
22
|
+
|
23
|
+
scrapy>=2.11.2
|
24
|
+
pandas>=2.2.2
|
25
|
+
sqlalchemy>=2.0.31
|
26
|
+
selenium>=4.22.0
|
27
|
+
utils-hj3415>=2.0.1
|
28
|
+
analyser_hj3415>=0.3.5
|
29
|
+
|
30
|
+
---
|
31
|
+
#### API
|
32
|
+
|
33
|
+
---
|
34
|
+
#### Install
|
35
|
+
|
36
|
+
|
37
|
+
---
|
38
|
+
#### Composition
|
39
|
+
analyser_hj3415 모듈은 세가지 파트로 구성되어 있습니다.
|
40
|
+
|
41
|
+
1. setting 모듈
|
42
|
+
setting 모듈은 데이터베이스를 활성화하고 주소를 설정하는 역할을 합니다.
|
43
|
+
데이터베이스의 주소와 활성화 여부를 파일에 저장합니다.
|
44
|
+
|
45
|
+
```python
|
46
|
+
from analyser_hj3415 import setting
|
47
|
+
|
48
|
+
# 현재 데이터 베이스 상태를 DbSetting 클래스 형식으로 반환한다.
|
49
|
+
db_setting = setting.load_df()
|
50
|
+
|
51
|
+
# 현재 데이터베이스 상태 출력
|
52
|
+
print(db_setting)
|
53
|
+
|
54
|
+
# 몽고db 주소 변경 (2가지 방식)
|
55
|
+
setting.chg_mongo_addr('mongodb://192.168.0.173:27017')
|
56
|
+
db_setting.mongo_addr = 'mongodb://192.168.0.173:27017'
|
57
|
+
|
58
|
+
# sqlite3 주소 변경 (2가지 방식)
|
59
|
+
setting.chg_sqlite3_path('/home/hj3415/Stock/_db')
|
60
|
+
db_setting.sqlite3_path = '/home/hj3415/Stock/_db'
|
61
|
+
|
62
|
+
# 데이터베이스를 기본값으로 설정합니다.
|
63
|
+
# DEF_MONGO_ADDR = 'mongodb://localhost:27017'
|
64
|
+
# DEF_WIN_SQLITE3_PATH = 'C:\\_db'
|
65
|
+
# DEF_LINUX_SQLITE3_PATH = '/home/hj3415/Stock/_db'
|
66
|
+
setting.set_default()
|
67
|
+
|
68
|
+
# 각 데이터베이스 사용 설정
|
69
|
+
setting.turn_on_mongo()
|
70
|
+
setting.turn_off_mongo()
|
71
|
+
setting.turn_off_sqlite3()
|
72
|
+
setting.turn_on_sqlite3()
|
73
|
+
```
|
74
|
+
|
75
|
+
2. mongo 모듈
|
76
|
+
몽고db를 데이터베이스로 사용할 경우를 위한 함수들의 모듈입니다.
|
77
|
+
현재는 몽고db를 비활성화 할 경우 올바로 작동하지 않기 때문에 디폴트 데이터베이스 입니다.
|
78
|
+
|
79
|
+
1) Base 클래스
|
80
|
+
|
81
|
+
모든 데이터베이스 클래스의 기반 클래스로 실제 직접 사용하지 않음.
|
82
|
+
|
83
|
+
```python
|
84
|
+
from analyser_hj3415.mongo import Base
|
85
|
+
base = Base(db='mi', col='kospi')
|
86
|
+
|
87
|
+
# db 주소를 변경함. 단 파일에 저장되는 것이 아니라 클래스 내부에서 일시적으로 설정하는 것임
|
88
|
+
base.chg_addr('mongodb://192.168.0.173:27017')
|
89
|
+
|
90
|
+
# 현재 설정된 db 주소, db 명, 컬렉션을 반환함.
|
91
|
+
base.get_status()
|
92
|
+
# ('mongodb://192.168.0.173:27017', 'mi', 'kospi')
|
93
|
+
|
94
|
+
# 데이터 베이스 관리 함수
|
95
|
+
base.get_all_db()
|
96
|
+
```
|
97
|
+
|
98
|
+
2 - 1) Corps 클래스
|
99
|
+
|
100
|
+
DB 내에서 종목에 관련된 기반클래스로 db명은 6자리 숫자 코드명임.
|
101
|
+
|
102
|
+
```python
|
103
|
+
from analyser_hj3415.mongo import Corps
|
104
|
+
|
105
|
+
corps = Corps(code='005930', page='c101')
|
106
|
+
|
107
|
+
# 코드를 변경함. 6자리 숫자인지 확인 후 설정함.
|
108
|
+
corps.chg_code('005490')
|
109
|
+
|
110
|
+
# 페이지를 변경함. 페이지명의 유효성 확인 후 설정함.
|
111
|
+
# ('c101', 'c104y', 'c104q', 'c106', 'c108', 'c103손익계산서q', 'c103재무상태표q', 'c103현금흐름표q', 'c103손익계산서y', 'c103재무상태표y', 'c103현금흐름표y', 'dart')
|
112
|
+
corps.chg_page(page='c108')
|
113
|
+
|
114
|
+
# 데이터 베이스 관리 함수
|
115
|
+
corps.get_all_codes()
|
116
|
+
corps.del_all_codes()
|
117
|
+
corps.drop_corp(code='005930')
|
118
|
+
corps.get_all_pages()
|
119
|
+
corps.drop_all_pages(code='005930')
|
120
|
+
corps.drop_page(code='005930', page='c101')
|
121
|
+
corps.get_all_item()
|
122
|
+
```
|
123
|
+
|
124
|
+
2 - 2) MI 클래스
|
125
|
+
|
126
|
+
DB 내에서 Market index 관련 클래스
|
127
|
+
|
128
|
+
```python
|
129
|
+
from analyser_hj3415.mongo import MI
|
130
|
+
mi = MI(index='kospi')
|
131
|
+
|
132
|
+
# 인덱스를 변경함. 인덱스명의 유효성 확인 후 설정
|
133
|
+
# ('aud', 'chf', 'gbond3y', 'gold', 'silver', 'kosdaq', 'kospi', 'sp500', 'usdkrw', 'wti', 'avgper', 'yieldgap', 'usdidx')
|
134
|
+
mi.chg_index(index='gold')
|
135
|
+
|
136
|
+
# 저장된 가장 최근 값 반환
|
137
|
+
mi.get_recent()
|
138
|
+
|
139
|
+
# 데이터를 저장함.
|
140
|
+
mi.save(mi_dict={'date': '2021.07.21', 'value': '1154.50'})
|
141
|
+
|
142
|
+
# 데이터 베이스 관리 함수
|
143
|
+
mi.get_all_indexes()
|
144
|
+
mi.drop_all_indexes()
|
145
|
+
mi.drop_index(index='silver')
|
146
|
+
mi.get_all_item()
|
147
|
+
```
|
148
|
+
|
149
|
+
2 - 3) DartByDate 클래스
|
150
|
+
|
151
|
+
dart_hj3415의 dart 모듈에서 dart 데이터프레임을 추출하면 각 날짜별 컬렉션으로 저장하는 클래스
|
152
|
+
|
153
|
+
```python
|
154
|
+
from dart_hj3415 import dart
|
155
|
+
from analyser_hj3415.mongo import DartByDate
|
156
|
+
|
157
|
+
date = '20210812'
|
158
|
+
dart_db = DartByDate(date=date)
|
159
|
+
|
160
|
+
# 오늘 날짜의 dart 데이터프레임을 추출하여 데이터베이스에 저장
|
161
|
+
df = dart.get_df(edate=date)
|
162
|
+
dart_db.save(df)
|
163
|
+
|
164
|
+
# 공시 데이터를 데이터프레임으로 반환한다.
|
165
|
+
dart_db.get_data()
|
166
|
+
dart_db.get_data(title='임원ㆍ주요주주특정증권등소유상황보고서')
|
167
|
+
```
|
168
|
+
|
169
|
+
2 - 4) EvalByDate 클래스
|
170
|
+
|
171
|
+
eval_hj3415의 eval 모듈에서 eval 데이터프레임을 추출하여 저장하거나 불러올때 사용.
|
172
|
+
(실제로 eval_hj3415.eval.make_today_eval_df()에서 오늘자 데이터프레임을 항상 저장한다)
|
173
|
+
|
174
|
+
```python
|
175
|
+
import pandas as pd
|
176
|
+
import datetime
|
177
|
+
from analyser_hj3415.mongo import EvalByDate
|
178
|
+
|
179
|
+
today_str = datetime.datetime.today().strftime('%Y%m%d')
|
180
|
+
eval_db = EvalByDate(date=today_str)
|
181
|
+
|
182
|
+
# 오늘 날짜의 dart 데이터프레임을 추출하여 데이터베이스에 저장
|
183
|
+
eval_db.save(pd.DataFrame())
|
184
|
+
|
185
|
+
# 공시 데이터를 데이터프레임으로 반환한다.
|
186
|
+
eval_db.get_data()
|
187
|
+
```
|
188
|
+
|
189
|
+
2 - 5) Noti 클래스
|
190
|
+
|
191
|
+
dart_hj3415의 analysis 모듈에서 공시를 분석하여 의미있는 공시를 노티하고 노티한 기록을 저장하는 클래스
|
192
|
+
|
193
|
+
```python
|
194
|
+
from analyser_hj3415.mongo import Noti
|
195
|
+
noti_db = Noti()
|
196
|
+
|
197
|
+
# 저장이 필요한 노티 데이터를 딕셔너리로 전달하여 데이터베이스에 저장
|
198
|
+
data = {'code': '005930',
|
199
|
+
'rcept_no': '20210514000624',
|
200
|
+
'rcept_dt': '20210514',
|
201
|
+
'report_nm': '임원ㆍ주요주주특정증권등소유상황보고서',
|
202
|
+
'point': 2,
|
203
|
+
'text': '등기임원이 1.0억 이상 구매하지 않음.'}
|
204
|
+
noti_db.save(noti_dict=data)
|
205
|
+
|
206
|
+
# 오래된 노티 데이터를 정리하는 함수
|
207
|
+
noti_db.cleaning_data(days_ago=15)
|
208
|
+
```
|
209
|
+
|
210
|
+
3) Corps
|
211
|
+
|
212
|
+
C101 페이지 관리 클래스
|
213
|
+
|
214
|
+
```python
|
215
|
+
from analyser_hj3415.mongo import C101
|
216
|
+
c101 = C101(code='005930')
|
217
|
+
...
|
218
|
+
```
|
219
|
+
|
220
|
+
구현 클래스는 C101, C108, C106, C103, C104
|
221
|
+
|
222
|
+
3. sqlite 모듈
|
223
|
+
sqlite3를 데이테베이스로 사용할 경우를 위한 함수들의 모듈입니다.
|
224
|
+
현재 sqlite3는 사용하지 않기 때문에 작동하지 않습니다.
|
225
|
+
|
226
|
+
```python
|
227
|
+
from analyser_hj3415 import sqlite
|
228
|
+
|
229
|
+
```
|
230
|
+
---
|
231
|
+
|
232
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
analyser_hj3415/.DS_Store,sha256=ru32hJTZXnU4wX_07F4wd6nuiLkH9bA3gTALaIXBamM,6148
|
2
|
+
analyser_hj3415/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
analyser_hj3415/analyser/.DS_Store,sha256=2TtZ8BUuHZ-ELhwey6b6MNgQgqCc4aieYOQOsBQgzhM,6148
|
4
|
+
analyser_hj3415/analyser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
analyser_hj3415/analyser/cli.py,sha256=qzRnpDRJvQnQevSKHBpKbTsBjmSWllZjzTV4z_alg2A,4891
|
6
|
+
analyser_hj3415/analyser/eval.py,sha256=v86gNZ722GYHyQKvm1NxHLC8r9mzSx1JgKkznSCyc6Q,16951
|
7
|
+
analyser_hj3415/analyser/report.py,sha256=Sr14o6edWDwFZeR_cjE7zWw3K02Rv1nPKkgCUP-3Ah8,10917
|
8
|
+
analyser_hj3415/analyser/score.py,sha256=hQCV_i_fsXKOYVKDZ4QIpf1SwFh8iHDrQAVFuM9x4os,16693
|
9
|
+
analyser_hj3415/analyser/db/.DS_Store,sha256=1lFlJ5EFymdzGAUAaI30vcaaLHt3F1LwpG7xILf9jsM,6148
|
10
|
+
analyser_hj3415/analyser/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
+
analyser_hj3415/analyser/db/chk_db.py,sha256=NguwgG3TP9Lassh2eJ101AIHGVoWZCxUcESdPt19swE,10115
|
12
|
+
analyser_hj3415/analyser/db/evaltools.py,sha256=aSOnMG2uKp5kTv-UPIgnpfeeePNhScIXZVu50mioAnY,11867
|
13
|
+
analyser_hj3415/analyser/db/mongo.py,sha256=wc8GPwXhsibKWHERflVrOUCSJNmRlKKe0MzWZ6KQsfs,35309
|
14
|
+
analyser_hj3415-2.0.0.dist-info/entry_points.txt,sha256=dHaCM3eOAGONmxTWuRVqo9Zyq2C7J5TZmpH0PD6FW5k,103
|
15
|
+
analyser_hj3415-2.0.0.dist-info/LICENSE,sha256=QVKTp0dTnB5xG8RLgG17LwSWCKNEzYoVVM6KjoCPKc0,1079
|
16
|
+
analyser_hj3415-2.0.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
|
17
|
+
analyser_hj3415-2.0.0.dist-info/METADATA,sha256=_Uuz79IyD9q3T0INP85Aq5pJSGzTBLJpPseMTn-mieQ,6534
|
18
|
+
analyser_hj3415-2.0.0.dist-info/RECORD,,
|