analyser_hj3415 2.6.8__py2.py3-none-any.whl → 2.7.1__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/cli.py +115 -45
- 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.1.dist-info}/METADATA +3 -3
- analyser_hj3415-2.7.1.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.1.dist-info}/LICENSE +0 -0
- {analyser_hj3415-2.6.8.dist-info → analyser_hj3415-2.7.1.dist-info}/WHEEL +0 -0
- {analyser_hj3415-2.6.8.dist-info → analyser_hj3415-2.7.1.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
|