analyser_hj3415 2.0.2__py2.py3-none-any.whl → 2.1.0__py2.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.
- 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.2.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.2.dist-info/RECORD +0 -16
- /analyser_hj3415/{db/__init__.py → run.py} +0 -0
- {analyser_hj3415-2.0.2.dist-info → analyser_hj3415-2.1.0.dist-info}/LICENSE +0 -0
- {analyser_hj3415-2.0.2.dist-info → analyser_hj3415-2.1.0.dist-info}/WHEEL +0 -0
- {analyser_hj3415-2.0.2.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:
|