5월 20일 오늘 기준으로 주가를 크롤링해서 5월 20일 기준의 적정주가를 계산해보았다. 

2020.05.20적정주가.csv

물론 위에 첨부한 적정주가는, 단순하게 계산한 것이기 때문에, 기업분석의 우선순위를 줄 순 있어도, 상위에 있다고 무조건 저평가된 저렴한 주식이라고 해석할 수는 없다.

그래서 상위에 있는 것들 중에 눈에 띄는 애들을 순서로 재무제표를 확인하고 -> dart 보고서를 읽고, 괜찮아보이는지 판단하는 게 더 좋은 접근이라고 생각한다.


전체종목에 대해서 하게 되면 시간이 많이 걸려서, 

5년간 영업이익, 당기순이익, ROE, 배당이 플러스인 것으로 조건을 걸었고 그러니 한 500개 정도의 종목이 나오는 듯 하다.


1분기 실적도 이제 많은 회사들이 다 나온 것 같고,

추가로 발굴할만한 종목이 있는지 한 번 살펴볼 필요가 있을 것 같다! 


앞으로 옆에 업종 평균 PER, PBR, 배당을 붙여서 업종간 비교를 할만한 잣대를 만들어주면 좋은데, 자꾸 미루게된다. 


다음에 적정주가 계산한 자료를 올릴 때에는 업종 평균치에 대한 정보들도 함께 붙여보아야겠다:)


요근래 코스피도 많이 회복된 것 같고, 주식 수익률도 나쁘지 않다. 다만 조금 더 천천히 올라주지.. 확 오른 느낌이라 아쉽긴 하다. 

그럼에도 저평가 되있는 종목이 있는지 주말간 차차 살펴보아야겠다. 

지금 눈에 띄는 것은 참좋은여행.. 코로나 때문에 많이 떨어졌는데. 그게 BM을 흐트리진 않는지, 그런걸 대비할 현금이 두둑히 있는지. 나중에 살펴봐야겠다. 

728x90

한국거래소에서 크롤링을 통해 종목코드, 종목명, 업종에 대한 크롤링을 할 수가 있다. 


그걸 Fnguide에 있는 재무제표, 가치지표와 엮으면,

업종에 속해있는 회사의 수, 평균 PER, 평균 PBR, 평균 ROE, 평균 배당률, 평균 부채비율을 계산할 수 있다. 


PER이 낮고 PBR이 낮으면 회사가 저평가되있다고 말하는 경우가 있지만, 산업별로 그 잣대를 다르게 하는 것이 맞다고 생각하기 때문에, 

실제 한 종목에 대한 기업분석을 할 때, 업종별 정보가 어떤 기준이 되는 잣대가 되면 좋겠다는 생각에서 한번 살펴보았다. 


* 우선 기준은 2019.12월 기준의 재무제표를 크롤링한 것이기 때문에 업종수나, 평균 가치지표는 시점에 따라 상이해질 수는 있다.


과거에 종목정보(code)와 재무제표(data)정보를 크롤링한 글을 작성했었고, 그때 저장해놓은 database를 아래 쿼리를 통해 불러왔다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
select 
industry
, count(distinct code) as company_cnt
, sum(PER)/count(*) as avg_PER
, sum(PBR)/count(*) as avg_PBR
, sum(ROE)/count(*) as avg_ROE
, sum(배당수익률)/count(*) as avg_distri
, sum(부채비율)/count(*) as debt_rate
, sum(매출액) as total_revenue
from (
    select a.industry, b.*
    from code a
    join data b 
    on a.code = b.code
) a 
where year = '2019/12'
 
group by industry
order by avg_PER asc 
cs



우리나라에 얼마나 다양한 업종이 있을까? 매출 그리고 회사수 기준으로 봤을 때 가장 규모가 큰 업종은 무엇일까?



우리나라에는 156개의 업종이 있다. 먼저 매출규모 기준으로 정렬해보았을 때, 

통신 및 방송 장비 제조업이 가장 먼저 나오고, 그다음으로 기타금융업, 자동차용 엔진 및 자동차 제조업이 나온다. 

기타 금융업이 역시 평균적인 PER이나 PBR이 낮은 편이고 배당률이 높은 것을 확인할 수 있고, 

고배당주가 많은 석유 정제품 제조업 산업도 보면, 평균배당률이 2.82%나 됨을 바로 확인할 수 있었다.


(은행~ 보험업은 재무제표상 매출액이 아니라 순이자손익같이 다른 표현으로 적혀있어서, 크롤링이 안된거 같은데, 이거는 나중에 보정이 필요해보인다!!)


역시나 쉽게 예측가능하듯이, 1위 업종에는 삼성전자가 포함되어있다. 2019년 12월 매출액이 230조가 넘는다 ㅎㅎ 역시 어마어마한 기업이다. 


그럼 우리나라에 회사 수가 가장 많은 업종 순으로 정렬해보면 어떻게 될까? 



우리나라는 제조업 기반의 회사이기 때문에, 

특수 목적용 기계 '제조업', 전자부품 '제조업', 의약품 '제조업' 등.. 제조업이 참 많음을 확인할 수 있다. 


여기서 내 예상과 달리, 소프트웨어 개발 및 공급업이 2번째로 많은 업종임을 확인할 수 있었다.

(대충 토탈 매출액을 보았을 때, 2번째로 회사 수가 많은 업종인데, 매출액 규모가 작은걸로 봐서 코스닥에 해당 회사가 많이 상장 되어 있을 것 같다는 생각이 들었고, 

나중에는 코스닥과 코스피를 분리해서 보는 것도 재미있겠다는 생각이 들었다.)


그럼 소프트웨어 개발 및 공급업에는 어느 회사들이 있을까? 


넷마블이 가장 먼저 나왔다. 카카오나 네이버가 제일 먼저 나올 줄 알았는데, 또 업종이 오묘하게 다른가보다. 


카카오와 네이버는 '자료처리, 호스팅, 포털 및 기타 인터넷 정보매개 서비스업'으로 분류되어 있었다 ㅎㅎ 

IT기업이라 같은 업종으로 분류될 거라 생각했는데 생각보다 세부적으로 분류되있다는 느낌이 들었고, 나중에는 이것들을 clustering해서 같은 업종 별로 묶어서 지표를 보는 것도 의미가 있을 것 같다는 생각이 들었다.


다음번에는 Heatmap을 통해서 시각화해서 이것저것 인사이트를 줄 만한 게 무엇이 있을지 확인해서 정리해보려고 한다. 

그리고 이후에는 적정주가 계산하는 output에 회사의 지표, 업종 평균 지표를 column으로 추가하면서 다른 업종이라면 다른 기준을 두고 분석할 수 있도록 해야겠다. 

최종적으로는 이런 프로세스가 안정화가 되었을 때, 매주 한번씩 해당 tistory 에 csv파일로 공유하는 것이 목표이다!! 


728x90

이전까지 적정주가를 계산하고, 이를 기반으로 기대수익률을 구해보았다. 


사실 가치지표와 재무지표 숫자들만 가지고 간단하게 적정주가를 계산한 것이기 때문에, 

경영진이나 아니면 시계열적인 회사의 변화나 경영환경의 변화들이 해당 기대수익률에 반영이 되지는 않는다. 


그래서 기대수익률만을 기반으로 투자를 할 생각은 없다.


하지만, 내가 분석을 잘못해서 골랐다고 하더라도, 실제 기대수익률이 높은, 그러니까 저평가된 종목이 실제로도 수익률이 높을 지 궁금해졌다. 


그래서 처음 적정주가를 계산한 4.17일과 4.29일 간의 수익률을 한번 비교해보고자 한다. 


물론 굉장히 짧은 기간이고, 가치투자라는 게 생각보다 장기적인 관점에서 주가가 올라갈 때까지 기다리는 것이지만, 

'코로나'라는 특이한 상황에 의해서 단기간에 주가가 올랐기 때문에, 정말 주가가 급등하는 시기에 기대수익률이 높은 게, 수익을 더 많이 안겨주는지를 확인해 볼 수 있을거라 생각했다. 


코스피가 3.19일이 최저점이어서 해당 주가를 크롤링하려했는데, 시간이 오래걸려서 우선은 4.17과 4.29를 비교해 보려하며,

생각보다 의미있는 결과가 나온것 같아서 틈틈히 주기적으로 확인해봐도 괜찮을 것 같다는 생각이 들었다. 


먼저, 4.19일의 기대수익률과 종가를 불러오고, 4.29일의 종가를 불러왔다. 

그리고 기대수익률에 대한 구간을 나눠서, 해당 구간 내의 평균 수익률을 한번 나타내보았다. 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
select 
case when return_rate > 100 then '1.기대수익률 100%~' 
     when return_rate > 75 then '2.기대수익률 75%~100%'
     when return_rate > 50 then '3.기대수익률 50%~75%'
     when return_rate > 25 then '4.기대수익률 25%~50%'
     when return_rate > 0 then '5.기대수익률 0%~25%'
     when return_rate > -25 then '6.기대수익률 -25%~0%'
     when return_rate > -50 then '7.기대수익률 -50%~-25%' 
     when return_rate > -75 then '8.기대수익률 -75%~-50%' 
     when return_rate > -100 then '9.기대수익률 -100%~-75%'      
     else '91.기대수익률 ~-100%' end as gubun
 
, round(sum(rate)/count(*),2) as avg_rate
from 
(
    select code, name, industry, main_product, round((end_price_4_29/ end_price - 1 ) * 100,2) as rate, return_rate,
    ntile(100) over (order by return_rate desc) as percentile, row_number() over(order by return_rate desc) as idx 
    from result
) a 
group by gubun
order by gubun asc
 
cs


대충 이렇게 쿼리를 작성하였다. 

기대수익률이 100%가 넘었던 종목, 75%~100% 사이인 종목 등으로 구분을 하였고, 그 구분에 따른 평균 수익률을 계산해보았다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
= test['quantile']
= test['cnt']
 
fig, ax = plt.subplots(figsize=(20,6))  
plt.plot(
    x, y, c="orange", ls = '-', marker = 'o', ms = 6
)
 
fig.suptitle("구간 별 수익률", size = 15, weight = 'bold', color='#3D2E2A')
fig.subplots_adjust(top=0.92# adjust the placing of subplot, adjust top, bottom, left and right spacing  
 
ax.set_xticks(x)
ax.tick_params(axis='y', which='both', labelsize=14, labelcolor = '#9B8C8E', color='#9B8C8E')
 
 
cs


seaborn을 통해서 간단하게 그래프를 그려보았고, 결과는 아래와 같다. 





우선 해당 기간동안 장이 좋았기 때문에 (물론 오늘 코스피는 2%넘게 하락하였지만) 구간별 수익률이 다 +이고, 중간에 업다운은 있지만, 

기대수익률이 낮을 수록, 실제 수익률이 낮아지는 경향성을 보임을 확인할 수 있었다.  


해당 수익률은 종목 2,000여개 전체에 대해서 한것이지만, 


실제 일반적으로 나는 '5개년간 ROE>0, 영업이익 >0, 배당수익률 > 0, 직전년도 배당수익률 >3%' 조건을 하나 더 걸어서 본다. 그렇게 조건을 두니, 

현재기준으로 139개의 종목이 나왔고, 그것을 바탕으고 구간 별 수익률을 구해보았다. 



우선 위의 조건을 걸게 되니, 기대수익률이 -75% 미만인 경우는 없나보다. 

여기서는 위에서보다, 기대수익률이 낮을수록 실제 수익률이 낮아지는 경향이 조금 더 뚜렷하게 보여진다. 


생각보다 적정주가가 저평가되있다면, 위로 올라갈 상승여력이 있고, 하방이 낮기 때문에, 적정주가 계산하는 것은 괜찮은 방법이라는 생각이 다시한번 들었다!


나중에 1월과 3.19일 급락때도, 저평가되있는 종목이 하락률이 더 낮았는지도 확인을 해봐야겠다. 

주가 크롤링이 page가 넘어가면 잘 안되는 경향이 있는데, 한번 확인해보면 재밌을 것 같다.

728x90

(1) 5개년치 재무제표 불러오기 (fnguide)

(2) 종목 별 주가(가장 최근) 불러오기

(3) Database에 저장하기

(4) PER&EPS, PBR&BPS, S-RIM으로 적정주가 산출 및 기대수익률 구하기

(5) 기대수익률 순으로 Sorting하기

(6) Hyper Parameter 기반으로, 조건 정교화하기

위의 순서대로 적정주가 계산하는 간단한 프로그램을 만들려고 한다. (파이썬을 이용하였다.)

 

-----------------------------------------------------------

 

제목을 자동화라고 거창하게 적었지만 사실 별게 있진 않다. 

 

기존에는 주가 날짜를 '날짜 = '2020.04.17'' 과 같은식으로 직접 입력해서 ipynb를 통해 수동으로 돌렸다. 

하지만, 이제는 어느정도 체계가 잡혔기 때문에, stock.py 파이썬 파일을 만들고, 쉘스크립트에서 돌릴 수 있도록 수정하였다.

 

3시30분에 한국주식장이 종료되니까, 그날 종가를 기준으로 적정주가를 계산할 수도 있겠지만, 

하루의 변동폭이 엄청나게 큰 것은 아니기 때문에, '어제' 날짜를 불러와서 적정주가를 계산하도록 만들어놓았다. 

1
2
3
4
5
from datetime import date, timedelta
 
yesterday = date.today() - timedelta(1)
date = yesterday.strftime('%Y.%m.%d'
 
cs

이렇게 어제 날짜를 불러오고, 이를 print해보면 현재 글을 쓴 기준인 5.1일의 하루전인 '2020.4.30'이 print되는 것을 확인할 수 있다. 

물론... 이날은 공휴일이기 때문에 주식장이 열리지 않는다. 그래서 Test는 다른날짜로 임의로 수정해서 돌려보았다. 

이렇게 date를 만들어놓고, 하드코딩으로 넣었던 날짜를 date로 수정하고, stock.py파일을 만들면 기본적인 것은 완성이다. 

 

이런식으로 stock.py 파일이 생성이 되었고, 이걸 돌리는 run.sh 을 만들었다. 

사실 매개변수를 python파일 안에서 설정해주기 때문에, 그냥 그대로 돌려도 괜찮을 것 같긴 하지만, 우선은 쉘에서 돌리는 것으로 해놓았다.

 

너무 단순하지만, 우선 stock.py가 있는 경로를 설정해서 python을 돌리도록 만들어놓았다. 

그다음 sh.run을 하게 되면 실행이 된다! 다행히 한방에 되었다.

 

이렇게 종목 하나하나에 대해서 크롤링이 되고, 완성이 되면, csv파일로 해당일자에 대한 적정주가가 떨궈진다. 

 

어제 날짜가 공휴일이어서, 우선 임의로 4.17일로 만들었는데, 다행히 잘 생성이 되었다. 

이제 종목분석을 시작하기 전에 이 python 스크립트를 실행시킨 후, 어떤 게 기대수익률이 높은지를 확인할 수 있게 되었다. 

 

내가 원하는 기본적인 틀은 완성이 되었고,

나중에 조금 더 필터조건을 정교하게 하는 것,

그리고 왜 이게 기대수익률이 높은 것으로 계산되었는지에 대한 설명변수(shap)를 추가할 수 있도록 하려한다.

그리고 나중에는 word2vec을 통해서 기업간의 유사도를 확인해보거나, nlp를 활용해서 업종 / 주요상품 데이터를 활용해서 동종업종을 군집화하고, 동종업종간의 기대수익률을 비교해보려고 한다. 아직 지식이 짧아 언제 하게 될지 모르겠지만, 찬찬히 시도해봐야지. 

728x90

(1) 5개년치 재무제표 불러오기 (fnguide)

(2) 종목 별 주가(가장 최근) 불러오기

(3) Database에 저장하기

(4) PER&EPS, PBR&BPS, S-RIM으로 적정주가 산출 및 기대수익률 구하기

(5) 기대수익률 순으로 Sorting하기

(6) Hyper Parameter 기반으로, 조건 정교화하기

위의 순서대로 적정주가 계산하는 간단한 프로그램을 만들려고 한다. (파이썬을 이용하였다.)

 

-----------------------------------------------------------

 

재무제표 불러왔고, 주가도 불러왔다. 

 

이걸 활용해서, 적정주가를 산출하고, 그걸 통해 기대수익률을 계산하고자 한다. 

적정주가를 계산하는 방법은 다양하게 있는데 비교적 단순하게 계산가능하고 의미가 직관적인 PER*EPS, PBR&BPS를 활용하였고,

사경인 회계사가 쓴 책에 나오는 S-RIM을 활용하였다. 

 

우선 아래와 같은 산식으로 계산을 하였고, 기대수익률은 보수적으로 저 3가지 적정주가 중에 가장 비싼것과 비교를 하였다.

 

PER&EPS : EPS * (5개년 PER평균) 

PBR&BPS : BPS * (5개년 PBR평균) 

S-RIM : BPS * (ROE 가중평균)/기대수익률 

 

작성한 쿼리는 아래와 같다.

여기서 몇가지 조건을 더 추가하였는데, 

5년간 당기순이익, 영업이익, ROE, 배당수익률이 > 0 인 종목만 가져오도록 하였다. 

 

나는 배당을 잘주고, 이익이 꾸준히 나는 회사를 선호한다. 좀 더 조건을 보수적으로 줄 수도 있지만,

우선은 정말 최소한의 조건으로 filter를 걸었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
select 
code
, name
, industry
, main_product
, end_price
, per_eps
, pbr_bps
, bps * ((3*roe_1 + 2*roe_2 + 1*roe_3)/6)/8 as srim
, case when per_eps > pbr_bps and  bps * ((3*roe_1 + 2*roe_2 + 1*roe_3)/6)/8 > pbr_bps 
        then (pbr_bps-end_price)*100/end_price 
       when pbr_bps > per_eps and  bps * ((3*roe_1 + 2*roe_2 + 1*roe_3)/6)/8 > per_eps 
        then (per_eps-end_price)*100/end_price
  else (bps * ((3*roe_1 + 2*roe_2 + 1*roe_3)/6)/8 - end_price)*100/end_price end as return_rate
from
(
    select
    a.code
    , c.name
    , c.industry
    , c.main_product
    , b.종가 as end_price
    , (sum(PER) / count(*)) * sum(case when inx = 1 then EPS else 0 end) as per_eps
    , (sum(PBR) / count(*)) * sum(case when inx = 1 then BPS else 0 end) as pbr_bps
    , sum(case when inx = 1 then BPS else 0 end) as bps
    , sum(case when inx = 1 then ROE else 0 end) as roe_1
    , sum(case when inx = 2 then ROE else 0 end) as roe_2
    , sum(case when inx = 3 then ROE else 0 end) as roe_3
    , sum(case when 당기순이익 > 0 then 1 else 0 end) as earn 
    , sum(case when ROE > 0 then 1 else 0 end) as roe 
    , sum(case when 영업이익 > 0 then 1 else 0 end) as earning
    , sum(case when 배당수익률 > 0 then 1 else 0 end) as distribution
from 
(   select * , row_number() over(partition by code order by year desc) as inx
    from data
) a
join 
(
    select code, 종가
    from price
    where 날짜 = '2020.04.17'
) b
on a.code = b.code
join
(
select code, name, industry, main_product
from code
) c 
on a.code = c.code
group by a.code
) x
where 
earn = 5 and roe = 5 and earning = 5 and distribution = 5
order by return_rate desc
 
cs

드디어 거의 끝이 보인다. 

1
2
3
4
5
cur.execute(query)
test = DataFrame(cur.fetchall())
test = test.rename(columns={0 : 'code'1 : 'name'
                            , 2 : 'industry'3 : 'main_product'
                            , 4 : 'end_price'5 : 'per_eps'6 : 'pbr_bps'7'srim'8 : 'return_rate'})
 
cs

위에서 정의한 query를 실행하고, dataframe에 넣었다.

쿼리에서 return_rate이 높은 순으로 정리를 하였으니, 기대수익이 높다고 보여지는 것부터 추출이 될 것이다. 

이런식으로 종목명, 산업, 주요제품, 현재주가, 적정주가, 기대수익률 순으로 값이 나온다. 참 깔끔하다. 

참좋은여행은 코로나 때문에 주가가 많이 떨어져서인지, 다른 이유가 있는것인지 굉장히 상위에 올라와있다.

return_rate이 0보다 큰 것을 세보았을 때 4.17일 기준으로 274개가 나온다.

 

여기에 industry와 main_product를 적은 이유는, 단순히 기대수익률이 높다고 무턱대고 사지 않기 위함이다.

산업의 구조적인 특성상 PER, PBR이 낮게 나오는 경우가 있기도 하고, 이에 따라 굉장히 값이 싸다고 비춰지는 경우도 있다.

또한 PER, PBR을 5개년 평균으로 썼기 때문에, 해당 가치지표에 대한 변동성이 큰 종목이라면 또 왜곡되서 비춰질수도 있다.

 

그렇기 때문에, 무조건적으로 여기서 기계적으로 나온 값을 믿고 매수하는 것은 위험할 수 있다.

 

처음에 적정주가를 계산하는 것을 만들기 위한 목적도,

종목을 바로 고르기 위함이 아니라, 내가 탐색할 종목의 우선순위를 정하기 위함이었다.

 

여기서 이제 눈으로 차차 살펴보면서, 괜찮아보이는 종목의 사업보고서를 읽고 재무제표를 보고서 종목선정을 판단하려고 한다.

 

그 다음글에서는 쉘 스크립트를 짜서, 주식 장이 끝난 후 쉘을 돌리면, 해당일 기준으로 csv파일을 떨궈주도록 하는 과정을 적어보려 한다.

728x90

(1) 5개년치 재무제표 불러오기 (fnguide)

(2) 종목 별 주가(가장 최근) 불러오기

(3) Database에 저장하기

(4) PER&EPS, PBR&BPS, S-RIM으로 적정주가 산출 및 기대수익률 구하기

(5) 기대수익률 순으로 Sorting하기

(6) Hyper Parameter 기반으로, 조건 정교화하기

위의 순서대로 적정주가 계산하는 간단한 프로그램을 만들려고 한다. (파이썬을 이용하였다.)

 

-----------------------------------------------------------

 

지난번 글에서는 fnguide를 크롤링하여, 종목별로 재무제표를 불러왔다.

이번에는 종목 별 주가를 불러온 후, DB에 저장하려고 하며, 종목 별 주가는 naver를 통해 크롤링하였다. 

 

네이버금융에서 주가를 불러오는 것은 아래와 같다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get_price(item_name, code_new):
    code = code_new.query("name=='{}'".format(item_name))['code'].to_string(index=False)
    code = code[-6:]
    print(code)
    url = 'http://finance.naver.com/item/sise_day.nhn?code={code}'.format(code=code)
    print("요청 URL = {}".format(url))
    df = DataFrame()
    for page in range(121): 
        pg_url = '{url}&page={page}'.format(url=url, page=page) 
        df = df.append(pd.read_html(pg_url, header=0)[0], ignore_index=True)
        df = df.dropna()
        df['code'= code
        df = df[df.날짜 == '2020.04.17'### 원하는 날짜 입력하기 ### 
    return df
cs

예를 들어 위와같이 대한약품을 입력하면, 이렇게 날짜, 종가, 시가, 고가, 저가, 거래량에 대한 정보가 나온다. 

 

원하는 날짜를 입력해서 함수를 정의하였고, 이제 이걸 이용해서 전종목에 대한 현재 주가를 불러와보자.

1
2
3
4
5
6
item_name = code_df['name'].values.tolist()
 
price = pd.DataFrame(columns = [])
 
for i in item_name:
    price = price.append(get_price(i, code_df))
cs

이번에는 종목 별 주가를 불러온 후, DB에 저장하려고 하며, 종목 별 주가는 naver를 통해 크롤링하였다. 

이렇게 for문을 돌려서 전체 종목에 대한 종가를 price라는 Datafram에 넣었다. 

나중에 조금 보완을 하면 좋을 것 같은게, url을 호출해서, 종목코드 하나 하나씩 append를 하기 때문에, 시간이 오래 걸린다.

원하는 code를 추린 후 for문을 돌리거나, 아니면 나중에 다른 방식을 찾아봐야겠다. (아직은 초보라 이게 최선이다.)

 

그 다음으로 data라는 db를 만들었다.

1
2
3
4
5
6
7
8
9
10
11
12
import sqlite3
from pandas import Series, DataFrame
 
con = sqlite3.connect('data.db')
cur = con.cursor()
 
code_df.to_sql('code',con)
df.to_sql('data', con)
price.to_sql('price', con)
 
# 종가 업데이트
price.to_sql('price', con, if_exists = 'append', index = False ) ## 새로운 날짜 있으면 여기에서 추가하면 됨 
cs

지난번에 정의한 재무제표를 담은 df, 종목코드별 설명을 담은 code, 특정 날짜의 주가를 담은 price에 대한 테이블을 생성한다.

code, df 테이블은 값의 변동이 거의 없지만, 주가같은 경우는 영업일에는 매일 달라지기 때문에,

예를 들어 4.24일 날짜로 추가로 넣을 때에는 값을 append한다. 그리고 con.commit()을 실행하면 db저장 끝!

 

이제는 이렇게 적재된 것을 기반으로 종목 별 적정주가를 계산하는 산식을 넣어서, 기대 수익률 순으로 sorting하려고 한다. 

프로그래밍이 아직 완벽히 익숙치 않아서, 최선으로 짠 코드인지는 모르겠지만, 우선 내가 원하는 결과를 잘 만들어준다. 

728x90

(1) 5개년치 재무제표 불러오기 (fnguide)

(2) 종목 별 주가(가장 최근) 불러오기

(3) Database에 저장하기

(4) PER&EPS, PBR&BPS, S-RIM으로 적정주가 산출 및 기대수익률 구하기

(5) 기대수익률 순으로 Sorting하기

(6) Hyper Parameter 기반으로, 조건 정교화하기

위의 순서대로 적정주가 계산하는 간단한 프로그램을 만들려고 한다. (파이썬을 이용하였다.)

 

-----------------------------------------------------------

 

먼저, 증권거래소에서 회사명/업종/주요제품에 대한 정보를 크롤링하였다.

적정주가를 계산하는 것도 중요하지만, 어떤 산업인지도 종목선정할 때 기준이 되기 때문에 함께 긁어왔다.

(예를 들어 자동차부품산업이나, Cost를 통제하기 어려운 산업, 사이클 경기를 타는 산업은 내기준에서 투자하기 어려운 종목들이다)

 

1
2
3
4
5
6
7
8
9
import pandas as pd
from pandas import DataFrame
from pandas import ExcelWriter
 
code_df = pd.read_html('http://kind.krx.co.kr/corpgeneral/corpList.do?method=download&searchType=13', header=0)[0]
 
code_df.종목코드 = code_df.종목코드.map("{:06d}".format)
code_df = code_df[['종목코드''회사명''업종''주요제품']]
code_df = code_df.rename(columns={'종목코드''code''회사명':'name''업종' : 'industry''주요제품' : 'main_product'})
cs

 

우선 pandas의 read_html을 활용해서, 종목코드, 회사이름, 업종, 주요제품에 대한 정보를 가져왔다. 

 

이런식으로 각 code에 대한 회사명, 업종, 주요제품을 가져올 수 있다.

 

-----------------------------------------------------------

 

두 번째로는 Fnguide에 있는 재무제표를 크롤링 하는 것으로, 파이썬에서는 일반적으로 BeautifulSoup를 많이 사용한다.

일반적인 코드는 인터넷 검색으로 참고를 하였고, 일부 Error에 대해서 예외 처리를 하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def get_fnguide_table(code):
    
    try
 
        ''' 경로 탐색'''
        url = re.get('http://comp.fnguide.com/SVO2/ASP/SVD_main.asp?pGB=1&gicode=A%s'%(code))
        url = url.content
 
        html = BeautifulSoup(url,'html.parser')
        body = html.find('body')
 
        fn_body = body.find('div',{'class':'fng_body asp_body'})
        ur_table = fn_body.find('div',{'id':'div15'})
        table = ur_table.find('div',{'id':'highlight_D_Y'})
 
        tbody = table.find('tbody')
        tr = tbody.find_all('tr')
        Table = DataFrame()
 
        for i in tr:
 
            ''' 항목 가져오기'''
            category = i.find('span',{'class':'txt_acd'})
 
            if category == None:
                category = i.find('th')   
 
            category = category.text.strip()
 
 
            '''값 가져오기'''
            value_list =[]
 
            j = i.find_all('td',{'class':'r'})
 
            for value in j:
                temp = value.text.replace(',','').strip()
 
                try:
                    temp = float(temp)
                    value_list.append(temp)
                except:
                    value_list.append(0)
 
            Table['%s'%(category)] = value_list
 
            ''' 기간 가져오기 '''    
 
            thead = table.find('thead')
            tr_2 = thead.find('tr',{'class':'td_gapcolor2'}).find_all('th')
            year_list = []
 
            for i in tr_2:
                try:
                    temp_year = i.find('span',{'class':'txt_acd'}).text
                except:
                    temp_year = i.text
                year_list.append(temp_year)
            Table.index = year_list
 
        #Table = Table.T
        Table.reset_index(level=0, inplace=True)
        Table = Table.rename(columns={'index''year'}) 
        Table['code'= code
        
            #Table = Table.append(Table)
        Table = Table.loc[Table.year.isin(['2014/12''2015/12''2016/12''2017/12''2018/12''2019/12'])]
        return Table
    
    except
        print('error detection!')
cs

이렇게 get_fnguide_table라는 code를 입력하면, 그 코드에 대한 5년치 재무제표를 반환해주는 함수가 생겼다. 

 

예를들어 가장 흔한 삼성전자를 해당함수에 대입해본다면,

삼성전자 재무제표 크롤링

위와 같이, 2015년~ 2019년 5개년간의 기본적인 재무정보가 나온다. 

(삼성전자의 2018년 영업이익이 무려 58조가 넘는다. 정말 거대한 기업이다.)

 

하지만, 내가 원하는 것은 내가 아는 종목을 검색해서 재무제표를 찾는 것이 아니라, 

전체 종목에 대한 재무제표 정보를 크롤링 하는 것이다.

1
2
3
4
5
code_list = code_df['code'].values.tolist()
df = pd.DataFrame(columns = [])
 
for i in code_list:
    df = df.append(get_fnguide_table(i))
cs

이렇게 아까 생성한 code_df를 list화 시킨 후, for문으로 전체 크롤링 정보를 df라는 데이터프레임에 담아놓았다. 

2000개가 넘는 종목에 대해서 for문을 돌리는 것이라 시간이 조금 소요되기는 한다. 

 

df에 전종목에 대한 재무정보가 다 적재가 되었다. 내가 관심을 두고 있는 대한약품(023910)으로,

데이터가 잘 생성되었는지 검증을 해보았고, 다행히 잘 적재가 된 것 같다.

이렇게 5년치 재무제표를 불러오는 것은 성공하였다.

 

그 다음 글에서는, 종목 별 주가 데이터까지 불러온 뒤, data base에 적재하는 것까지 해보려고 한다.

728x90

'삼성전자를 분석해봐야지!' 라고 시작을 하게 되면, 정보를 찾는 방법은 꽤나 순조롭다.

 

dart 사이트에서 삼성전자의 사업보고서를 보면서, 

사업의 내용과 재무제표 등을 확인할 수 있다. 

 

약간 사업보고서가 어렵게 느껴진다면, fnguide나 증권사 앱을 통해서 다양한 정보들을 확인할 수 있다. 

가치 지표들이 어떠하고, 성장성, 수익성 등 보기 쉽게 만들어 놓은 자료들이 많다. 

 

하지만, 상장 되어있는 2,000여개의 회사들 중, 머리에 떠오르는 기업은 많지 않을 것이다.

삼성, 현대, SK등 일부 대기업들이 떠오를 수 있지만, 상대적으로 중소형 기업은 찾기가 어렵다. 

 

좋은 기업, 매출액이 큰, 시가총액이 큰 기업을 찾는 것이 목적이 아니라, 

작은 기업이라도, 저평가 되어있는 기업을 찾는 것이 나의 목적이기 때문에,

 

내 머릿 속에 떠오르는 회사 pool만을 가지고는, 원하는 기업을 찾지 못할 수가 있다. 

 

-----------------------------------------------------------

 

물론, 스크리너 tool도 잘 되있는 것들이 있다. 

영업이익이 1000억 이상이면서, 배당률이 2% 이상이고, ROE가 10% 이상인 기업! 

으로 필터를 걸고 조회를 하게 되면, 그 조건에 해당하는 기업들이 등장할 것이다. 

 

조건을 정교화하게 걸면 걸수록, 등장하는 기업이 줄어들게 되지만, 

러프하게 조건을 걸어버리면, 거기서 등장하는, 100개가 넘는 회사들에 대한 우선순위를 파악하기가 쉽지 않다. 

 

그리고

    1) 5년간 당기순이익과, 영업이익이 0보다 큰 회사이면서

    2) 5년간 한번도 빠짐없이 배당을 준 회사

 

를 찾고 싶다고 했을 때, 그걸 찾아줄 수 있는 사이트를 찾지 못했다. 

 

내가 원하는 대로, 자유자재로 필터를 걸고 + 그렇게 해서 나온 기업들 중에 먼저 탐색할 수 있는 우선순위를 정하기 위해서, 

이런 토이프로젝트를 시작해보려고 한다.

 

-----------------------------------------------------------

 

(1) 5개년치 재무제표 불러오기 (fnguide) 

(2) 종목 별 주가(가장 최근) 불러오기

(3) Database에 저장하기 

(4) PER&EPS, PBR&BPS, S-RIM으로 적정주가 산출 및 기대수익률 구하기

(5) 기대수익률 순으로 Sorting하기 

(6) Hyper Parameter 기반으로, 조건 정교화하기

 

728x90

+ Recent posts