analyser_hj3415 2.6.8__py2.py3-none-any.whl → 2.7.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/cli.py +111 -41
- analyser_hj3415/eval.py +948 -0
- analyser_hj3415/workroom/__init__.py +0 -0
- analyser_hj3415/workroom/lstm.py +113 -0
- analyser_hj3415/workroom/myprophet.py +58 -0
- analyser_hj3415/workroom/mysklearn.py +50 -0
- analyser_hj3415/workroom/mysklearn2.py +39 -0
- analyser_hj3415/{analysers → workroom}/score.py +0 -38
- {analyser_hj3415-2.6.8.dist-info → analyser_hj3415-2.7.0.dist-info}/METADATA +3 -3
- analyser_hj3415-2.7.0.dist-info/RECORD +16 -0
- analyser_hj3415/analysers/eval.py +0 -276
- analyser_hj3415/analysers/report.py +0 -182
- analyser_hj3415/myredis.py +0 -186
- analyser_hj3415/tools.py +0 -247
- analyser_hj3415-2.6.8.dist-info/RECORD +0 -14
- /analyser_hj3415/{trash.py → workroom/trash.py} +0 -0
- {analyser_hj3415-2.6.8.dist-info → analyser_hj3415-2.7.0.dist-info}/LICENSE +0 -0
- {analyser_hj3415-2.6.8.dist-info → analyser_hj3415-2.7.0.dist-info}/WHEEL +0 -0
- {analyser_hj3415-2.6.8.dist-info → analyser_hj3415-2.7.0.dist-info}/entry_points.txt +0 -0
@@ -1,182 +0,0 @@
|
|
1
|
-
"""다양한 문자열 출력 형식에 맞춘 함수들
|
2
|
-
"""
|
3
|
-
from db_hj3415 import myredis
|
4
|
-
from analyser_hj3415.analysers import eval
|
5
|
-
from analyser_hj3415.analysers import score
|
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 = myredis.Corps.get_name(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 = myredis.C101(self.code).get_recent()
|
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.code)
|
69
|
-
괴리율 = score.red(self.code)
|
70
|
-
logger.info(red_dict)
|
71
|
-
|
72
|
-
title = f"Red\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.code)
|
85
|
-
p1, p2, p3, p4 = score.mil(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.code)
|
108
|
-
p1, p2, p3, p4, p5 = score.blue(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.code)
|
125
|
-
p1, p2 = score.growth(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': 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),
|
181
|
-
}
|
182
|
-
|
analyser_hj3415/myredis.py
DELETED
@@ -1,186 +0,0 @@
|
|
1
|
-
from analyser_hj3415.analysers import eval, score
|
2
|
-
from typing import Dict, Tuple
|
3
|
-
from collections import OrderedDict
|
4
|
-
from db_hj3415 import myredis as db_myredis
|
5
|
-
|
6
|
-
page = '.analyser'
|
7
|
-
myredis_base = db_myredis.Base()
|
8
|
-
|
9
|
-
def get_redis_expect_earn(redis_name:str) -> float:
|
10
|
-
redis_name_e = f'{redis_name}_expect_earn'
|
11
|
-
byte_expect_earn = db_myredis.Base.redis_client.get(redis_name_e)
|
12
|
-
if byte_expect_earn is not None:
|
13
|
-
e = float(byte_expect_earn.decode('utf-8'))
|
14
|
-
else:
|
15
|
-
e = float('nan')
|
16
|
-
return e
|
17
|
-
|
18
|
-
def red_ranking(expect_earn: float, refresh=False) -> Tuple[OrderedDict, str]:
|
19
|
-
"""
|
20
|
-
redis를 사용하며 red score를 계산해서 0이상의 값을 가지는 종목을 순서대로 저장하여 반환한다.
|
21
|
-
:param expect_earn: 기대수익률(일반적으로 0.06 - 0.10)
|
22
|
-
:param refresh: 캐시를 사용하지 않고 강제로 다시 계산
|
23
|
-
:return: OrderedDict([('023590', 101),
|
24
|
-
('010060', 91),...]), 레디스이름
|
25
|
-
"""
|
26
|
-
|
27
|
-
print("**** Start red_ranking... ****")
|
28
|
-
redis_name = 'red_ranking'
|
29
|
-
redis_name_e = f'{redis_name}_expect_earn'
|
30
|
-
if get_redis_expect_earn(redis_name) != expect_earn:
|
31
|
-
# 레디스 저장값과 기대수익률이 다르다면 무조건 리프레시 한다.
|
32
|
-
refresh = True
|
33
|
-
db_myredis.Base.redis_client.setex(redis_name_e, db_myredis.Base.DEFAULT_CACHE_EXPIRATION_SEC, expect_earn)
|
34
|
-
|
35
|
-
def fetch_red_scores(expect_earn_i: float) -> Dict:
|
36
|
-
data = {}
|
37
|
-
for i, code in enumerate(db_myredis.Corps.list_all_codes()):
|
38
|
-
red_score = score.red(code, expect_earn_i)
|
39
|
-
if red_score > 0:
|
40
|
-
data[code] = red_score
|
41
|
-
print(f"{i}: {code} - {red_score}")
|
42
|
-
return data
|
43
|
-
|
44
|
-
data = myredis_base.fetch_and_cache_data(redis_name, refresh, fetch_red_scores, expect_earn)
|
45
|
-
return OrderedDict(sorted(data.items(), key=lambda item: item[1], reverse=True)), redis_name
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def red_n_score(code: str, expect_earn: float, refresh=False) -> Tuple[dict, str]:
|
50
|
-
"""
|
51
|
-
red 평가후 스코어 계산후 red 딕셔너리에 첨가후 반환
|
52
|
-
:param code: 종목코드
|
53
|
-
:param expect_earn: 기대수익률(일반적으로 0.06 - 0.10)
|
54
|
-
:param refresh: 캐시를 사용하지 않고 강제로 다시 계산
|
55
|
-
:return:
|
56
|
-
"""
|
57
|
-
redis_name = f"{code}_{page}_red_n_score"
|
58
|
-
redis_name_e = f'{redis_name}_expect_earn'
|
59
|
-
if get_redis_expect_earn(redis_name) != expect_earn:
|
60
|
-
# 레디스 저장값과 기대수익률이 다르다면 무조건 리프레시 한다.
|
61
|
-
refresh = True
|
62
|
-
# print(get_redis_expect_earn(redis_name))
|
63
|
-
db_myredis.Base.redis_client.setex(redis_name_e, db_myredis.Base.DEFAULT_CACHE_EXPIRATION_SEC, expect_earn)
|
64
|
-
|
65
|
-
def fetch_red_n_score(code_i: str, expect_earn_i: float) -> dict:
|
66
|
-
data = eval.red(code_i, expect_earn_i)
|
67
|
-
data['score'] = score.red(code_i, expect_earn_i)
|
68
|
-
return data
|
69
|
-
|
70
|
-
return myredis_base.fetch_and_cache_data(redis_name, refresh, fetch_red_n_score, code, expect_earn), redis_name
|
71
|
-
|
72
|
-
|
73
|
-
def mil_n_score(code: str, expect_earn: float, refresh=False) -> Tuple[dict, str]:
|
74
|
-
"""
|
75
|
-
redis 사용 - 소멸타이머 사용
|
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
|
-
redis_name = f"{code}_{page}_mil_n_score"
|
110
|
-
redis_name_e = f'{redis_name}_expect_earn'
|
111
|
-
if get_redis_expect_earn(redis_name) != expect_earn:
|
112
|
-
# 레디스 저장값과 기대수익률이 다르다면 무조건 리프레시 한다.
|
113
|
-
refresh = True
|
114
|
-
db_myredis.Base.redis_client.setex(redis_name_e, db_myredis.Base.DEFAULT_CACHE_EXPIRATION_SEC, expect_earn)
|
115
|
-
|
116
|
-
def fetch_mil_n_score(code_i: str) -> dict:
|
117
|
-
data = eval.mil(code_i)
|
118
|
-
data['score'] = score.mil(code_i, expect_earn)
|
119
|
-
return data
|
120
|
-
|
121
|
-
return myredis_base.fetch_and_cache_data(redis_name, refresh, fetch_mil_n_score, code), redis_name
|
122
|
-
|
123
|
-
|
124
|
-
def blue_n_score(code: str, refresh=False) -> Tuple[dict, str]:
|
125
|
-
"""
|
126
|
-
redis 사용 - 소멸타이머 사용
|
127
|
-
리턴값
|
128
|
-
{
|
129
|
-
'date': [각 유효한 값의 최근분기 값 리스트(ex- 2020/09)],
|
130
|
-
'순부채비율': (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}),
|
131
|
-
'순운전자본회전율': (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}),
|
132
|
-
'유동비율': 64.29,
|
133
|
-
'이자보상배율': (-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}),
|
134
|
-
'재고자산회전율': (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})
|
135
|
-
}
|
136
|
-
|
137
|
-
<유동비율>
|
138
|
-
100미만이면 주의하나 현금흐름창출력이 좋으면 괜찮을수 있다.
|
139
|
-
만약 100%이하면 유동자산에 추정영업현금흐름을 더해서 다시계산해보아 기회를 준다.
|
140
|
-
<이자보상배율>
|
141
|
-
이자보상배율 영업이익/이자비용으로 1이면 자금사정빡빡 5이상이면 양호
|
142
|
-
<순운전자금회전율>
|
143
|
-
순운전자금 => 기업활동을 하기 위해 필요한 자금 (매출채권 + 재고자산 - 매입채무)
|
144
|
-
순운전자본회전율은 매출액/순운전자본으로 일정비율이 유지되는것이 좋으며 너무 작아지면 순운전자본이 많아졌다는 의미로 재고나 외상이 쌓인다는 뜻
|
145
|
-
<재고자산회전율>
|
146
|
-
재고자산회전율은 매출액/재고자산으로 회전율이 낮을수록 재고가 많다는 이야기이므로 불리 전년도등과 비교해서 큰차이 발생하면 알람.
|
147
|
-
재고자산회전율이 작아지면 재고가 쌓인다는뜻
|
148
|
-
<순부채비율>
|
149
|
-
부채비율은 업종마다 달라 일괄비교 어려우나 순부채 비율이 20%이하인것이 좋고 꾸준히 늘어나지 않는것이 좋다.
|
150
|
-
순부채 비율이 30%이상이면 좋치 않다.
|
151
|
-
<매출액>
|
152
|
-
매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
|
153
|
-
<영업이익률>
|
154
|
-
영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
|
155
|
-
"""
|
156
|
-
redis_name = f"{code}_{page}_blue_n_score"
|
157
|
-
|
158
|
-
def fetch_blue_n_score(code_i: str) -> dict:
|
159
|
-
data = eval.blue(code_i)
|
160
|
-
data['score'] = score.blue(code_i)
|
161
|
-
return data
|
162
|
-
|
163
|
-
return myredis_base.fetch_and_cache_data(redis_name, refresh, fetch_blue_n_score, code), redis_name
|
164
|
-
|
165
|
-
|
166
|
-
def growth_n_score(code: str, refresh=False) -> Tuple[dict, str]:
|
167
|
-
"""
|
168
|
-
redis 사용 - 소멸타이머 사용
|
169
|
-
리턴값
|
170
|
-
{'date': [각 유효한 값의 최근분기 값 리스트(ex- 2020/09)],
|
171
|
-
'매출액증가율': (-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}),
|
172
|
-
'영업이익률': {'뉴프렉스': '17.36', '동일기연': '13.58', '비에이치': '16.23', '에이엔피': '-9.30', '이브이첨단소재': '-4.93'}}
|
173
|
-
|
174
|
-
<매출액>
|
175
|
-
매출액은 어떤경우에도 성장하는 기업이 좋다.매출이 20%씩 늘어나는 종목은 유망한 종목
|
176
|
-
<영업이익률>
|
177
|
-
영업이익률은 기업의 경쟁력척도로 경쟁사에 비해 높으면 경제적해자를 갖춘셈
|
178
|
-
"""
|
179
|
-
redis_name = f"{code}_{page}_growth_n_score"
|
180
|
-
|
181
|
-
def fetch_growth_n_score(code_i: str) -> dict:
|
182
|
-
data = eval.growth(code_i)
|
183
|
-
data['score'] = score.growth(code_i)
|
184
|
-
return data
|
185
|
-
|
186
|
-
return myredis_base.fetch_and_cache_data(redis_name, refresh, fetch_growth_n_score, code), redis_name
|
analyser_hj3415/tools.py
DELETED
@@ -1,247 +0,0 @@
|
|
1
|
-
import math
|
2
|
-
from typing import Tuple
|
3
|
-
from db_hj3415.myredis import C101, C103, C104
|
4
|
-
|
5
|
-
import logging
|
6
|
-
|
7
|
-
logger = logging.getLogger(__name__)
|
8
|
-
formatter = logging.Formatter('%(levelname)s: [%(name)s] %(message)s')
|
9
|
-
ch = logging.StreamHandler()
|
10
|
-
ch.setFormatter(formatter)
|
11
|
-
logger.addHandler(ch)
|
12
|
-
logger.setLevel(logging.WARNING)
|
13
|
-
|
14
|
-
|
15
|
-
def set_data(*args) -> list:
|
16
|
-
"""
|
17
|
-
비유효한 내용 제거(None,nan)하고 중복된 항목 제거하고 리스트로 반환한다.
|
18
|
-
여기서 set의 의미는 집합을 뜻함
|
19
|
-
:param args:
|
20
|
-
:return:
|
21
|
-
"""
|
22
|
-
return [i for i in {*args} if i != "" and i is not math.nan and i is not None]
|
23
|
-
|
24
|
-
|
25
|
-
def calc당기순이익(code: str) -> Tuple[str, float]:
|
26
|
-
"""지배지분 당기순이익 계산
|
27
|
-
|
28
|
-
일반적인 경우로는 직전 지배주주지분 당기순이익을 찾아서 반환한다.\n
|
29
|
-
금융기관의 경우는 지배당기순이익이 없기 때문에\n
|
30
|
-
계산을 통해서 간접적으로 구한다.\n
|
31
|
-
"""
|
32
|
-
logger.debug(f'In the calc당기순이익... code:{code}')
|
33
|
-
c103q = C103(code, 'c103재무상태표q')
|
34
|
-
try:
|
35
|
-
# print("*(지배)당기순이익: ", c103q.latest_value_pop2('*(지배)당기순이익'))
|
36
|
-
return c103q.latest_value_pop2('*(지배)당기순이익')
|
37
|
-
except:
|
38
|
-
logger.warning(f"{code} - (지배)당기순이익이 없는 종목. 수동으로 계산합니다(금융관련업종일 가능성있음).")
|
39
|
-
c103q.page = 'c103손익계산서q'
|
40
|
-
최근당기순이익date, 최근당기순이익value = c103q.sum_recent_4q('당기순이익')
|
41
|
-
c103q.page = 'c103재무상태표q'
|
42
|
-
비지배당기순이익date, 비지배당기순이익value = c103q.latest_value_pop2('*(비지배)당기순이익')
|
43
|
-
|
44
|
-
# 가변리스트 언패킹으로 하나의 날짜만 사용하고 나머지는 버린다.
|
45
|
-
date, *_ = set_data(최근당기순이익date, 비지배당기순이익date)
|
46
|
-
계산된지배당기순이익value = 최근당기순이익value - 비지배당기순이익value
|
47
|
-
|
48
|
-
return date, 계산된지배당기순이익value
|
49
|
-
|
50
|
-
|
51
|
-
def calc유동자산(code: str) -> Tuple[str, float]:
|
52
|
-
"""유효한 유동자산 계산
|
53
|
-
|
54
|
-
일반적인 경우로 유동자산을 찾아서 반환한다.\n
|
55
|
-
금융기관의 경우는 간접적으로 계산한다.\n
|
56
|
-
Red와 Blue에서 사용한다.\n
|
57
|
-
"""
|
58
|
-
logger.debug(f'In the calc유동자산... code:{code}')
|
59
|
-
c103q = C103(code, 'c103재무상태표q')
|
60
|
-
try:
|
61
|
-
return c103q.sum_recent_4q('유동자산')
|
62
|
-
except:
|
63
|
-
logger.warning(f"{code} - 유동자산이 없는 종목. 수동으로 계산합니다(금융관련업종일 가능성있음).")
|
64
|
-
d1, v1 = c103q.latest_value_pop2('현금및예치금')
|
65
|
-
d2, v2 = c103q.latest_value_pop2('단기매매금융자산')
|
66
|
-
d3, v3 = c103q.latest_value_pop2('매도가능금융자산')
|
67
|
-
d4, v4 = c103q.latest_value_pop2('만기보유금융자산')
|
68
|
-
logger.debug(f'현금및예치금 : {d1}, {v1}')
|
69
|
-
logger.debug(f'단기매매금융자산 : {d2}, {v2}')
|
70
|
-
logger.debug(f'매도가능금융자산 : {d3}, {v3}')
|
71
|
-
logger.debug(f'만기보유금융자산 : {d4}, {v4}')
|
72
|
-
|
73
|
-
date, *_ = set_data(d1, d2, d3, d4)
|
74
|
-
계산된유동자산value = v1 + v2 + v3 + v4
|
75
|
-
|
76
|
-
return date, 계산된유동자산value
|
77
|
-
|
78
|
-
|
79
|
-
def calc유동부채(code: str) -> Tuple[str, float]:
|
80
|
-
"""유효한 유동부채 계산
|
81
|
-
|
82
|
-
일반적인 경우로 유동부채를 찾아서 반환한다.\n
|
83
|
-
금융기관의 경우는 간접적으로 계산한다.\n
|
84
|
-
Red와 Blue에서 사용한다.\n
|
85
|
-
"""
|
86
|
-
logger.debug(f'In the calc유동부채... code:{code}')
|
87
|
-
c103q = C103(code, 'c103재무상태표q')
|
88
|
-
try:
|
89
|
-
return c103q.sum_recent_4q('유동부채')
|
90
|
-
except:
|
91
|
-
logger.warning(f"{code} - 유동부채가 없는 종목. 수동으로 계산합니다(금융관련업종일 가능성있음).")
|
92
|
-
d1, v1 = c103q.latest_value_pop2('당기손익인식(지정)금융부채')
|
93
|
-
d2, v2 = c103q.latest_value_pop2('당기손익-공정가치측정금융부채')
|
94
|
-
d3, v3 = c103q.latest_value_pop2('매도파생결합증권')
|
95
|
-
d4, v4 = c103q.latest_value_pop2('단기매매금융부채')
|
96
|
-
logger.debug(f'당기손익인식(지정)금융부채 : {d1}, {v1}')
|
97
|
-
logger.debug(f'당기손익-공정가치측정금융부채 : {d2}, {v2}')
|
98
|
-
logger.debug(f'매도파생결합증권 : {d3}, {v3}')
|
99
|
-
logger.debug(f'단기매매금융부채 : {d4}, {v4}')
|
100
|
-
|
101
|
-
date, *_ = set_data(d1, d2, d3, d4)
|
102
|
-
계산된유동부채value = v1 + v2 + v3 + v4
|
103
|
-
|
104
|
-
return date, 계산된유동부채value
|
105
|
-
|
106
|
-
|
107
|
-
def calc비유동부채(code: str) -> Tuple[str, float]:
|
108
|
-
"""유효한 비유동부채 계산
|
109
|
-
|
110
|
-
일반적인 경우로 비유동부채를 찾아서 반환한다.\n
|
111
|
-
금융기관의 경우는 간접적으로 계산한다.\n
|
112
|
-
Red와 Blue에서 사용한다.\n
|
113
|
-
"""
|
114
|
-
logger.debug(f'In the calc비유동부채... code:{code}')
|
115
|
-
c103q = C103(code, 'c103재무상태표q')
|
116
|
-
try:
|
117
|
-
return c103q.sum_recent_4q('비유동부채')
|
118
|
-
except:
|
119
|
-
logger.warning(f"{code} - 비유동부채가 없는 종목. 수동으로 계산합니다(금융관련업종일 가능성있음).")
|
120
|
-
# 보험관련업종은 예수부채가 없는대신 보험계약부채가 있다...
|
121
|
-
d1, v1 = c103q.latest_value_pop2('예수부채')
|
122
|
-
d2, v2 = c103q.latest_value_pop2('보험계약부채(책임준비금)')
|
123
|
-
d3, v3 = c103q.latest_value_pop2('차입부채')
|
124
|
-
d4, v4 = c103q.latest_value_pop2('기타부채')
|
125
|
-
logger.debug(f'예수부채 : {d1}, {v1}')
|
126
|
-
logger.debug(f'보험계약부채(책임준비금) : {d2}, {v2}')
|
127
|
-
logger.debug(f'차입부채 : {d3}, {v3}')
|
128
|
-
logger.debug(f'기타부채 : {d4}, {v4}')
|
129
|
-
|
130
|
-
date, *_ = set_data(d1, d2, d3, d4)
|
131
|
-
계산된비유동부채value = v1 + v2 + v3 + v4
|
132
|
-
|
133
|
-
return date, 계산된비유동부채value
|
134
|
-
|
135
|
-
|
136
|
-
def calc유동비율(code: str, pop_count: int) -> Tuple[str, float]:
|
137
|
-
"""유동비율계산 - Blue에서 사용
|
138
|
-
|
139
|
-
c104q에서 최근유동비율 찾아보고 유효하지 않거나 \n
|
140
|
-
100이하인 경우에는수동으로 계산해서 다시 한번 평가해 본다.\n
|
141
|
-
"""
|
142
|
-
logger.debug(f'In the calc유동비율... code:{code}')
|
143
|
-
c104q = C104(code, 'c104q')
|
144
|
-
유동비율date, 유동비율value = c104q.mymongo_c1034.latest_value('유동비율', pop_count=pop_count)
|
145
|
-
logger.debug(f'{code} 유동비율 : {유동비율value}({유동비율date})')
|
146
|
-
|
147
|
-
if math.isnan(유동비율value) or 유동비율value < 100:
|
148
|
-
logger.warning('유동비율 is under 100 or nan..so we will recalculate..')
|
149
|
-
유동자산date, 유동자산value = calc유동자산(code)
|
150
|
-
유동부채date, 유동부채value = calc유동부채(code)
|
151
|
-
|
152
|
-
c103q = C103(code, 'c103현금흐름표q')
|
153
|
-
추정영업현금흐름date, 추정영업현금흐름value = c103q.sum_recent_4q('영업활동으로인한현금흐름')
|
154
|
-
logger.debug(f'{code} 계산전 유동비율 : {유동비율value}({유동비율date})')
|
155
|
-
|
156
|
-
계산된유동비율 = 0
|
157
|
-
try:
|
158
|
-
계산된유동비율 = round(((유동자산value + 추정영업현금흐름value) / 유동부채value) * 100, 2)
|
159
|
-
except ZeroDivisionError:
|
160
|
-
logger.debug(f'유동자산: {유동자산value} + 추정영업현금흐름: {추정영업현금흐름value} / 유동부채: {유동부채value}')
|
161
|
-
계산된유동비율 = float('inf')
|
162
|
-
finally:
|
163
|
-
logger.debug(f'{code} 계산된 유동비율 : {계산된유동비율}')
|
164
|
-
date, *_ = set_data(유동자산date, 유동부채date, 추정영업현금흐름date)
|
165
|
-
return date, 계산된유동비율
|
166
|
-
else:
|
167
|
-
return 유동비율date, 유동비율value
|
168
|
-
|
169
|
-
|
170
|
-
def findFCF(code: str) -> dict:
|
171
|
-
"""
|
172
|
-
FCF 계산
|
173
|
-
Returns:
|
174
|
-
dict: 계산된 fcf 딕셔너리 또는 영업현금흐름 없는 경우 - {}
|
175
|
-
|
176
|
-
Note:
|
177
|
-
CAPEX 가 없는 업종은 영업활동현금흐름을 그대로 사용한다.\n
|
178
|
-
|
179
|
-
"""
|
180
|
-
c103y = C103(code, 'c103현금흐름표y')
|
181
|
-
_, 영업활동현금흐름_dict = c103y.find_without_yoy('영업활동으로인한현금흐름')
|
182
|
-
c103y.page = 'c103재무상태표y'
|
183
|
-
_, capex = c103y.find_without_yoy('*CAPEX')
|
184
|
-
|
185
|
-
logger.debug(f'영업활동현금흐름 {영업활동현금흐름_dict}')
|
186
|
-
logger.debug(f'CAPEX {capex}')
|
187
|
-
|
188
|
-
if len(영업활동현금흐름_dict) == 0:
|
189
|
-
return {}
|
190
|
-
|
191
|
-
if len(capex) == 0:
|
192
|
-
# CAPEX 가 없는 업종은 영업활동현금흐름을 그대로 사용한다.
|
193
|
-
logger.warning(f"{code} - CAPEX가 없는 업종으로 영업현금흐름을 그대로 사용합니다..")
|
194
|
-
return 영업활동현금흐름_dict
|
195
|
-
|
196
|
-
# 영업 활동으로 인한 현금 흐름에서 CAPEX 를 각 연도별로 빼주어 fcf 를 구하고 리턴값으로 fcf 딕셔너리를 반환한다.
|
197
|
-
r_dict = {}
|
198
|
-
for i in range(len(영업활동현금흐름_dict)):
|
199
|
-
# 영업활동현금흐름에서 아이템을 하나씩 꺼내서 CAPEX 전체와 비교하여 같으면 차를 구해서 r_dict 에 추가한다.
|
200
|
-
영업활동현금흐름date, 영업활동현금흐름value = 영업활동현금흐름_dict.popitem()
|
201
|
-
# 해당 연도의 capex 가 없는 경우도 있어 일단 capex를 0으로 치고 먼저 추가한다.
|
202
|
-
r_dict[영업활동현금흐름date] = 영업활동현금흐름value
|
203
|
-
for CAPEXdate, CAPEXvalue in capex.items():
|
204
|
-
if 영업활동현금흐름date == CAPEXdate:
|
205
|
-
r_dict[영업활동현금흐름date] = round(영업활동현금흐름value - CAPEXvalue, 2)
|
206
|
-
logger.debug(f'r_dict {r_dict}')
|
207
|
-
# 연도순으로 정렬해서 딕셔너리로 반환한다.
|
208
|
-
return dict(sorted(r_dict.items(), reverse=False))
|
209
|
-
|
210
|
-
|
211
|
-
def findPFCF(code: str) -> dict:
|
212
|
-
"""Price to Free Cash Flow Ratio(주가 대비 자유 현금 흐름 비율)계산
|
213
|
-
|
214
|
-
PFCF = 시가총액 / FCF
|
215
|
-
|
216
|
-
Note:
|
217
|
-
https://www.investopedia.com/terms/p/pricetofreecashflow.asp
|
218
|
-
"""
|
219
|
-
# marketcap 계산 (fcf가 억 단위라 시가총액을 억으로 나눠서 단위를 맞춰 준다)
|
220
|
-
marketcap억 = get_marketcap(code) / 100000000
|
221
|
-
if math.isnan(marketcap억):
|
222
|
-
return {}
|
223
|
-
|
224
|
-
# pfcf 계산
|
225
|
-
fcf_dict = findFCF(code)
|
226
|
-
logger.debug(f'fcf_dict : {fcf_dict}')
|
227
|
-
pfcf_dict = {}
|
228
|
-
for FCFdate, FCFvalue in fcf_dict.items():
|
229
|
-
if FCFvalue == 0:
|
230
|
-
pfcf_dict[FCFdate] = math.nan
|
231
|
-
else:
|
232
|
-
pfcf_dict[FCFdate] = round(marketcap억 / FCFvalue, 2)
|
233
|
-
logger.debug(f'pfcf_dict : {pfcf_dict}')
|
234
|
-
return pfcf_dict
|
235
|
-
|
236
|
-
|
237
|
-
def get_marketcap(code: str) -> float:
|
238
|
-
"""
|
239
|
-
시가총액(원) 반환
|
240
|
-
:param code:
|
241
|
-
:return:
|
242
|
-
"""
|
243
|
-
c101 = C101(code)
|
244
|
-
try:
|
245
|
-
return int(c101.get_recent()['시가총액'])
|
246
|
-
except KeyError:
|
247
|
-
return math.nan
|
@@ -1,14 +0,0 @@
|
|
1
|
-
analyser_hj3415/.DS_Store,sha256=qr9-0FPn5CFKe6kEu8_dWCNhzQ0sN7bwQgffKsaJEEo,6148
|
2
|
-
analyser_hj3415/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
analyser_hj3415/cli.py,sha256=eLo3U2VpBXVFwCCwxvge-nLc6wEeWWCbcORHRFELAFs,7356
|
4
|
-
analyser_hj3415/myredis.py,sha256=e9WH9Vbyph3jsA1TO9KWjow6-wxct4vLwINjRVHw1IQ,9609
|
5
|
-
analyser_hj3415/tools.py,sha256=lyfvmze9e4lGqgimuVOXUbgt7YNaJ7h8PpC7dWaaW94,10942
|
6
|
-
analyser_hj3415/trash.py,sha256=zF-W0piqkGr66UP6-iybo9EXh2gO0RP6R1FnIpsGkl8,12262
|
7
|
-
analyser_hj3415/analysers/eval.py,sha256=Ihx1UiNbrUUPgaZLNvbWQfO_aAwlB9vxREXJl7HNA_c,12072
|
8
|
-
analyser_hj3415/analysers/report.py,sha256=yY-AeDHOqDPrm4lpvZv3NPTJK1Z7lKsjoO5ck8AP6u4,9007
|
9
|
-
analyser_hj3415/analysers/score.py,sha256=8dlnYu-iQiwY-pkKH4N05MVanVGVsOajmryVR9_BYMI,18534
|
10
|
-
analyser_hj3415-2.6.8.dist-info/entry_points.txt,sha256=ZfjPnJuH8SzvhE9vftIPMBIofsc65IAWYOhqOC_L5ck,65
|
11
|
-
analyser_hj3415-2.6.8.dist-info/LICENSE,sha256=QVKTp0dTnB5xG8RLgG17LwSWCKNEzYoVVM6KjoCPKc0,1079
|
12
|
-
analyser_hj3415-2.6.8.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
|
13
|
-
analyser_hj3415-2.6.8.dist-info/METADATA,sha256=cXfIcA1mrmJ2lLnnEG7xU8kBhS0G2E8H4LfiPy01r-g,6417
|
14
|
-
analyser_hj3415-2.6.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|