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.
@@ -0,0 +1,382 @@
1
+ """red, mil, blue 3가지 분야에서 자료를 계산하여 리턴하는 함수 모음
2
+ """
3
+ import math
4
+ import time
5
+ import datetime
6
+ import pandas as pd
7
+ from multiprocessing import Process, Queue
8
+
9
+ from .db import mongo, evaltools
10
+ from utils_hj3415 import utils
11
+
12
+ import logging
13
+
14
+ logger = logging.getLogger(__name__)
15
+ formatter = logging.Formatter('%(levelname)s: [%(name)s] %(message)s')
16
+ ch = logging.StreamHandler()
17
+ ch.setFormatter(formatter)
18
+ logger.addHandler(ch)
19
+ logger.setLevel(logging.WARNING)
20
+
21
+ # 주식을 통한 기대수익률 - 금리가 3%일 경우 두배인 6% 정도로 잡는다.
22
+ EXPECT_EARN = 0.04
23
+
24
+
25
+ def red(client, code: str) -> dict:
26
+ """
27
+ 리턴값
28
+ {
29
+ 'red_price': red_price,
30
+ '사업가치': 사업가치,
31
+ '재산가치': 재산가치,
32
+ '부채평가': 부채평가,
33
+ '발행주식수': 발행주식수,
34
+ 'date': [각 유효한 값의 년월값 리스트(ex- 2020/09)],
35
+ }
36
+ """
37
+ c101 = mongo.C101(client, code)
38
+ c103q = mongo.C103(client, code, 'c103재무상태표q')
39
+
40
+ d1, 지배주주당기순이익 = evaltools.calc당기순이익(client, code)
41
+ d2, 유동자산 = evaltools.calc유동자산(client, code)
42
+ d3, 유동부채 = evaltools.calc유동부채(client, code)
43
+ d4, 부채평가 = evaltools.calc비유동부채(client, code)
44
+
45
+ c103q.page = 'c103재무상태표q'
46
+ d5, 투자자산 = c103q.latest_value('투자자산', nan_to_zero=True, allow_empty=True)
47
+ d6, 투자부동산 = c103q.latest_value('투자부동산', nan_to_zero=True, allow_empty=True)
48
+
49
+ # 사업가치 계산 - 지배주주지분 당기순이익 / 기대수익률
50
+ 사업가치 = round(지배주주당기순이익 / EXPECT_EARN, 2)
51
+
52
+ # 재산가치 계산 - 유동자산 - (유동부채*1.2) + 고정자산중 투자자산
53
+ 재산가치 = round(유동자산 - (유동부채 * 1.2) + 투자자산 + 투자부동산, 2)
54
+ if math.isnan(재산가치):
55
+ logger.warning(f'유동자산: {유동자산} - 유동부채: {유동부채} * 1.2 + 투자자산: {투자자산} + 투자부동산: {투자부동산}')
56
+
57
+ d, 발행주식수 = c103q.latest_value('발행주식수', allow_empty=True)
58
+ if math.isnan(발행주식수):
59
+ rc101 = c101.get_recent()
60
+ logger.error(rc101)
61
+ 발행주식수 = utils.to_int(rc101.get('발행주식'))
62
+ else:
63
+ 발행주식수 = 발행주식수 * 1000
64
+
65
+ try:
66
+ red_price = round(((사업가치 + 재산가치 - 부채평가) * 100000000) / 발행주식수)
67
+ except (ZeroDivisionError, ValueError) as e:
68
+ logger.error(f'In calc red price... {e} : {code}')
69
+ red_price = math.nan
70
+
71
+ logger.debug(f'Red Price : {red_price}원')
72
+ return {
73
+ 'red_price': red_price,
74
+ '사업가치': 사업가치,
75
+ '재산가치': 재산가치,
76
+ '부채평가': 부채평가,
77
+ '발행주식수': 발행주식수,
78
+ 'date': [i for i in {d1, d2, d3, d4, d5, d6} if i != ''], # ''값을 제거하고 리스트로 바꾼다.
79
+ }
80
+
81
+
82
+ def mil(client, code: str) -> dict:
83
+ """
84
+ 리턴값
85
+ {
86
+ '주주수익률': 주주수익률,
87
+ '이익지표': 이익지표,
88
+ '투자수익률': {'ROIC': roic, 'ROE': roe},
89
+ '가치지표': {'FCF': fcf_dict, 'PFCF': pfcf_dict, 'PCR': pcr_dict},
90
+ 'date': [각 유효한 값의 년월값 리스트(ex- 2020/09)],
91
+ }
92
+ """
93
+ c103 = mongo.C103(client, code, 'c103현금흐름표q')
94
+ c104 = mongo.C104(client, code, 'c104q')
95
+
96
+ marketcap억 = evaltools.get_marketcap(client, code, nan_to_zero=False) / 100000000
97
+ logger.debug(f'{code} market cap: {marketcap억}')
98
+ fcf_dict = evaltools.findFCF(client, code)
99
+ pfcf_dict = evaltools.findPFCF(client, code)
100
+ d1, 지배주주당기순이익 = evaltools.calc당기순이익(client, code)
101
+
102
+ d2, 재무활동현금흐름 = c103.sum_recent_4q(title='재무활동으로인한현금흐름', nan_to_zero=False)
103
+ d3, 영업활동현금흐름 = c103.sum_recent_4q(title='영업활동으로인한현금흐름', nan_to_zero=False)
104
+
105
+ d4, roic = c104.sum_recent_4q('ROIC', nan_to_zero=False)
106
+ d5, roe = c104.latest_value('ROE', nan_to_zero=False, allow_empty=True)
107
+ pcr_dict = c104.find('PCR', allow_empty=True)
108
+
109
+ try:
110
+ 주주수익률 = round((재무활동현금흐름 / marketcap억 * -100), 2)
111
+ 이익지표 = round((지배주주당기순이익 - 영업활동현금흐름) / marketcap억, 5)
112
+ except ZeroDivisionError:
113
+ 주주수익률 = math.nan
114
+ 이익지표 = math.nan
115
+
116
+ if math.isnan(주주수익률) or math.isnan(이익지표):
117
+ logger.warning(f'주주수익률: {주주수익률} 이익지표: {이익지표}')
118
+ logger.warning(f'재무활동현금흐름: {재무활동현금흐름}지배주주당기순이익: {지배주주당기순이익}영업활동현금흐름: {영업활동현금흐름}')
119
+
120
+ logger.debug(f'{code} fcf_dict : {fcf_dict}')
121
+ logger.debug(f"{code} market_cap : {marketcap억}")
122
+ logger.debug(f'{code} pfcf_dict : {pfcf_dict}')
123
+ logger.debug(f'{code} pcr_dict : {pcr_dict}')
124
+
125
+ return {
126
+ '주주수익률': 주주수익률,
127
+ '이익지표': 이익지표,
128
+ '투자수익률': {'ROIC': roic, 'ROE': roe},
129
+ '가치지표': {'FCF': fcf_dict, 'PFCF': pfcf_dict, 'PCR': pcr_dict},
130
+ 'date': [i for i in {d1, d2, d3, d4, d5} if i != ''], # ''값을 제거하고 리스트로 바꾼다.
131
+ }
132
+
133
+
134
+ def blue(client, code: str) -> dict:
135
+ """
136
+ 리턴값
137
+ {
138
+ 'date': [각 유효한 값의 최근분기 값 리스트(ex- 2020/09)],
139
+ '순부채비율': (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}),
140
+ '순운전자본회전율': (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}),
141
+ '유동비율': 64.29,
142
+ '이자보상배율': (-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}),
143
+ '재고자산회전율': (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})
144
+ }
145
+
146
+ <유동비율>
147
+ 100미만이면 주의하나 현금흐름창출력이 좋으면 괜찮을수 있다.
148
+ 만약 100%이하면 유동자산에 추정영업현금흐름을 더해서 다시계산해보아 기회를 준다.
149
+ <이자보상배율>
150
+ 이자보상배율 영업이익/이자비용으로 1이면 자금사정빡빡 5이상이면 양호
151
+ <순운전자금회전율>
152
+ 순운전자금 => 기업활동을 하기 위해 필요한 자금 (매출채권 + 재고자산 - 매입채무)
153
+ 순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
154
+ <재고자산회전율>
155
+ 재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
156
+ 재고자산회전율이 작아지면 재고가 쌓인다는뜻
157
+ <순부채비율>
158
+ 부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
159
+ 순부채 비율이 30%이상이면 좋치 않다.
160
+ <매출액>
161
+ 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
162
+ <영업이익률>
163
+ 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
164
+ """
165
+
166
+ d1, 유동비율 = evaltools.calc유동비율(client, code, pop_count=3)
167
+ logger.debug(f'유동비율 {유동비율} / [{d1}]')
168
+
169
+ c104y = mongo.C104(client, code, 'c104y')
170
+ dict이자보상배율y = c104y.find('이자보상배율', allow_empty=True)
171
+ dict순운전자본회전율y = c104y.find('순운전자본회전율', allow_empty=True)
172
+ dict재고자산회전율y = c104y.find('재고자산회전율', allow_empty=True)
173
+ dict순부채비율y = c104y.find('순부채비율', allow_empty=True)
174
+
175
+ c104q = mongo.C104(client, code, 'c104q')
176
+ d2, 이자보상배율q = c104q.latest_value('이자보상배율', allow_empty=True)
177
+ d3, 순운전자본회전율q = c104q.latest_value('순운전자본회전율', allow_empty=True)
178
+ d4, 재고자산회전율q = c104q.latest_value('재고자산회전율', allow_empty=True)
179
+ d5, 순부채비율q = c104q.latest_value('순부채비율', allow_empty=True)
180
+
181
+ if len(dict이자보상배율y) == 0:
182
+ logger.warning(f'empty dict - 이자보상배율 : {이자보상배율q} {dict이자보상배율y}')
183
+
184
+ if len(dict순운전자본회전율y) == 0:
185
+ logger.warning(f'empty dict - 순운전자본회전율 : {순운전자본회전율q} {dict순운전자본회전율y}')
186
+
187
+ if len(dict재고자산회전율y) == 0:
188
+ logger.warning(f'empty dict - 재고자산회전율 : {재고자산회전율q} {dict재고자산회전율y}')
189
+
190
+ if len(dict순부채비율y) == 0:
191
+ logger.warning(f'empty dict - 순부채비율 : {순부채비율q} {dict순부채비율y}')
192
+
193
+ ################################################################
194
+
195
+ return {
196
+ '유동비율': 유동비율,
197
+ '이자보상배율': (이자보상배율q, dict이자보상배율y),
198
+ '순운전자본회전율': (순운전자본회전율q, dict순운전자본회전율y),
199
+ '재고자산회전율': (재고자산회전율q, dict재고자산회전율y),
200
+ '순부채비율': (순부채비율q, dict순부채비율y),
201
+ 'date': [i for i in {d1, d2, d3, d4, d5} if i != ''], # ''값을 제거하고 리스트로 바꾼다.
202
+ }
203
+
204
+
205
+ def growth(client, code: str) -> dict:
206
+ """
207
+ 리턴값
208
+ {'date': [각 유효한 값의 최근분기 값 리스트(ex- 2020/09)],
209
+ '매출액증가율': (-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}),
210
+ '영업이익률': {'뉴프렉스': '17.36', '동일기연': '13.58', '비에이치': '16.23', '에이엔피': '-9.30', '이브이첨단소재': '-4.93'}}
211
+
212
+ <매출액>
213
+ 매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
214
+ <영업이익률>
215
+ 영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
216
+ """
217
+ c104y = mongo.C104(client, code, 'c104y')
218
+ c106y = mongo.C106(client, code, 'c106y')
219
+
220
+ dict매출액증가율y = c104y.find('매출액증가율', allow_empty=True)
221
+
222
+ c104q = mongo.C104(client, code, 'c104q')
223
+ d1, 매출액증가율q = c104q.latest_value('매출액증가율', nan_to_zero=False, allow_empty=True)
224
+
225
+ logger.debug(f'매출액증가율 : {매출액증가율q} {dict매출액증가율y}')
226
+
227
+ ################################################################
228
+
229
+ # c106 에서 타 기업과 영업이익률 비교
230
+ dict영업이익률 = c106y.find('영업이익률', allow_empty=True)
231
+
232
+ return {
233
+ '매출액증가율': (매출액증가율q, dict매출액증가율y),
234
+ '영업이익률': dict영업이익률,
235
+ 'date': [d1, ]}
236
+
237
+
238
+ """
239
+ - 각분기의 합이 연이 아닌 타이틀(즉 sum_4q를 사용하면 안됨)
240
+ '*(지배)당기순이익'
241
+ '*(비지배)당기순이익'
242
+ '장기차입금'
243
+ '현금및예치금'
244
+ '매도가능금융자산'
245
+ '매도파생결합증권'
246
+ '만기보유금융자산'
247
+ '당기손익-공정가치측정금융부채'
248
+ '당기손익인식(지정)금융부채'
249
+ '단기매매금융자산'
250
+ '단기매매금융부채'
251
+ '예수부채'
252
+ '차입부채'
253
+ '기타부채'
254
+ '보험계약부채(책임준비금)'
255
+ '*CAPEX'
256
+ 'ROE'
257
+ """
258
+
259
+ """
260
+ - sum_4q를 사용해도 되는 타이틀
261
+ '자산총계'
262
+ '당기순이익'
263
+ '유동자산'
264
+ '유동부채'
265
+ '비유동부채'
266
+
267
+ '영업활동으로인한현금흐름'
268
+ '재무활동으로인한현금흐름'
269
+ 'ROIC'
270
+ """
271
+
272
+
273
+ def _make_df_part(db_addr, codes: list, q):
274
+ def make_record(my_client, my_code: str) -> dict:
275
+ # 장고에서 사용할 eval 테이블을 만들기 위해 각각의 레코드를 구성하는 함수
276
+ c101 = mongo.C101(my_client, my_code).get_recent()
277
+
278
+ red_dict = red(my_client, my_code)
279
+ mil_dict = mil(my_client, my_code)
280
+ growth_dict = growth(my_client, my_code)
281
+
282
+ mil_date = mil_dict['date']
283
+ red_date = red_dict['date']
284
+ growth_date = growth_dict['date']
285
+
286
+ return {
287
+ 'code': c101['코드'],
288
+ '종목명': c101['종목명'],
289
+ '주가': utils.to_int(c101['주가']),
290
+ 'PER': utils.to_float(c101['PER']),
291
+ 'PBR': utils.to_float(c101['PBR']),
292
+ '시가총액': utils.to_float(c101['시가총액']),
293
+ 'RED': utils.to_int(red_dict['red_price']),
294
+ '주주수익률': utils.to_float(mil_dict['주주수익률']),
295
+ '이익지표': utils.to_float(mil_dict['이익지표']),
296
+ 'ROIC': utils.to_float(mil_dict['투자수익률']['ROIC']),
297
+ 'ROE': utils.to_float(mil_dict['투자수익률']['ROE']),
298
+ 'PFCF': utils.to_float(mongo.Corps.latest_value(mil_dict['가치지표']['PFCF'])[1]),
299
+ 'PCR': utils.to_float(mongo.Corps.latest_value(mil_dict['가치지표']['PCR'])[1]),
300
+ '매출액증가율': utils.to_float(growth_dict['매출액증가율'][0]),
301
+ 'date': list(set(mil_date + red_date + growth_date))
302
+ }
303
+ # 각 코어별로 디비 클라이언트를 만들어야만 한다. 안그러면 에러발생
304
+ client = mongo.connect_mongo(db_addr)
305
+ t = len(codes)
306
+ d = []
307
+ for i, code in enumerate(codes):
308
+ print(f'{i+1}/{t} {code}')
309
+ try:
310
+ d.append(make_record(client, code))
311
+ except:
312
+ logger.error(f'error on {code}')
313
+ continue
314
+ df = pd.DataFrame(d)
315
+ logger.info(df)
316
+ q.put(df)
317
+
318
+
319
+ def make_today_eval_df(client, refresh: bool = False) -> pd.DataFrame:
320
+ """ 멀티프로세싱을 사용하여 전체 종목의 eval 을 데이터프레임으로 만들어 반환
321
+
322
+ 기본값으로 refresh 는 False 로 설정되어 당일자의 저장된 데이터프레임이 있으면 새로 생성하지 않고 mongo DB를 이용한다.
323
+ """
324
+ today_str = datetime.datetime.today().strftime('%Y%m%d')
325
+ df = mongo.EvalByDate(client, today_str).load_df()
326
+ if refresh or len(df) == 0:
327
+ codes_in_db = mongo.Corps.get_all_codes(client)
328
+
329
+ print('*' * 25, f"Eval all using multiprocess(refresh={refresh})", '*' * 25)
330
+ print(f'Total {len(codes_in_db)} items..')
331
+ logger.debug(codes_in_db)
332
+ n, divided_list = utils.code_divider_by_cpu_core(codes_in_db)
333
+
334
+ addr = mongo.extract_addr_from_client(client)
335
+
336
+ start_time = time.time()
337
+ q = Queue()
338
+ ths = []
339
+ for i in range(n):
340
+ ths.append(Process(target=_make_df_part, args=(addr, divided_list[i], q)))
341
+ for i in range(n):
342
+ ths[i].start()
343
+
344
+ df_list = []
345
+ for i in range(n):
346
+ df_list.append(q.get())
347
+ # 부분데이터프레임들을 하나로 합침
348
+ final_df = pd.concat(df_list, ignore_index=True)
349
+
350
+ for i in range(n):
351
+ ths[i].join()
352
+
353
+ print(f'Total spent time : {round(time.time() - start_time, 2)} sec.')
354
+ logger.debug(final_df)
355
+ print(f"Save to mongo db(db: eval col: {today_str})")
356
+ mongo.EvalByDate(client, today_str).save_df(final_df)
357
+ else:
358
+ print(f"Use saved dataframe from mongo db..")
359
+ final_df = df
360
+ return final_df
361
+
362
+
363
+ def yield_valid_spac(client) -> tuple:
364
+ """
365
+ 전체 스팩주의 현재가를 평가하여 2000원 이하인 경우 yield한다.
366
+
367
+ Returns:
368
+ tuple: (code, name, price)
369
+ """
370
+ codes = mongo.Corps.get_all_codes(client)
371
+ logger.debug(f'len(codes) : {len(codes)}')
372
+ print('<<< Finding valuable SPAC >>>')
373
+ for i, code in enumerate(codes):
374
+ name = mongo.Corps.get_name(client, code)
375
+ logger.debug(f'code : {code} name : {name}')
376
+ if '스팩' in str(name):
377
+ logger.debug(f'>>> spac - code : {code} name : {name}')
378
+ price, _, _ = utils.get_price_now(code=code)
379
+ if price <= 2000:
380
+ logger.warning(f'현재가:{price}')
381
+ print(f"code: {code} name: {name}, price: {price}")
382
+ yield code, name, price
@@ -0,0 +1,218 @@
1
+ """다양한 문자열 출력 형식에 맞춘 함수들
2
+ """
3
+ from .db import mongo
4
+ from .eval import red as eval_red, mil as eval_mil, blue as eval_blue, growth as eval_growth
5
+ from .score import red as score_red, mil as score_mil, blue as score_blue, growth as score_growth
6
+ from utils_hj3415 import utils
7
+ import textwrap
8
+
9
+ import logging
10
+ logger = logging.getLogger(__name__)
11
+ formatter = logging.Formatter('%(levelname)s: [%(name)s] %(message)s')
12
+ ch = logging.StreamHandler()
13
+ ch.setFormatter(formatter)
14
+ logger.addHandler(ch)
15
+ logger.setLevel(logging.WARNING)
16
+
17
+
18
+ class Report:
19
+ separate_line = '\n' + ('-' * 65) + '\n'
20
+
21
+ def __init__(self, client, code: str):
22
+ self.client = client
23
+ self.code = code
24
+ self.name = mongo.Corps.get_name(client, code)
25
+
26
+ def __str__(self):
27
+ return (self.c101() + self.separate_line
28
+ + self.red() + self.separate_line
29
+ + self.mil() + self.separate_line
30
+ + self.blue() + self.separate_line
31
+ + self.growth())
32
+ # + make_str.c108())
33
+
34
+ def c101(self, full=True):
35
+ c101 = mongo.C101(self.client, self.code).get_recent(merge_intro=True)
36
+ logger.info(c101)
37
+
38
+ title = '=' * 35 + f"\t{c101['코드']}\t\t{c101['종목명']}\t\t{c101['업종']}\t" + '=' * 35
39
+ intro = textwrap.fill(f"{c101['intro']}", width=70)
40
+
41
+ if full:
42
+ price = (f"{c101['date']}\t\t"
43
+ f"주가: {utils.deco_num(c101['주가'])}원\t\t"
44
+ f"52주최고: {utils.deco_num(c101['최고52주'])}원\t"
45
+ f"52주최저: {utils.deco_num(c101['최저52주'])}원")
46
+ info = (f"PER: {c101['PER']}\t\t"
47
+ f"PBR: {c101['PBR']}\t\t\t"
48
+ f"배당수익률: {c101['배당수익률']}%\t\t"
49
+ f"시가총액: {utils.get_kor_amount(utils.to_int(c101['시가총액']), omit='억')}\n"
50
+ f"업종PER: {c101['업종PER']}\t"
51
+ f"유통비율: {c101['유통비율']}%\t\t"
52
+ f"거래대금: {utils.to_억(c101['거래대금'])}원\t\t"
53
+ f"발행주식: {utils.to_만(c101['발행주식'])}주")
54
+ else:
55
+ price = (f"<< {c101['date']} >>\n"
56
+ f"주가: {utils.deco_num(c101['주가'])}원")
57
+ info = (f"PER: {c101['PER']}\n"
58
+ f"업종PER: {c101['업종PER']}\n"
59
+ f"PBR: {c101['PBR']}\n"
60
+ f"배당수익률: {c101['배당수익률']}%\n"
61
+ f"유통비율: {c101['유통비율']}%\n"
62
+ f"발행주식: {utils.to_만(c101['발행주식'])}주\n"
63
+ f"시가총액: {utils.get_kor_amount(utils.to_int(c101['시가총액']), omit='억')}")
64
+
65
+ return title + '\n' + intro + self.separate_line + price + '\n' + info
66
+
67
+ def red(self, full=True) -> str:
68
+ red_dict = eval_red(self.client, self.code)
69
+ p, 괴리율 = score_red(self.client, self.code)
70
+ logger.info(red_dict)
71
+
72
+ title = f"Red\tPoint({p})\t괴리율({괴리율}%)\t{red_dict['date']}\n"
73
+ if full:
74
+ contents = (f"사업가치({utils.deco_num(red_dict['사업가치'])}억) "
75
+ f"+ 재산가치({utils.deco_num(red_dict['재산가치'])}억) "
76
+ f"- 부채({utils.deco_num(red_dict['부채평가'])}억) "
77
+ f"/ 발행주식({utils.to_만(red_dict['발행주식수'])}주) "
78
+ f"= {utils.deco_num(red_dict['red_price'])}원")
79
+ else:
80
+ contents = f"{utils.deco_num(red_dict['red_price'])}원"
81
+ return title + contents
82
+
83
+ def mil(self, full=True) -> str:
84
+ mil_dict = eval_mil(self.client, self.code)
85
+ p1, p2, p3, p4 = score_mil(self.client, self.code)
86
+ logger.info(mil_dict)
87
+
88
+ title = f"Millenial\tPoint({p1+p2+p3+p4})\t{mil_dict['date']}\n"
89
+ if full:
90
+ contents = (f"1. 주주수익률({p1}): {mil_dict['주주수익률']} %\n"
91
+ f"2. 이익지표({p2}): {mil_dict['이익지표']}\n"
92
+ f"3. 투자수익률({p3}): ROIC 4분기합: {mil_dict['투자수익률']['ROIC']}%, "
93
+ f"최근 ROE: {mil_dict['투자수익률']['ROE']}%\n"
94
+ f"4. 가치지표\n"
95
+ f"\tFCF: {mil_dict['가치지표']['FCF']}\n"
96
+ f"\tPFCF({p4}) : {mil_dict['가치지표']['PFCF']}\n"
97
+ f"\tPCR: {mil_dict['가치지표']['PCR']}")
98
+ else:
99
+ contents = (f"1. 주주수익률({p1}): {mil_dict['주주수익률']} %\n"
100
+ f"2. 이익지표({p2}): {mil_dict['이익지표']}\n"
101
+ f"3. 투자수익률({p3}): ROIC 4분기합: {mil_dict['투자수익률']['ROIC']}%, "
102
+ f"최근 ROE: {mil_dict['투자수익률']['ROE']}%\n"
103
+ f"4. 가치지표\tPFCF({p4}) : {mongo.EvalTools.get_recent(mil_dict['가치지표']['PFCF'])}")
104
+ return title + contents
105
+
106
+ def blue(self, full=True) -> str:
107
+ blue_dict = eval_blue(self.client, self.code)
108
+ p1, p2, p3, p4, p5 = score_blue(self.client, self.code)
109
+ logger.info(blue_dict)
110
+
111
+ title = f"Blue\tPoint({p1+p2+p3+p4+p5})\t{blue_dict['date']}\n"
112
+ if full:
113
+ contents = (f"1. 유동비율({p1}): {blue_dict['유동비율']}(100이하 위험)\n"
114
+ f"2. 이자보상배율({p2}): {blue_dict['이자보상배율']}(1이하 위험 5이상 양호)\n"
115
+ f"3. 순부채비율({p3}): {blue_dict['순부채비율']}(30이상 not good)\n"
116
+ f"4. 순운전자본회전율({p4}): {blue_dict['순운전자본회전율']}\n"
117
+ f"5. 재고자산회전율({p5}): {blue_dict['재고자산회전율']}")
118
+
119
+ else:
120
+ contents = ''
121
+ return title + contents
122
+
123
+ def growth(self, full=True) -> str:
124
+ growth_dict = eval_growth(self.client, self.code)
125
+ p1, p2 = score_growth(self.client, self.code)
126
+ logger.info(growth_dict)
127
+
128
+ title = f"Growth\tPoint({p1 + p2})\t{growth_dict['date']}\n"
129
+ if full:
130
+ contents = (f"1. 매출액증가율({p1}): {growth_dict['매출액증가율']}\n"
131
+ f"2. 영업이익률({p2}): {growth_dict['영업이익률']}")
132
+ else:
133
+ contents = (f"1. 매출액증가율({p1}): {growth_dict['매출액증가율'][0]}\n"
134
+ f"2. 영업이익률({p2}): {growth_dict['영업이익률'].get(self.name)}")
135
+ return title + contents
136
+
137
+ def for_django(self) -> dict:
138
+ """
139
+ 장고에서 report 페이지에서 사용될 eval & score data 를 반환
140
+
141
+ 장고의 view context는 딕셔너리 형식이기 때문에 딕셔너리 모음으로 반환한다.
142
+
143
+ 리턴값
144
+ {'blue': {'date': ['2022/12'],
145
+ '순부채비율': (-29.57, {'2018/12': -34.82,...'2023/12': -27.54}),
146
+ '순운전자본회전율': (1.59, {'2018/12': 12.3,...'2023/12': nan}),
147
+ '유동비율': 278.86,
148
+ '이자보상배율': (15.7, {'2018/12': 87.29,...'2023/12': nan}),
149
+ '재고자산회전율': (1.29, {'2018/12': 9.03,...'2023/12': nan})},
150
+ 'blue_s': (0, 0, 0, -1, 0),
151
+ 'c101': {'BPS': 50817.0, 'EPS': 8057.0, 'PBR': 1.28, 'PER': 8.08, 'date': '2023.04.14', 'intro': '...',
152
+ '거래대금': '1062800000000', '거래량': '16176500', '발행주식': '5969782550', '배당수익률': '2.22', '베타52주': '0.95',
153
+ '시가총액': '388632800000000', '업종': '반도체와반도체장비', '업종PER': '8.36', '유통비율': '75.82', '종목명': '삼성전자',
154
+ '주가': '65100', '최고52주': '68800', '최저52주': '51800', '코드': '005930'},
155
+ 'growth': {'date': ['2022/12'],
156
+ '매출액증가율': (-8.23, {'2018/12': 1.75,...'2023/12': -10.5}),
157
+ '영업이익률': {'LG디스플레이': '3.61', 'LG전자': '42.38', 'SK하이닉스': '15.26', '삼성전자': '14.35', '에스에프에이': '45.89'}},
158
+ 'growth_s': (-2, 1),
159
+ 'mil': {'date': ['2022/12'],
160
+ '가치지표': {'FCF': {'2018/12': 374754.5,...'2023/12': 24605.8},
161
+ 'PCR': {'2021/12': 8.17,...'2022/12': 6.04},
162
+ 'PFCF': {'2018/12': 10.37,...'2023/12': 157.94}},
163
+ '이익지표': -0.01917,
164
+ '주주수익률': 4.99,
165
+ '투자수익률': {'ROE': 17.07, 'ROIC': 13.41}},
166
+ 'mil_s': (1, 10, 7, 0),
167
+ 'red': {'date': ['2022/12'], 'red_price': 264881, '발행주식수': 6792669000.0, '부채평가': 1257599.6,
168
+ '사업가치': 13682505.0, '재산가치': 5567563.52},
169
+ 'red_s': (53, -75.42)}
170
+ """
171
+ return {
172
+ 'c101': mongo.C101(self.client, self.code).get_recent(merge_intro=True),
173
+ 'red': eval_red(self.client, self.code),
174
+ 'mil': eval_mil(self.client, self.code),
175
+ 'blue': eval_blue(self.client, self.code),
176
+ 'growth': eval_growth(self.client, self.code),
177
+ # 'c108': mongo.C108(self.client, self.code).get_recent(),
178
+ 'red_s': score_red(self.client, self.code),
179
+ 'mil_s': score_mil(self.client, self.code),
180
+ 'blue_s': score_blue(self.client, self.code),
181
+ 'growth_s': score_growth(self.client, self.code),
182
+ }
183
+
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))