analyser_hj3415 2.0.1__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.
- analyser_hj3415/.DS_Store +0 -0
- analyser_hj3415/analysers/eval.py +289 -0
- analyser_hj3415/{report.py → analysers/report.py} +23 -59
- analyser_hj3415/{score.py → analysers/score.py} +56 -61
- analyser_hj3415/{db/evaltools.py → tools.py} +102 -79
- analyser_hj3415/trash.py +210 -0
- {analyser_hj3415-2.0.1.dist-info → analyser_hj3415-2.1.0.dist-info}/METADATA +5 -9
- analyser_hj3415-2.1.0.dist-info/RECORD +14 -0
- analyser_hj3415/db/.DS_Store +0 -0
- analyser_hj3415/db/chk_db.py +0 -240
- analyser_hj3415/db/mongo.py +0 -934
- analyser_hj3415/eval.py +0 -382
- analyser_hj3415-2.0.1.dist-info/RECORD +0 -16
- /analyser_hj3415/{db/__init__.py → run.py} +0 -0
- {analyser_hj3415-2.0.1.dist-info → analyser_hj3415-2.1.0.dist-info}/LICENSE +0 -0
- {analyser_hj3415-2.0.1.dist-info → analyser_hj3415-2.1.0.dist-info}/WHEEL +0 -0
- {analyser_hj3415-2.0.1.dist-info → analyser_hj3415-2.1.0.dist-info}/entry_points.txt +0 -0
analyser_hj3415/.DS_Store
CHANGED
Binary file
|
@@ -0,0 +1,289 @@
|
|
1
|
+
"""red, mil, blue 3가지 분야에서 자료를 계산하여 리턴하는 함수 모음
|
2
|
+
"""
|
3
|
+
import math
|
4
|
+
|
5
|
+
from analyser_hj3415 import tools
|
6
|
+
from utils_hj3415 import utils
|
7
|
+
from db_hj3415 import myredis
|
8
|
+
|
9
|
+
import logging
|
10
|
+
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
formatter = logging.Formatter('%(levelname)s: [%(name)s] %(message)s')
|
13
|
+
ch = logging.StreamHandler()
|
14
|
+
ch.setFormatter(formatter)
|
15
|
+
logger.addHandler(ch)
|
16
|
+
logger.setLevel(logging.WARNING)
|
17
|
+
|
18
|
+
|
19
|
+
# 주식을 통한 기대수익률 - 금리가 3%일 경우 두배인 6% 정도로 잡는다.
|
20
|
+
EXPECT_EARN = 0.06
|
21
|
+
|
22
|
+
|
23
|
+
def red(code: str) -> dict:
|
24
|
+
"""
|
25
|
+
리턴값
|
26
|
+
{
|
27
|
+
'red_price': red_price,
|
28
|
+
'사업가치': 사업가치,
|
29
|
+
'재산가치': 재산가치,
|
30
|
+
'부채평가': 부채평가,
|
31
|
+
'발행주식수': 발행주식수,
|
32
|
+
'date': [각 유효한 값의 년월값 리스트(ex- 2020/09)],
|
33
|
+
}
|
34
|
+
"""
|
35
|
+
c103q = myredis.C103(code, 'c103재무상태표q')
|
36
|
+
|
37
|
+
d1, 지배주주당기순이익 = tools.calc당기순이익(code)
|
38
|
+
d2, 유동자산 = tools.calc유동자산(code)
|
39
|
+
d3, 유동부채 = tools.calc유동부채(code)
|
40
|
+
d4, 부채평가 = tools.calc비유동부채(code)
|
41
|
+
|
42
|
+
c103q.page = 'c103재무상태표q'
|
43
|
+
d5, 투자자산 = c103q.latest_value_pop2('투자자산')
|
44
|
+
d6, 투자부동산 = c103q.latest_value_pop2('투자부동산')
|
45
|
+
|
46
|
+
# 사업가치 계산 - 지배주주지분 당기순이익 / 기대수익률
|
47
|
+
사업가치 = round(utils.nan_to_zero(지배주주당기순이익) / EXPECT_EARN, 2)
|
48
|
+
|
49
|
+
# 재산가치 계산 - 유동자산 - (유동부채*1.2) + 고정자산중 투자자산
|
50
|
+
재산가치 = round(유동자산 - (유동부채 * 1.2) + utils.nan_to_zero(투자자산) + utils.nan_to_zero(투자부동산), 2)
|
51
|
+
|
52
|
+
_, 발행주식수 = c103q.latest_value_pop2('발행주식수')
|
53
|
+
if math.isnan(발행주식수):
|
54
|
+
발행주식수 = utils.to_int(myredis.C101(code).get_recent().get('발행주식'))
|
55
|
+
else:
|
56
|
+
발행주식수 = 발행주식수 * 1000
|
57
|
+
|
58
|
+
try:
|
59
|
+
red_price = round(((사업가치 + 재산가치 - 부채평가) * 100000000) / 발행주식수)
|
60
|
+
except (ZeroDivisionError, ValueError) as e:
|
61
|
+
red_price = math.nan
|
62
|
+
|
63
|
+
logger.debug(f'Red Price : {red_price}원')
|
64
|
+
return {
|
65
|
+
'red_price': red_price,
|
66
|
+
'사업가치': 사업가치,
|
67
|
+
'재산가치': 재산가치,
|
68
|
+
'부채평가': 부채평가,
|
69
|
+
'발행주식수': 발행주식수,
|
70
|
+
'date': tools.set_data(d1, d2, d3, d4, d5, d6), # ''값을 제거하고 리스트로 바꾼다.
|
71
|
+
}
|
72
|
+
|
73
|
+
|
74
|
+
def mil(code: str) -> dict:
|
75
|
+
"""
|
76
|
+
리턴값
|
77
|
+
{
|
78
|
+
'주주수익률': 주주수익률,
|
79
|
+
'이익지표': 이익지표,
|
80
|
+
'투자수익률': {'ROIC': roic, 'ROE': roe , 'ROE106': {}},
|
81
|
+
'가치지표': {'FCF': fcf_dict, 'PFCF': pfcf_dict, 'PCR': pcr_dict},
|
82
|
+
'date': [각 유효한 값의 년월값 리스트(ex- 2020/09)],
|
83
|
+
}
|
84
|
+
|
85
|
+
- 재무활동현금흐름이 마이너스라는 것은 배당급 지급했거나, 자사주 매입했거나, 부채를 상환한 상태임.
|
86
|
+
- 반대는 채권자로 자금을 조달했거나 신주를 발행했다는 의미
|
87
|
+
<주주수익률> - 재무활동현금흐름/시가총액 => 5%이상인가?
|
88
|
+
|
89
|
+
투하자본수익률(ROIC)가 30%이상인가
|
90
|
+
ROE(자기자본이익률) 20%이상이면 아주 우수 다른 투자이익률과 비교해볼것 10%미만이면 별로...단, 부채비율을 확인해야함.
|
91
|
+
|
92
|
+
이익지표 ...영업현금흐름이 순이익보다 많은가 - 결과값이 음수인가..
|
93
|
+
|
94
|
+
FCF는 영업현금흐름에서 자본적 지출(유·무형투자 비용)을 차감한 순수한 현금력이라 할 수 있다.
|
95
|
+
말 그대로 자유롭게(Free) 사용할 수 있는 여윳돈을 뜻한다.
|
96
|
+
잉여현금흐름이 플러스라면 미래의 투자나 채무상환에 쓸 재원이 늘어난 것이다.
|
97
|
+
CAPEX(Capital expenditures)는 미래의 이윤을 창출하기 위해 지출된 비용을 말한다.
|
98
|
+
이는 기업이 고정자산을 구매하거나, 유효수명이 당회계년도를 초과하는 기존의 고정자산에 대한 투자에 돈이 사용될 때 발생한다.
|
99
|
+
|
100
|
+
잉여현금흐름이 마이너스일때는 설비투자가 많은 시기라 주가가 약세이며 이후 설비투자 마무리되면서 주가가 상승할수 있다.
|
101
|
+
주가는 잉여현금흐름이 증가할때 상승하는 경향이 있다.
|
102
|
+
fcf = 영업현금흐름 - capex
|
103
|
+
|
104
|
+
가치지표평가
|
105
|
+
price to fcf 계산
|
106
|
+
https://www.investopedia.com/terms/p/pricetofreecashflow.asp
|
107
|
+
pcr보다 정확하게 주식의 가치를 평가할수 있음. 10배이하 추천
|
108
|
+
"""
|
109
|
+
c103q = myredis.C103(code, 'c103현금흐름표q')
|
110
|
+
c104q = myredis.C104(code, 'c104q')
|
111
|
+
c106q = myredis.C106(code, 'c106q')
|
112
|
+
|
113
|
+
marketcap억 = tools.get_marketcap(code) / 100000000
|
114
|
+
logger.debug(f'{code} market cap: {marketcap억}')
|
115
|
+
fcf_dict = tools.findFCF(code)
|
116
|
+
pfcf_dict = tools.findPFCF(code)
|
117
|
+
d1, 지배주주당기순이익 = tools.calc당기순이익(code)
|
118
|
+
|
119
|
+
d2, 재무활동현금흐름 = c103q.sum_recent_4q('재무활동으로인한현금흐름')
|
120
|
+
d3, 영업활동현금흐름 = c103q.sum_recent_4q('영업활동으로인한현금흐름')
|
121
|
+
|
122
|
+
d4, roic = c104q.sum_recent_4q('ROIC')
|
123
|
+
d5, roe = c104q.latest_value_pop2('ROE')
|
124
|
+
roe106 = c106q.find('ROE')
|
125
|
+
|
126
|
+
_, pcr_dict = c104q.find_without_yoy('PCR')
|
127
|
+
|
128
|
+
try:
|
129
|
+
주주수익률 = round((재무활동현금흐름 / marketcap억 * -100), 2)
|
130
|
+
이익지표 = round((지배주주당기순이익 - 영업활동현금흐름) / marketcap억, 5)
|
131
|
+
except ZeroDivisionError:
|
132
|
+
주주수익률 = math.nan
|
133
|
+
이익지표 = math.nan
|
134
|
+
|
135
|
+
if math.isnan(주주수익률) or math.isnan(이익지표):
|
136
|
+
logger.warning(f'주주수익률: {주주수익률} 이익지표: {이익지표}')
|
137
|
+
logger.warning(f'재무활동현금흐름: {재무활동현금흐름} / 지배주주당기순이익: {지배주주당기순이익} / 영업활동현금흐름: {영업활동현금흐름}')
|
138
|
+
|
139
|
+
logger.debug(f'{code} fcf_dict : {fcf_dict}')
|
140
|
+
logger.debug(f"{code} market_cap : {marketcap억}")
|
141
|
+
logger.debug(f'{code} pfcf_dict : {pfcf_dict}')
|
142
|
+
logger.debug(f'{code} pcr_dict : {pcr_dict}')
|
143
|
+
|
144
|
+
return {
|
145
|
+
'주주수익률': 주주수익률,
|
146
|
+
'이익지표': 이익지표,
|
147
|
+
'투자수익률': {'ROIC': roic, 'ROE': roe, 'ROE106': roe106},
|
148
|
+
'가치지표': {'FCF': fcf_dict, 'PFCF': pfcf_dict, 'PCR': pcr_dict},
|
149
|
+
'date': tools.set_data(d1, d2, d3, d4, d5),
|
150
|
+
}
|
151
|
+
|
152
|
+
|
153
|
+
def blue(code: str) -> dict:
|
154
|
+
"""
|
155
|
+
리턴값
|
156
|
+
{
|
157
|
+
'date': [각 유효한 값의 최근분기 값 리스트(ex- 2020/09)],
|
158
|
+
'순부채비율': (29.99, {'2018/12': 19.45, '2019/12': 19.52, '2020/12': 12.07, '2021/12': 82.2, '2022/12': 29.99, '2023/12': nan}),
|
159
|
+
'순운전자본회전율': (1.04, {'2018/12': 21.91, '2019/12': 23.12, '2020/12': 5.88, '2021/12': 5.6, '2022/12': 6.04, '2023/12': nan}),
|
160
|
+
'유동비율': 64.29,
|
161
|
+
'이자보상배율': (-3.64, {'2018/12': 4.01, '2019/12': 1.3, '2020/12': -5.05, '2021/12': 0.56, '2022/12': -1.28, '2023/12': nan}),
|
162
|
+
'재고자산회전율': (1.66, {'2018/12': 12.41, '2019/12': 12.44, '2020/12': 9.18, '2021/12': 9.76, '2022/12': 8.79, '2023/12': nan})
|
163
|
+
}
|
164
|
+
|
165
|
+
<유동비율>
|
166
|
+
100미만이면 주의하나 현금흐름창출력이 좋으면 괜찮을수 있다.
|
167
|
+
만약 100%이하면 유동자산에 추정영업현금흐름을 더해서 다시계산해보아 기회를 준다.
|
168
|
+
<이자보상배율>
|
169
|
+
이자보상배율 영업이익/이자비용으로 1이면 자금사정빡빡 5이상이면 양호
|
170
|
+
<순운전자금회전율>
|
171
|
+
순운전자금 => 기업활동을 하기 위해 필요한 자금 (매출채권 + 재고자산 - 매입채무)
|
172
|
+
순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
|
173
|
+
<재고자산회전율>
|
174
|
+
재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
|
175
|
+
재고자산회전율이 작아지면 재고가 쌓인다는뜻
|
176
|
+
<순부채비율>
|
177
|
+
부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
|
178
|
+
순부채 비율이 30%이상이면 좋치 않다.
|
179
|
+
<매출액>
|
180
|
+
매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
|
181
|
+
<영업이익률>
|
182
|
+
영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
|
183
|
+
"""
|
184
|
+
|
185
|
+
d1, 유동비율 = tools.calc유동비율(code, pop_count=3)
|
186
|
+
logger.debug(f'유동비율 {유동비율} / [{d1}]')
|
187
|
+
|
188
|
+
c104y = myredis.C104(code, 'c104y')
|
189
|
+
_, dict이자보상배율y = c104y.find_with_yoy('이자보상배율')
|
190
|
+
_, dict순운전자본회전율y = c104y.find_with_yoy('순운전자본회전율')
|
191
|
+
_, dict재고자산회전율y = c104y.find_with_yoy('재고자산회전율')
|
192
|
+
_, dict순부채비율y = c104y.find_with_yoy('순부채비율')
|
193
|
+
|
194
|
+
c104q = myredis.C104(code, 'c104q')
|
195
|
+
d6, 이자보상배율q = c104q.latest_value_pop2('이자보상배율')
|
196
|
+
d7, 순운전자본회전율q = c104q.latest_value_pop2('순운전자본회전율')
|
197
|
+
d8, 재고자산회전율q = c104q.latest_value_pop2('재고자산회전율')
|
198
|
+
d9, 순부채비율q = c104q.latest_value_pop2('순부채비율')
|
199
|
+
|
200
|
+
if len(dict이자보상배율y) == 0:
|
201
|
+
logger.warning(f'empty dict - 이자보상배율 : {이자보상배율q} {dict이자보상배율y}')
|
202
|
+
|
203
|
+
if len(dict순운전자본회전율y) == 0:
|
204
|
+
logger.warning(f'empty dict - 순운전자본회전율 : {순운전자본회전율q} {dict순운전자본회전율y}')
|
205
|
+
|
206
|
+
if len(dict재고자산회전율y) == 0:
|
207
|
+
logger.warning(f'empty dict - 재고자산회전율 : {재고자산회전율q} {dict재고자산회전율y}')
|
208
|
+
|
209
|
+
if len(dict순부채비율y) == 0:
|
210
|
+
logger.warning(f'empty dict - 순부채비율 : {순부채비율q} {dict순부채비율y}')
|
211
|
+
|
212
|
+
################################################################
|
213
|
+
|
214
|
+
return {
|
215
|
+
'유동비율': 유동비율,
|
216
|
+
'이자보상배율': (이자보상배율q, dict이자보상배율y),
|
217
|
+
'순운전자본회전율': (순운전자본회전율q, dict순운전자본회전율y),
|
218
|
+
'재고자산회전율': (재고자산회전율q, dict재고자산회전율y),
|
219
|
+
'순부채비율': (순부채비율q, dict순부채비율y),
|
220
|
+
'date': tools.set_data(d1, d6, d7, d8, d9), # ''값을 제거하고 리스트로 바꾼다.
|
221
|
+
}
|
222
|
+
|
223
|
+
|
224
|
+
def growth(code: str) -> dict:
|
225
|
+
"""
|
226
|
+
리턴값
|
227
|
+
{'date': [각 유효한 값의 최근분기 값 리스트(ex- 2020/09)],
|
228
|
+
'매출액증가율': (-14.37, {'2018/12': -24.56, '2019/12': -20.19, '2020/12': -12.64, '2021/12': 38.65, '2022/12': -8.56, '2023/12': nan}),
|
229
|
+
'영업이익률': {'뉴프렉스': '17.36', '동일기연': '13.58', '비에이치': '16.23', '에이엔피': '-9.30', '이브이첨단소재': '-4.93'}}
|
230
|
+
|
231
|
+
<매출액>
|
232
|
+
매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
|
233
|
+
<영업이익률>
|
234
|
+
영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
|
235
|
+
"""
|
236
|
+
c104y = myredis.C104(code, 'c104y')
|
237
|
+
c106y = myredis.C106(code, 'c106y')
|
238
|
+
|
239
|
+
_, dict매출액증가율y = c104y.find_with_yoy('매출액증가율')
|
240
|
+
|
241
|
+
c104q = myredis.C104(code, 'c104q')
|
242
|
+
d2, 매출액증가율q = c104q.latest_value_pop2('매출액증가율')
|
243
|
+
|
244
|
+
logger.debug(f'매출액증가율 : {매출액증가율q} {dict매출액증가율y}')
|
245
|
+
|
246
|
+
################################################################
|
247
|
+
|
248
|
+
# c106 에서 타 기업과 영업이익률 비교
|
249
|
+
dict영업이익률 = c106y.find('영업이익률')
|
250
|
+
|
251
|
+
return {
|
252
|
+
'매출액증가율': (매출액증가율q, dict매출액증가율y),
|
253
|
+
'영업이익률': dict영업이익률,
|
254
|
+
'date': [d2, ]}
|
255
|
+
|
256
|
+
|
257
|
+
"""
|
258
|
+
- 각분기의 합이 연이 아닌 타이틀(즉 sum_4q를 사용하면 안됨)
|
259
|
+
'*(지배)당기순이익'
|
260
|
+
'*(비지배)당기순이익'
|
261
|
+
'장기차입금'
|
262
|
+
'현금및예치금'
|
263
|
+
'매도가능금융자산'
|
264
|
+
'매도파생결합증권'
|
265
|
+
'만기보유금융자산'
|
266
|
+
'당기손익-공정가치측정금융부채'
|
267
|
+
'당기손익인식(지정)금융부채'
|
268
|
+
'단기매매금융자산'
|
269
|
+
'단기매매금융부채'
|
270
|
+
'예수부채'
|
271
|
+
'차입부채'
|
272
|
+
'기타부채'
|
273
|
+
'보험계약부채(책임준비금)'
|
274
|
+
'*CAPEX'
|
275
|
+
'ROE'
|
276
|
+
"""
|
277
|
+
|
278
|
+
"""
|
279
|
+
- sum_4q를 사용해도 되는 타이틀
|
280
|
+
'자산총계'
|
281
|
+
'당기순이익'
|
282
|
+
'유동자산'
|
283
|
+
'유동부채'
|
284
|
+
'비유동부채'
|
285
|
+
|
286
|
+
'영업활동으로인한현금흐름'
|
287
|
+
'재무활동으로인한현금흐름'
|
288
|
+
'ROIC'
|
289
|
+
"""
|
@@ -1,8 +1,8 @@
|
|
1
1
|
"""다양한 문자열 출력 형식에 맞춘 함수들
|
2
2
|
"""
|
3
|
-
from
|
4
|
-
from .
|
5
|
-
from .
|
3
|
+
from db_hj3415 import myredis
|
4
|
+
from analyser_hj3415.analysers import eval
|
5
|
+
from analyser_hj3415.analysers import score
|
6
6
|
from utils_hj3415 import utils
|
7
7
|
import textwrap
|
8
8
|
|
@@ -21,7 +21,7 @@ class Report:
|
|
21
21
|
def __init__(self, client, code: str):
|
22
22
|
self.client = client
|
23
23
|
self.code = code
|
24
|
-
self.name =
|
24
|
+
self.name = myredis.Corps.get_name(code)
|
25
25
|
|
26
26
|
def __str__(self):
|
27
27
|
return (self.c101() + self.separate_line
|
@@ -32,7 +32,7 @@ class Report:
|
|
32
32
|
# + make_str.c108())
|
33
33
|
|
34
34
|
def c101(self, full=True):
|
35
|
-
c101 =
|
35
|
+
c101 = myredis.C101(self.code).get_recent()
|
36
36
|
logger.info(c101)
|
37
37
|
|
38
38
|
title = '=' * 35 + f"\t{c101['코드']}\t\t{c101['종목명']}\t\t{c101['업종']}\t" + '=' * 35
|
@@ -65,11 +65,11 @@ class Report:
|
|
65
65
|
return title + '\n' + intro + self.separate_line + price + '\n' + info
|
66
66
|
|
67
67
|
def red(self, full=True) -> str:
|
68
|
-
red_dict =
|
69
|
-
|
68
|
+
red_dict = eval.red(self.code)
|
69
|
+
괴리율 = score.red(self.code)
|
70
70
|
logger.info(red_dict)
|
71
71
|
|
72
|
-
title = f"Red\
|
72
|
+
title = f"Red\t괴리율({괴리율}%)\t{red_dict['date']}\n"
|
73
73
|
if full:
|
74
74
|
contents = (f"사업가치({utils.deco_num(red_dict['사업가치'])}억) "
|
75
75
|
f"+ 재산가치({utils.deco_num(red_dict['재산가치'])}억) "
|
@@ -81,8 +81,8 @@ class Report:
|
|
81
81
|
return title + contents
|
82
82
|
|
83
83
|
def mil(self, full=True) -> str:
|
84
|
-
mil_dict =
|
85
|
-
p1, p2, p3, p4 =
|
84
|
+
mil_dict = eval.mil(self.code)
|
85
|
+
p1, p2, p3, p4 = score.mil(self.code)
|
86
86
|
logger.info(mil_dict)
|
87
87
|
|
88
88
|
title = f"Millenial\tPoint({p1+p2+p3+p4})\t{mil_dict['date']}\n"
|
@@ -104,8 +104,8 @@ class Report:
|
|
104
104
|
return title + contents
|
105
105
|
|
106
106
|
def blue(self, full=True) -> str:
|
107
|
-
blue_dict =
|
108
|
-
p1, p2, p3, p4, p5 =
|
107
|
+
blue_dict = eval.blue(self.code)
|
108
|
+
p1, p2, p3, p4, p5 = score.blue(self.code)
|
109
109
|
logger.info(blue_dict)
|
110
110
|
|
111
111
|
title = f"Blue\tPoint({p1+p2+p3+p4+p5})\t{blue_dict['date']}\n"
|
@@ -121,8 +121,8 @@ class Report:
|
|
121
121
|
return title + contents
|
122
122
|
|
123
123
|
def growth(self, full=True) -> str:
|
124
|
-
growth_dict =
|
125
|
-
p1, p2 =
|
124
|
+
growth_dict = eval.growth(self.code)
|
125
|
+
p1, p2 = score.growth(self.code)
|
126
126
|
logger.info(growth_dict)
|
127
127
|
|
128
128
|
title = f"Growth\tPoint({p1 + p2})\t{growth_dict['date']}\n"
|
@@ -169,50 +169,14 @@ class Report:
|
|
169
169
|
'red_s': (53, -75.42)}
|
170
170
|
"""
|
171
171
|
return {
|
172
|
-
'c101':
|
173
|
-
'red':
|
174
|
-
'mil':
|
175
|
-
'blue':
|
176
|
-
'growth':
|
177
|
-
|
178
|
-
'
|
179
|
-
'
|
180
|
-
'
|
181
|
-
'growth_s': score_growth(self.client, self.code),
|
172
|
+
'c101': myredis.C101(self.code).get_recent(),
|
173
|
+
'red': eval.red(self.code),
|
174
|
+
'mil': eval.mil(self.code),
|
175
|
+
'blue': eval.blue(self.code),
|
176
|
+
'growth': eval.growth(self.code),
|
177
|
+
'red_s': score.red(self.code),
|
178
|
+
'mil_s': score.mil(self.code),
|
179
|
+
'blue_s': score.blue(self.code),
|
180
|
+
'growth_s': score.growth(self.code),
|
182
181
|
}
|
183
182
|
|
184
|
-
|
185
|
-
"""
|
186
|
-
def c108(self, full=True) -> str:
|
187
|
-
if full:
|
188
|
-
c108_list = mongo.C108(self.client, self.code).get_all()
|
189
|
-
else:
|
190
|
-
c108_list = mongo.C108(self.client, self.code).get_recent()
|
191
|
-
s = ''
|
192
|
-
logger.info(c108_list)
|
193
|
-
for i, c108_dict in enumerate(c108_list):
|
194
|
-
logger.info(c108_dict)
|
195
|
-
if i == 0:
|
196
|
-
pass
|
197
|
-
else:
|
198
|
-
s += '\n'
|
199
|
-
header = f"{c108_dict['날짜']}\thprice : {c108_dict['목표가']} 원\n"
|
200
|
-
title = f"<<{c108_dict['제목']}>>\n"
|
201
|
-
contents = ''
|
202
|
-
for line in c108_dict['내용'].split('▶'):
|
203
|
-
contents += line.strip()
|
204
|
-
s += header + title + textwrap.fill(contents, width=70) + self.separate_line
|
205
|
-
return s
|
206
|
-
"""
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
def for_telegram(client, code: str) -> str:
|
211
|
-
make_str = MakeStr(client, code)
|
212
|
-
|
213
|
-
return (make_str.c101(full=False) + make_str.separate_line
|
214
|
-
+ make_str.red(full=False) + make_str.separate_line
|
215
|
-
+ make_str.mil(full=False) + make_str.separate_line
|
216
|
-
+ make_str.blue(full=False) + make_str.separate_line
|
217
|
-
+ make_str.growth(full=False))
|
218
|
-
# + make_str.c108(full=False))
|
@@ -4,8 +4,8 @@ import pprint
|
|
4
4
|
import copy
|
5
5
|
from typing import Tuple
|
6
6
|
|
7
|
-
from
|
8
|
-
from .
|
7
|
+
from db_hj3415 import myredis, mymongo
|
8
|
+
from analyser_hj3415.analysers import eval
|
9
9
|
from utils_hj3415 import utils
|
10
10
|
|
11
11
|
import logging
|
@@ -18,50 +18,45 @@ logger.addHandler(ch)
|
|
18
18
|
logger.setLevel(logging.WARNING)
|
19
19
|
|
20
20
|
|
21
|
-
def
|
22
|
-
"""
|
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
|
+
|
23
34
|
|
24
|
-
|
25
|
-
|
35
|
+
def red(code: str) -> int:
|
36
|
+
"""red price와 최근 주가의 괴리율 파악
|
26
37
|
|
27
38
|
Returns:
|
28
|
-
|
39
|
+
int : 주가와 red price 비교한 괴리율
|
29
40
|
"""
|
30
|
-
c101 = mongo.C101(client, code)
|
31
|
-
|
32
|
-
logger.debug(f'c101 {c101.get_recent()}')
|
33
|
-
|
34
41
|
try:
|
35
|
-
recent_price = utils.to_int(
|
42
|
+
recent_price = utils.to_int(myredis.C101(code).get_recent()['주가'])
|
36
43
|
except KeyError:
|
37
44
|
recent_price = float('nan')
|
38
|
-
|
39
|
-
|
40
|
-
logger.debug(f"recent_price : {recent_price}\tred_price : {red_price}")
|
45
|
+
return 0
|
41
46
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
except ZeroDivisionError:
|
49
|
-
괴리율 = float('nan')
|
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점에 가깝게)
|
50
53
|
|
51
|
-
|
54
|
+
#print(f"최근주가 : {recent_price}", f"red가격 : {red_price}", f"괴리율 : {utils.to_int(deviation)}", f"score : {utils.to_int(score)}")
|
52
55
|
|
53
|
-
|
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, 괴리율
|
56
|
+
return utils.to_int(score)
|
62
57
|
|
63
58
|
|
64
|
-
def mil(
|
59
|
+
def mil(code: str) -> Tuple[int, int, int, int]:
|
65
60
|
"""
|
66
61
|
- 재무활동현금흐름이 마이너스라는 것은 배당급 지급했거나, 자사주 매입했거나, 부채를 상환한 상태임.
|
67
62
|
- 반대는 채권자로 자금을 조달했거나 신주를 발행했다는 의미
|
@@ -90,55 +85,55 @@ def mil(client, code: str) -> Tuple[int, int, int, int]:
|
|
90
85
|
Returns:
|
91
86
|
tuple: 주주수익률, 이익지표, 투자수익률, PFCF포인트
|
92
87
|
"""
|
93
|
-
mil_dict =
|
88
|
+
mil_dict = eval.mil(code)
|
94
89
|
|
95
|
-
|
90
|
+
print(pprint.pformat(mil_dict, width=200))
|
96
91
|
|
97
92
|
# 주주수익률 평가
|
98
93
|
if math.isnan(mil_dict['주주수익률']):
|
99
|
-
|
94
|
+
score1 = 0
|
100
95
|
else:
|
101
|
-
주주수익률평가 = math.ceil(mil_dict['주주수익률'] - (EXPECT_EARN * 100))
|
102
|
-
|
96
|
+
주주수익률평가 = math.ceil(mil_dict['주주수익률'] - (eval.EXPECT_EARN * 100))
|
97
|
+
score1 = 0 if 0 > 주주수익률평가 else 주주수익률평가
|
103
98
|
|
104
99
|
# 이익지표 평가
|
105
|
-
|
100
|
+
score2 = 10 if mil_dict['이익지표'] < 0 else 0
|
106
101
|
|
107
102
|
# 투자수익률 평가
|
108
103
|
MAX3 = 20
|
109
|
-
|
104
|
+
score3 = 0
|
110
105
|
roic = mil_dict['투자수익률']['ROIC']
|
111
106
|
roe = mil_dict['투자수익률']['ROE']
|
112
107
|
if math.isnan(roic) or roic <= 0:
|
113
108
|
# roic 가 비정상이라 평가할 수 없는 경우
|
114
109
|
if 10 < roe <= 20:
|
115
|
-
|
110
|
+
score3 += round(MAX3 * 0.333)
|
116
111
|
elif 20 < roe:
|
117
|
-
|
112
|
+
score3 += round(MAX3 * 0.666)
|
118
113
|
elif 0 < roic:
|
119
114
|
# roic 로 평가할 수 있는 경우
|
120
115
|
if 0 < roic <= 15:
|
121
|
-
|
116
|
+
score3 += round(MAX3 * 0.333)
|
122
117
|
elif 15 < roic <= 30:
|
123
|
-
|
118
|
+
score3 += round(MAX3 * 0.666)
|
124
119
|
elif 30 < roic:
|
125
|
-
|
120
|
+
score3 += MAX3
|
126
121
|
|
127
122
|
# PFCF 평가
|
128
123
|
pfcf_dict = mil_dict['가치지표']['PFCF']
|
129
|
-
_, pfcf =
|
124
|
+
_, pfcf = mymongo.C1034.latest_dict_value(pfcf_dict)
|
130
125
|
|
131
126
|
logger.debug(f'recent pfcf {_}, {pfcf}')
|
132
127
|
try:
|
133
128
|
p = round(-40 * math.log10(pfcf) + 40)
|
134
129
|
except ValueError:
|
135
130
|
p = 0
|
136
|
-
|
131
|
+
score4 = 0 if 0 > p else p
|
137
132
|
|
138
|
-
return
|
133
|
+
return score1, score2, score3, score4
|
139
134
|
|
140
135
|
|
141
|
-
def blue(
|
136
|
+
def blue(code: str) -> Tuple[int, int, int, int, int]:
|
142
137
|
"""회사의 안정성을 보는 지표들
|
143
138
|
|
144
139
|
0을 기준으로 상태가 좋치 않을 수록 마이너스 값을 가진다.
|
@@ -181,11 +176,11 @@ def blue(client, code: str) -> Tuple[int, int, int, int, int]:
|
|
181
176
|
|
182
177
|
return int(p)
|
183
178
|
|
184
|
-
c104y =
|
179
|
+
c104y = myredis.C104(code, 'c104y')
|
185
180
|
|
186
|
-
blue_dict =
|
181
|
+
blue_dict = eval.blue(code)
|
187
182
|
|
188
|
-
|
183
|
+
print(pprint.pformat(blue_dict, width=200))
|
189
184
|
|
190
185
|
def 유동비율평가(유동비율: float) -> int:
|
191
186
|
# 채점은 0을 기준으로 마이너스 해간다. 즉 0이 제일 좋은 상태임.
|
@@ -210,9 +205,9 @@ def blue(client, code: str) -> Tuple[int, int, int, int, int]:
|
|
210
205
|
if math.isnan(최근이자보상배율q) or 최근이자보상배율q <= 1:
|
211
206
|
# 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
|
212
207
|
|
213
|
-
_, 최근이자보상배율y =
|
208
|
+
_, 최근이자보상배율y = mymongo.C1034.latest_dict_value(dict_y)
|
214
209
|
c104y.page = 'c104y'
|
215
|
-
|
210
|
+
전년대비 = c104y.find_yoy(title='이자보상배율')
|
216
211
|
|
217
212
|
if math.isnan(최근이자보상배율y) or 최근이자보상배율y <= 1 or math.isnan(전년대비) or 전년대비 < 0:
|
218
213
|
p = NEG_MAX
|
@@ -233,9 +228,9 @@ def blue(client, code: str) -> Tuple[int, int, int, int, int]:
|
|
233
228
|
|
234
229
|
if math.isnan(최근순부채비율q) or 최근순부채비율q >= 80:
|
235
230
|
# 최근 분기의 값이 비정상이면 최근 년도를 한번 더 비교해 보지만 좀더 엄격하게 전년대비도 비교한다.
|
236
|
-
_, 최근순부채비율y =
|
231
|
+
_, 최근순부채비율y = mymongo.C1034.latest_dict_value(dict_y)
|
237
232
|
c104y.page = 'c104y'
|
238
|
-
|
233
|
+
전년대비 = c104y.find_yoy(title='순부채비율')
|
239
234
|
if math.isnan(최근순부채비율y) or 최근순부채비율y >= 80 or math.isnan(전년대비) or 전년대비 > 0:
|
240
235
|
p = NEG_MAX
|
241
236
|
else:
|
@@ -269,7 +264,7 @@ def blue(client, code: str) -> Tuple[int, int, int, int, int]:
|
|
269
264
|
return p1, p2, p3, p4, p5
|
270
265
|
|
271
266
|
|
272
|
-
def growth(
|
267
|
+
def growth(code: str) -> Tuple[int, int]:
|
273
268
|
"""회사의 성장성을 보는 지표들
|
274
269
|
|
275
270
|
<매출액>
|
@@ -280,7 +275,7 @@ def growth(client, code: str) -> Tuple[int, int]:
|
|
280
275
|
Returns:
|
281
276
|
tuple : 매출액증가율, 영업이익률 평가 포인트
|
282
277
|
"""
|
283
|
-
growth_dict =
|
278
|
+
growth_dict = eval.growth(code)
|
284
279
|
|
285
280
|
logger.debug(pprint.pformat(growth_dict, width=200))
|
286
281
|
|
@@ -288,7 +283,7 @@ def growth(client, code: str) -> Tuple[int, int]:
|
|
288
283
|
# 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
|
289
284
|
MAX = 20
|
290
285
|
최근매출액증가율q, dict_y = 매출액증가율
|
291
|
-
_, 최근매출액증가율y =
|
286
|
+
_, 최근매출액증가율y = mymongo.C1034.latest_dict_value(dict_y)
|
292
287
|
|
293
288
|
# 최근 자료가 성장하는 중인지 판단
|
294
289
|
if math.isnan(최근매출액증가율q):
|
@@ -342,7 +337,7 @@ def growth(client, code: str) -> Tuple[int, int]:
|
|
342
337
|
def 영업이익률평가(영업이익률: dict) -> int:
|
343
338
|
# 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
|
344
339
|
영업이익률 = copy.deepcopy(영업이익률)
|
345
|
-
name =
|
340
|
+
name = myredis.Corps.get_name(code)
|
346
341
|
|
347
342
|
p = 0
|
348
343
|
try:
|