콘텐츠로 이동
44 posts in the category “Quant”

[2021-01-15] 고려아연(010130) 종목분석

이번 포스트에서는 비철금속 제련 국내 1위 회사인 고려아연에 대해서 다양한 측면으로 분석한 자료를 공유합니다. 재무제표 참고 기준은 2020.11.16 분기보고서 입니다.

고려아연은 아연, 연, 금, 은을 생산 판매하고 있습니다. 아연의 경우 국내 시장 점유율이 90% 수준에 달하고 연시장에서도 30% 수준으로 매우 높은 국내 시장점유율을 가지고 있습니다.

다음 순서로 고려아연 종목분석을 다루겠습니다.

  • 기술적분석
  • 수급분석
  • 기본적분석
  • 재무제표 주요항목
  • 상관관계분석
  • 시계열분석

고려아연의 최근 240 거래일 일봉차트를 살펴보면 다음과 같습니다.

고려아연 기술적분석

  • 고가: 467,000
  • 저가: 276,500
  • 고가-저가: 68.9%
  • 현재가-저가: 49.2%
  • 현재가-고가: -11.7%
  • MDD: -36.3%

코로나19에 의해 큰 하락을 보인 이후 우상향하는 주가를 보이고 있습니다.

MACD와 RSI 지표를 봤을 때 매수 기회가 몇번 있었는데 MACD 차트에서 회색선(Signal)을 검정선이 상향돌파하면서 RSI가 30 부근으로 낮을 때로 볼 수 있습니다. 물론 MACD와 RSI만으로 투자 결정을 내리는 것은 매우 위험하므로 참고만 하는 것이 좋습니다. 기술적분석 보조지표에서 MACD, RSI 등의 상세 내용을 확인할 수 있습니다.

최근 개인, 기관, 외국인의 수급을 보면 개인은 매도, 기관은 매수, 외국인은 매도 포지션을 취하고 있습니다. 외국인은 2020년 8월 쯤에 많이 매집한 것으로 보입니다. 특히 최근 개인의 매도세와 기관의 매수세가 강한 상황입니다.

고려아연 개인기관외국인 수급

수급 점수를 보면 최근 5일 동안은 매도세가 강했으며 최근 20일 평균 매수세가 컸습니다. 매수세가 강하다가 매도세로 돌아선 상황입니다. 고려아연은 배당률이 높은 편이라 배당기준일 이후에 배당락이 꽤 큰 상황으로 보입니다.

고려아연 수급점수

고려아연의 다음 6가지 기본적분석 지표를 살펴보겠습니다. 2015년 부터의 최근까지 발표된 수치를 다룹니다.

  • 주당순자산 BPS(Book-value Per Share)
  • 주당순이익 EPS(Earning Per Share)
  • 주가순자산비율 PBR(Price Book-value Ratio)
  • 주가수익비율 PER(Price Earning Ratio)
  • 주당배당금 DPS(Dividend Per Share)
  • 배당수익률 DYR(Dividend Yield Ratio)

고려아연 기본적분석

PBR과 PER는 꽤 낮고 ROE는 10% 수준을 유지하고 있습니다. 주당배당금이 지속적으로 늘고 있는 모습입니다. 주가 상승 여력이 있어 보입니다.

이제 고려아연의 재무상태표, 손익계산서, 현금흐름표의 주요 항목을 살펴보겠습니다. 재무제표 보는 법에 대한 블로그 포스트가 있으니 관심있으시면 확인해 보시기 바랍니다.

재무상태표(Statement of Financial Position)

섹션 제목: “재무상태표(Statement of Financial Position)”

먼저 재무상태표에서 자산, 유동자산, 비유동자산, 자본, 지배기업자본, 비지배기업자본, 부채, 유동부채를 차트로 그렸습니다. 꾸준히 자산이 늘고있고 부채도 적당하게 유지되고 있습니다. 재무 상태가 탄탄해 보입니다.

고려아연 재무상태표

손익계산서에서 매출액, 매출원가, 영업이익, 당기순이익을 살펴보겠습니다. 영업이익과 당기순이익이 꾸준하게 있기는 하지만 크게 상승하지는 못하는 모습입니다. 매출액과 매출원가를 보면 마진이 거의 일정하다고 볼 수 있습니다.

고려아연 손익계산서

현금흐름표에서는 영업활동현금흐름, 투자활동현금흐름, 재무활동현금흐름을 살펴보겠습니다.

고려아연 현금흐름표

최근 고려아연은 신사업을 위해 투자를 크게 늘려 투자활동현금흐름이 마이너스를 기록하고 있습니다. 여기서 영업활동 현금흐름과 재무활동 현금흐름을 보면 패턴이 보입니다 4년 정도의 주기로 영업활동현금흐름과 재무활동현금흐름이 큰 등락을 보이고 있습니다. 패턴대로라면 내년도 영업활동현금흐름이 좋지 않을것 같습니다. 비철금속의 거래 가격에 따라서 현금흐름의 패턴이 발생하는 것으로 보입니다. 원자재 가격이 하락해도 헤지를 잘 하고 있다는 것은 긍정적입니다.

이제 고려아연 주가와 주요 원자재, 코스피, 달러 환율과의 상관관계 살펴보겠습니다. 상관관계 수치로는 금, 아연, USD/KRW, WTI, 은, 코스피 순으로 나왔습니다. 여기서 정관계는 아연, WTI, 코스피로 나왔고 역관계로 금, USD/KRW, 은으로 분석되었습니다.

고려아연 금

2019년을 기점으로 전과 후의 관계성이 달라 보입니다. 여기서 상관계수가 -0.5 정도가 나왔는데 이는 2019년 이후 크게 상승한 금값 대비 고려아연의 주가가 하락해서 그런 것으로 보입니다.

고려아연 아연

고려아연 주가와 아연 가격과의 상관관계는 눈으로 보기에도 정관계로 매우 뚜렷합니다.

고려아연 USD/KRW

원달러 환율과 고려아연 주가는 역관계가 뚜렷합니다. 이 관계에 따르면 최근 원달러 환율이 크게 하락하여 주가에는 긍정적일 것 같습니다.

2020.11.16 분기보고서에서 환율 10% 변동시 자산ㆍ부채의 환산 변동금액이 법인세비용차감전순이익에 미치는 영향을 살펴보면 다음처럼 환율 변동이 이익에 영향이 생깁니다.

고려아연 환율 리스크

여기서 USD 환율이 가장 큰 변수이며 원달러 환율 하락시 이익에 긍정적임을 알 수 있습니다.

다음으로 고려아연 주가와 WTI, 은, 코스피와의 상관관계 차트인데 특별한 상관성이 보이지는 않습니다.

고려아연 WTI

고려아연 은

고려아연 코스피

마지막으로 ARIMA 시계열 분석을 참고삼아 살펴보겠습니다.

고려아연 ARIMA

주가 시계열을 모델링하는것은 불가능에 가깝습니다만 재미삼아 참고삼아 ARIMA 시계열 모델을 고려아연 주가에 적용해 보았습니다. ARIMA 모델 상에서는 주가가 우상향할 것으로 예측되고 있습니다.

어디까지나 투자는 본인의 분석과 결단으로 하는 것이므로 본 분석 내용은 참고만 하시길 당부드립니다.

그럼 이번 포스트는 여기서 마치겠습니다.

에스엠, 와이지엔터테인먼트, JYP Ent., 빅히트 비교

이번 포스트에서는 최근 이슈가 되고있는 종목인 빅히트와 기존 Big-3 엔터 종목 에스엠, 와이지엔터테인먼트, JYP Ent.를 비교한 자료를 공유합니다.

2020-10-28 일자를 기준으로 종목 일봉 차트, 시가총액, 당기순이익 등을 비교했습니다.

먼저 네 엔터 종목들의 최근 60일 주가를 보면 모두 하락세인 것으로 보입니다. MDD(Maximum Draw-Down)는 에스엠, 와이지엔터테인먼트, JYP Ent., 빅히트가 각각 -27%, -34%, -26%, -40% 정도입니다. 빅히트와 와이지엔터테인먼트의 하락폭이 상대적으로 더 크다는 것을 알 수 있습니다.

일봉차트

빅히트의 경우 공모가가 13만 5000원이였는데 발행 주식수가 33,846,192주이므로 이는 시가총액으로 보면 4,569,235,920,000원 (약 4조 5천억원) 입니다. 거래 시작 시초가는 27만원 이였으므로 최고가인 공모가의 2배를 찍은 것입니다. 시초가로 따지면 9조에 달하는 시가총액을 기록한 것입니다. 현재는 주가가 많이 하락하여 빅히트의 시총은 5조 3천억 수준입니다.

다음은 2020-10-28일 기준으로 네 엔터주의 시가총액 등을 비교한 차트입니다.

시가총액

시가총액은 빅히트가 다른 엔터주들에 비해 압도적으로 높은 것을 알 수 있습니다. 물론 빅히트가 당기순이익, 경상이익 등에서 좋은 성적을 낸 것은 분명하지만 시총이 이렇게까지 차이가 나는게 적당한 것인지는 의문입니다. 주가에는 미래에 대한 기대가 반영되는 것이기에 빅히트의 성장을 믿는다면 충분히 투자해볼 수도 있겠습니다. 다만 객관적인 데이터를 살펴보고 현황을 이해하고 투자를 진행하는 습관을 가지는게 좋겠습니다.

[2020-07-16] 카카오(035720) 종목분석

최근 언택트 대표주로 매섭게 주가가 상승한 카카오(035720) 종목에 대한 기본적, 기술적, 수급, 딥러닝 분석 정보를 공유합니다.

카카오의 재무제표와 대표적인 지표들을 살펴보겠습니다.

kakao_finstate

매출이 가파르게 상승하는 모습입니다. 다만 순이익은 낮았었는데 매출이 더욱 오르면서 이익이 발생할 것으로 예상됩니다. 자산 규모도 자본과 부채 모두 증가하면서 커질 것으로 보입니다. 현금흐름을 보면 영업활동현금흐름이 커지면서 매출이 늘어날 것으로 보입니다. 다만 투자활동은 공격적이지 않은 것 같습니다.

kakao_perpbrroe

PER는 상당히 올라와 있는데 EPS가 증가될 것으로 예상되어 PER는 낮아질 것으로 컨센서스가 맞춰져 있습니다. BPS도 증가할 것으로 보이나 가격 상승에 비해 약할 것으로 보여서 PBR은 높아질 것으로 예상됩니다. ROE는 현재 마이너스인데 다시 플러스 전환 될 것으로 보입니다.

다음은 카카오 일봉 차트를 보여줍니다. 여기에 이동평균선, 볼린저밴드, MACD, RSI를 추가로 보여줍니다. 이 보조지표들에 대한 상세한 설명은 이 포스트를 참고해 주세요.

kakao

카카오 일봉차트의 볼린저밴드 윗부분에서 주가흐름을 보이고 있습니다. 요 며칠 동안은 코로나19 백신 기대로 주가가 하락했습니다. 주가도 높고 언택트 메리트가 약해졌다고 판단한 투자자들이 많았나 봅니다.

이평선들이 많이 벌어져 있어서 당분간은 횡보하지 않을까 조심스럽게 생각해 봅니다.

MACD는 시그널을 아직 하방돌파하지는 않았습니다. 그러나 RSI는 70을 넘기고 있어서 과열 양상을 보입니다.

다음 바차트는 카카오의 일봉 수급 점수입니다. 0보다 크면 매수세가 강했고 0보다 작으면 매도세가 강했음을 의미합니다.

kakao_dem

최근 60일, 20일, 10일 수급점수는 매수세가 점점 강해져왔음을 보이고 있습니다. 그러나 최근 5일의 수급점수는 매도세가 강해져서 수급점수가 점점 낮아질 것 같습니다.

다음 차트는 딥러닝 모델의 점수입니다. 진한 검정색 선은 카카오 종목의 종가이고 연한 회색 선이 딥러닝 모델이 산출한 카카오 종목 점수입니다.

kakao_score

카카오 주가가 상승을 시작한 3월 중순쯤 딥러닝 모델의 점수가 치솟았습니다. 이후 점수가 조금식 떨어졌는데 최근 다시 약상승 중입니다.

위 분석 내용들은 어디까지나 참고사항 입니다. 투자에 있어서 절대적인 것은 없습니다. 항상 본인의 철학을 지키며 투자하시길 바랍니다.

카카오의 성장과 투자자들의 성투를 응원합니다.

[2020-H1] 딥러닝 주식종목 랭킹모델 백테스팅

이번 포스트에서는 최근 구축한 주식종목 랭킹모델의 2020년 상반기 백테스팅 결과를 정리합니다.

백테스팅 환경은 이 포스트에 정리한 것과 같습니다.

비교 대상(baseline)은 코스피 인덱스로 잡았습니다.

백테스팅 결과

진한 선이 포트폴리오 가치(PV)이고 연한 선이 코스피입니다. 변화율을 y축으로 정했습니다. 0.2는 20%라는 뜻입니다.

2020년 상반기 백테스팅 결과를 보면 퀀티랩 PV 수익률은 약 +20.3%에 달하고 코스피 수익률은 약 -3.1% 입니다.

Maxium Drawdown(MDD)은 퀀티랩 딥러닝 모델은 약 -23.1%, 코스피는 -35.7%로 퀀티랩 딥러닝 모델의 위험이 더 작았습니다.

2020년 상반기 퀀티랩 딥러닝 주식투자 모델의 백테스팅의 매매 내역은 다음과 같습니다.

dateactioncodenamepriceamountprofitlosseval
20200102buy028300에이치엘비101,3159899,998,511
20200102buy005930삼성전자55,20018199,997,012
20200102buy140410메지온193,8005199,995,529
20200102buy196170알테오젠69,80014399,994,032
20200102buy032500케이엠더블유57,30017499,992,537
20200103buy000660SK하이닉스94,50010598,904,030
20200103buy068270셀트리온177,5005698,902,539
20200103buy002360SH에너지화학1,1258,88898,901,039
20200103buy053800안랩76,00013198,899,546
20200103buy005380현대자동차116,0008698,898,050
20200108sell002360SH에너지화학1,6208,88844.00%99,729,899
20200108buy078130국일제지5,3501,86999,728,400
20200219sell196170알테오젠98,50014341.12%107,025,215
20200219buy096530씨젠34,900286107,023,718
20200306sell096530씨젠48,35028638.54%104,062,865
20200313sell053800안랩49,750131-34.54%92,553,090
20200316buy140410메지온112,900191,114,960
20200318buy000660SK하이닉스73,100188,061,293
20200423buy196170알테오젠97,900102106,097,159
20200511sell196170알테오젠168,40010272.01%115,562,596
20200615sell078130국일제지5,4201,8691.31%116,858,954
20200619buy053800안랩51,400194119,638,858
20200625buy229000젠큐릭스21,650461120,262,761

[2019] 딥러닝 주식종목 랭킹모델 백테스팅

이번 포스트에서는 최근 구축한 주식종목 랭킹모델의 2019년도 1년치 백테스팅 결과를 정리합니다.

퀀티랩의 주식종목 랭킹모델은 PER/PBR/ROE 등의 기본적 분석 지표, OHLCV/MACD/RSI 등의 기술적 분석 지표, 수익률, 변동성, 거래규모 등을 종합적으로 고려하여 학습했습니다.

이렇게 학습한 모델의 출력 값이 높을 수록 매수 고려 대상 종목이 되는 매매 전략을 세웠습니다. 매수, 익절, 손절 등의 기준은 다음과 같습니다.

  • 매수: 종목 랭킹 결과 Top 5 종목
  • 익절: 60%
  • 손절: -60%
  • 청산: 과거 평가 수익 고점 대비 -20%
  • …

이러한 기준으로 종가 매매를 진행하며 최대 10개 종목을 보유할 수 있도록 제한했습니다.

자본금은 1억원으로 정하고 종목당 1천만원까지 매수할 수 있도록 했습니다.

매수/매도 거래 비용을 다음과 같이 현실적으로 적용했습니다.

  • 거래세: 0.25%
  • 매매 수수료: 0.015%

비교 대상(baseline)을 코스피 인덱스로 정하여 백테스팅 결과를 차트로 그려보면 다음과 같습니다.

백테스팅 결과

진한 검정 선이 포트폴리오 가치(PV)를, 연한 회색 선이 코스피 인덱스를 보여줍니다.

동일한 스케일로 표시하기 위해 PV, 코스피 인덱스의 처음 값을 0으로 두고 변화율을 표시했습니다.

그 결과 퀀티랩 주식 종목 랭킹 모델은 약 15% 수익을, 코스피 Buy & Hold 전략 수익률은 약 9% 로 구해졌습니다.

백테스팅 과정에서 매매한 종목들의 내역은 다음과 같습니다.

dateactioncodenamepriceamountprofitlosseval
20190102buy042110에스씨디1,4556,87299,998,500
20190102buy028300에이치엘비76,30013199,997,000
20190102buy095190이엠코리아7,4301,34599,995,501
20190102buy011320유니크7,7201,29599,994,002
20190103buy017900광전자2,2454,45499,188,832
20190103buy115440우리넷7,2701,37599,187,332
20190103buy225570넷게임즈9,5801,04399,185,834
20190103buy041960코미팜19,31051799,184,336
20190104buy004720우리들제약8,7701,140101,308,762
20190104buy041140넥슨지티9,7701,023101,307,262
20190110sell225570넷게임즈10,5001,0439.60%104,164,638
20190110buy039830오로라10,150985104,163,138
20190116sell041140넥슨지티12,7501,02330.50%110,248,684
20190116buy007720대명소노시즌2,5853,868110,247,184
20190117sell095190이엠코리아10,2001,34537.28%114,231,397
20190117sell011320유니크12,8001,29565.80%114,187,471
20190117buy108230톱텍9,9701,003114,185,971
20190117buy036200유니셈4,0952,442114,184,471
20190117buy007390네이처셀11,744851114,182,972
20190131sell039830오로라10,3009851.48%117,832,543
20190219sell042110에스씨디1,8956,87230.24%127,255,406
20190219buy008370원풍4,0502,469127,253,906
20190219buy250000보라티알8,6501,156127,252,406
20190308sell036200유니셈4,1252,4420.73%118,311,856
20190325sell028300에이치엘비78,0001312.23%120,028,982
20190325buy056080유진로봇4,4752,234120,027,483
20190325buy208860엔지스테크널러지6,9001,449120,025,983
20190411sell041960코미팜20,5405176.37%121,651,076
20190423sell007390네이처셀11,7448510.00%117,992,185
20190423buy097230한진중공업12,036830117,990,686
20190423buy000440중앙에너비스8,3801,193117,989,187
20190520sell017900광전자2,4354,4548.46%99,035,671
20190524sell097230한진중공업8,150830-32.29%103,109,920
20190524buy083640인콘3,2003,125103,108,420
20190524buy010660화천기계2,4904,016103,106,920
20190610sell115440우리넷9,9901,37537.41%106,821,419
20190628sell010660화천기계3,1204,01625.30%108,235,770
20190628buy004540깨끗한나라2,4154,140108,234,270
20190628buy004830덕성3,2003,125108,232,770
20190628buy019540일지테크3,1503,174108,231,270
20190708sell083640인콘2,1853,125-31.72%108,047,706
20190711sell019540일지테크4,2753,17435.71%112,418,803
20190725sell056080유진로봇3,0902,234-30.95%108,903,860
20190725buy230980솔트웍스3,8002,631108,902,361
20190725buy002070남영비비안1,5456,472108,900,861
20190726sell007720대명소노시즌1,7903,868-30.75%110,587,888
20190729sell004720우리들제약5,9601,140-32.04%112,485,768
20190729sell002070남영비비안2,6056,47268.61%112,441,090
20190729buy012700리드코프6,9501,438112,439,591
20190729buy000140하이트진로홀딩스12,000833112,438,091
20190729buy012610경인양행9,4001,063112,436,592
20190731sell004830덕성3,3003,1253.12%111,465,379
20190801sell250000보라티알9,0001,1564.05%107,712,924
20190801buy036030KTH5,3401,872107,711,424
20190801buy207760미스터블루7,4301,345107,709,925
20190801buy086980쇼박스4,0502,469107,708,425
20190805sell000440중앙에너비스5,6201,193-32.94%105,756,923
20190805sell230980솔트웍스2,4452,631-35.66%105,739,876
20190806sell208860엔지스테크널러지4,6901,449-32.03%105,802,627
20190806buy041960코미팜11,550865105,801,129
20190806buy007390네이처셀6,9001,449105,799,629
20190807sell004540깨끗한나라3,1804,14031.68%106,554,466
20190819sell207760미스터블루9,6901,34530.42%107,638,863
20190819buy006090사조오양9,6001,041107,637,364
20190819buy189860서전기전3,7902,638107,635,865
20191011sell189860서전기전3,9202,6383.43%109,339,291
20191024sell007390네이처셀9,4701,44937.25%115,558,718
20191024buy056080유진로봇3,0003,333115,557,218
20191024buy028300에이치엘비163,98460115,555,742
20191024buy039830오로라12,650790115,554,243
20191213sell028300에이치엘비87,04460-46.92%114,112,748
20191217sell012700리드코프7,1101,4382.30%113,575,584
20191219sell036030KTH5,4101,8721.31%113,394,701
20191219buy000440중앙에너비스8,6301,158113,393,202
20191219buy000520삼일제약22,700440113,391,704
20191223sell041960코미팜15,45086533.77%113,165,219

모델을 지속적으로 발전시켜 나가며 종목 랭킹 결과 및 운용 시뮬레이션 결과를 퀀티랩에서 제공할 계획입니다.

[2020-05-24] 한국전력(015760) 종목분석

이번 포스트에서는 눈물없어 볼 수 없는 종목, 한국전력(015760)의 최근 데이터를 살펴보고자 합니다. 정확한 종목명은 “한국전력공사” 입니다.

한국전력은 1989년에 상장하였으며 업종은 전기업이고 주요 제품은 전력자원개발,발전,송전,전력용기자재확보 입니다.

코스피에 상장되어 있는 한국전력은 최근 엄청난 주가 추락을 보이고 있는 종목 중 하나입니다. 2017년부터 2020년 5월 22일까지의 MDD(Maximum Draw-Down)는 무려 67%에 달합니다.

chart

한전 차트, MDD, ROE

탈원전 정책에 의한 발전 비용 증가와 불경기로 인한 전기 사용 감소 등의 한두가지가 아닌 복합적인 이유들이 겹치면서 엄청난 주가 하락을 견인한 것으로 보입니다.

ROE를 보면 수익이 얼마나 악화되었는지 볼 수 있습니다. 와이즈리포트에 따르면 2020년은 수익이 어느정도 회복될 것으로 전망됩니다.

최근 한전의 주가를 보면 코로나19로 인해 한전 역시 심각한 낙폭을 겪고 반등을 하는 듯 했으나 다시 하락하는 모습을 보입니다.

chart

한전과 WTI

사상 초유의 마이너스 WTI 사태로 발전 원가 절감을 조금 기대해 볼 수 있었지만 WTI가 다시 조금씩 회복하는 모습을 보입니다.

그 때문인지 한전의 주가는 WTI가 회복하는 즈음 다시 하락국면에 접어들었습니다.

chart

한전과 코스피

이는 코스피 인덱스와도 다른 모습임을 알 수 있습니다.

한전의 투자자들 포지션을 보면 외국인과 개인의 정 반대의 모습을 확인할 수 있습니다.

chart

파란색=외인, 주황색=기관, 초록색=개인

한전 투자자들 포지션

외국인들은 근 한달간 매도 포지션을 취하며 매도량 역시 늘려나가는 모습입니다. 반대로 개인들은 최근 한전을 매수하는 모습입니다.

chart

2020, 2021, 2022년은 예측값

한전의 PBR, PER, ROE 출처: 와이즈리포트

미국의 전기업의 PBR이 1.96 정도인데 한전은 0.1~0.3 사이에서 왔다갔다 하니 PBR만 보면 당장 사고싶습니다.

엄청나게 낮은 PBR을 보이는 한전이 실적 개선을 위한 해법을 찾기를 바라며 마칩니다.

  • 듀크 에너지 (DUK): 1.36
  • Public Service Enterprise Group Inc (PEG): 1.59
  • 에디슨 인터내셔널 (EIX): 1.54
  • …

기술적 분석 라이브러리 TA-LIB Python Wrapper

기술적 분석(Technical Analysis)는 과거 축적된 주가와 거래량 등을 분석하여 미래 주가의 방향성을 예측하는 분석 방법입니다. 대표적인 보조지표(Indicator)로 이동평균(Moving Average), 볼린저밴드(Bollinger Band), Moving Average Convergence Divergence(MACD), Relative Strength Index(RSI) 등이 있습니다.

보조지표들 외에도 차트에서 패턴을 알아내기도 합니다. 대표적인 패턴으로 Three Line Strike, Two Black Gapping, Three Black Crows, Evening Star, Abandoned Baby 등이 있습니다.

이번 포스트에서는 기술적 분석 라이브러리인 TA-LIB의 Python Wrapper에 대해서 소개하고자 합니다. TA-LIB를 이용하면 이동평균, 볼린저밴드, MACD, RSI 등의 대표적인 기술적 분석 보조지표를 쉽게 계산할 수 있고 다양한 차트 패턴을 포착할 수 있습니다. 자세한 기능은 이 문서에서 확인할 수 있습니다.

우분투 환경에서 TA-LIB 설치를 진행합니다.

Terminal window
wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
tar -xzf ta-lib-0.4.0-src.tar.gz
cd ta-lib/
./configure --prefix=/usr
make
sudo make install

다음과 같이 make가 없다는 메시지가 뜬다면 make를 먼서 설치하고 다시 make 명령을 실행하면 됩니다.

Command 'make' not found, but can be installed with:
sudo apt install make
sudo apt install make-guile

이제 TA-LIB 파이썬 라이브러리를 설치하면 됩니다. TA-LIB이 설치되어 있어야 TA-LIB 파이썬 라이브러리를 설치할 수 있습니다.

Terminal window
pip install ta-lib

TA-LIB 파이썬 라이브러리를 이용하여 볼린저밴드를 추가해 보겠습니다. 먼저 다음과 같은 OHLCV(Open-High-Low-Close-Volume) 데이터, 즉 봉차트가 있습니다.

import pandas as pd
df = pd.read_csv('chart.csv')
closecodedatediffdiffratiodiffsignhighlowopenpricevolume
46200.0593020180823100.0NaN246200.045700.046150.03093430000006730918
46150.0593020180824-50.0NaN546400.045550.045900.03022130000006582535
46300.0593020180827150.00.325027246550.046000.046100.02436960000005263782
46550.0593020180828250.00.539957246950.046300.046800.03433230000007365976
46800.0593020180829250.00.537057246800.046400.046750.02599640000005572374
47650.0593020180830850.01.816239247950.046700.046950.058025900000012193231
48450.0593020180831800.01.678909248450.047000.047100.065231800000013577454
……………………………
43500.0593020190806-450.0-1.023891543800.042500.042500.065480500000015083824
43200.0593020190807-300.0-0.689655543900.043100.043600.043450000000010002533
42650.0593020190808-550.0-1.273148543500.042650.043250.072807400000016926881
43150.0593020190809500.01.172333243350.043050.043250.04187190000009685147
43700.0593020190812550.01.274623244000.043550.044000.03705310000008466169
43000.0593020190813-700.0-1.601831543500.042950.043500.03141910000007276979
43700.0593020190814700.01.601831244250.043500.043900.03834270000008750135

TA-LIB 파이썬 라이브러리 모듈 이름은 talib 입니다. talib을 임포트하고 볼린저밴드를 구하는 함수를 호출합니다. 이 문서에서 함수들의 Signature를 확인할 수 있습니다.

upperband, middleband, lowerband = BBANDS(close, timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)

이렇게 Upper Bollinger Band, Middle Bollinger Band, Lower Bollinger Band를 쉽게 구할 수 있습니다.

import talib
ubb, mbb, lbb = talib.BBANDS(df.close, 20, 2)
df['ubb'] = ubb
df['mbb'] = mbb
df['lbb'] = lbb
closecodedatediffdiffratiodiffsignhighlowopenpricevolumeubbmbblbb
46200.0593020180823100.0NaN246200.045700.046150.03093430000006730918NaNNaNNaN
46150.0593020180824-50.0NaN546400.045550.045900.03022130000006582535NaNNaNNaN
46300.0593020180827150.00.325027246550.046000.046100.02436960000005263782NaNNaNNaN
46550.0593020180828250.00.539957246950.046300.046800.03433230000007365976NaNNaNNaN
46800.0593020180829250.00.537057246800.046400.046750.02599640000005572374NaNNaNNaN
47650.0593020180830850.01.816239247950.046700.046950.058025900000012193231NaNNaNNaN
48450.0593020180831800.01.678909248450.047000.047100.065231800000013577454NaNNaNNaN
……………………………………
43500.0593020190806-450.0-1.023891543800.042500.042500.06548050000001508382448098.40053746057.544016.599463
43200.0593020190807-300.0-0.689655543900.043100.043600.04345000000001000253348325.70744245940.043554.292558
42650.0593020190808-550.0-1.273148543500.042650.043250.07280740000001692688148540.42638545762.542984.573615
43150.0593020190809500.01.172333243350.043050.043250.0418719000000968514748592.45711345605.042617.542887
43700.0593020190812550.01.274623244000.043550.044000.0370531000000846616948538.70090545467.542396.299095
43000.0593020190813-700.0-1.601831543500.042950.043500.0314191000000727697948456.11615645275.042093.883844
43700.0593020190814700.01.601831244250.043500.043900.0383427000000875013548388.64143945157.541926.358561

파이썬으로 Maximum Drawdown (MDD) 확인하기

Maximum Drawdown (MDD)는 특정 기간동안 발생한 최대 낙폭을 의미하는 하방 리스크 지표 입니다. MDD가 클수록 투자 리스크가 크기 때문에 유의해야 합니다.

MDD = (기간 동안의 최저점 - 기간 동안의 최고점) / 기간 동안의 최고점으로 간단히 구할 수 있습니다.

파이썬에서 MDD를 다음과 같이 구할 수 있습니다.

import numpy as np
def get_mdd(x):
"""
MDD(Maximum Draw-Down)
:return: (peak_upper, peak_lower, mdd rate)
"""
arr_v = np.array(x)
peak_lower = np.argmax(np.maximum.accumulate(arr_v) - arr_v)
peak_upper = np.argmax(arr_v[:peak_lower])
return peak_upper, peak_lower, (arr_v[peak_lower] - arr_v[peak_upper]) / arr_v[peak_upper]
data = [['20190219', 125000.0, 127500.0, 123500.0, 126000.0, 57757], ['20190220', 125000.0, 127000.0, 124500.0, 126000.0, 68453], ['20190221', 125500.0, 126000.0, 124000.0, 125000.0, 43961], ['20190222', 125000.0, 125000.0, 123500.0, 125000.0, 31065], ['20190225', 125500.0, 126000.0, 124000.0, 125500.0, 45852], ['20190226', 125000.0, 127000.0, 124500.0, 126500.0, 37404], ['20190227', 126500.0, 127000.0, 124500.0, 126000.0, 36131], ['20190228', 126500.0, 127000.0, 124500.0, 125000.0, 69474], ['20190304', 124500.0, 125500.0, 122500.0, 123500.0, 65517], ['20190305', 123000.0, 125000.0, 122000.0, 124500.0, 35186], ['20190306', 124000.0, 124500.0, 122500.0, 123500.0, 35449], ['20190307', 123500.0, 124000.0, 122000.0, 123500.0, 34768], ['20190308', 122500.0, 122500.0, 120500.0, 121500.0, 35118], ['20190311', 121500.0, 122500.0, 120000.0, 122500.0, 39576], ['20190312', 123000.0, 124000.0, 122000.0, 123500.0, 24117], ['20190313', 123000.0, 123500.0, 121500.0, 123500.0, 37649], ['20190314', 123000.0, 124000.0, 122000.0, 123500.0, 95132], ['20190315', 123000.0, 128000.0, 123000.0, 126500.0, 107246], ['20190318', 127000.0, 131000.0, 126500.0, 131000.0, 74644], ['20190319', 130000.0, 134000.0, 129500.0, 133000.0, 68348], ['20190320', 132000.0, 133000.0, 129500.0, 131000.0, 42697], ['20190321', 130000.0, 132000.0, 127500.0, 128500.0, 54018], ['20190322', 127500.0, 129500.0, 126500.0, 127000.0, 32380], ['20190325', 125500.0, 126500.0, 124000.0, 124500.0, 37185], ['20190326', 124500.0, 125500.0, 123500.0, 124000.0, 45161], ['20190327', 124000.0, 125000.0, 123500.0, 124000.0, 34336], ['20190328', 124000.0, 124500.0, 120500.0, 121500.0, 43518], ['20190329', 122500.0, 125000.0, 122000.0, 124500.0, 39035], ['20190401', 124000.0, 126500.0, 124000.0, 126500.0, 22463], ['20190402', 125500.0, 126500.0, 123500.0, 126000.0, 31754], ['20190403', 124500.0, 128000.0, 124500.0, 128000.0, 36250], ['20190404', 128500.0, 128500.0, 126000.0, 128000.0, 34854], ['20190405', 127500.0, 129000.0, 126000.0, 127500.0, 33513], ['20190408', 127500.0, 128000.0, 126000.0, 128000.0, 39005], ['20190409', 128000.0, 129000.0, 127500.0, 128500.0, 33266], ['20190410', 128000.0, 129000.0, 126500.0, 128000.0, 64476], ['20190411', 128500.0, 129000.0, 125000.0, 125000.0, 84802], ['20190412', 126000.0, 127500.0, 125500.0, 127000.0, 39663], ['20190415', 126500.0, 128500.0, 126000.0, 127000.0, 61140], ['20190416', 127000.0, 129000.0, 126500.0, 128500.0, 40123], ['20190417', 128500.0, 129000.0, 127000.0, 128000.0, 30846], ['20190418', 128000.0, 128500.0, 124000.0, 124500.0, 55346], ['20190419', 124500.0, 125000.0, 122000.0, 123000.0, 53439], ['20190422', 123000.0, 123500.0, 121000.0, 122500.0, 30421], ['20190423', 122500.0, 123500.0, 120500.0, 121500.0, 54997], ['20190424', 122500.0, 122500.0, 119500.0, 120000.0, 63486], ['20190425', 121000.0, 121000.0, 118500.0, 119000.0, 36046], ['20190426', 118500.0, 119500.0, 117000.0, 119000.0, 43749], ['20190429', 119000.0, 119500.0, 117000.0, 119500.0, 33516], ['20190430', 122000.0, 123000.0, 119000.0, 119500.0, 94118], ['20190502', 118500.0, 122000.0, 118000.0, 121000.0, 56723], ['20190503', 121000.0, 122000.0, 119500.0, 120000.0, 35240], ['20190507', 118500.0, 119500.0, 117000.0, 117500.0, 44453], ['20190508', 116500.0, 117000.0, 115000.0, 116500.0, 52805], ['20190509', 117000.0, 117000.0, 113000.0, 113000.0, 116012], ['20190510', 113000.0, 114500.0, 110000.0, 111500.0, 86072], ['20190513', 110500.0, 110500.0, 107500.0, 108500.0, 70847], ['20190514', 107500.0, 108000.0, 105000.0, 106500.0, 92820], ['20190515', 107000.0, 107500.0, 105000.0, 107000.0, 68937], ['20190516', 107000.0, 107500.0, 104000.0, 105000.0, 64047]]
import pandas as pd
df = pd.DataFrame(data, columns=['date', 'open', 'high', 'low', 'close', 'volume'])
mdd = get_mdd(df['close'])
(19, 59, -0.21052631578947367)
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(8, 5))
fig.set_facecolor('w')
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1])
axes = []
axes.append(plt.subplot(gs[0]))
axes.append(plt.subplot(gs[1], sharex=axes[0]))
axes[0].get_xaxis().set_visible(False)
from mpl_finance import candlestick_ohlc
x = np.arange(len(df.index))
ohlc = df[['open', 'high', 'low', 'close']].astype(int).values
dohlc = np.hstack((np.reshape(x, (-1, 1)), ohlc))
# 봉차트
candlestick_ohlc(axes[0], dohlc, width=0.5, colorup='r', colordown='b')
# 거래량 차트
axes[1].bar(x, df.volume, color='k', width=0.6, align='center')
import datetime
_xticks = []
_xlabels = []
_wd_prev = 0
for _x, d in zip(x, df.date.values):
weekday = datetime.datetime.strptime(str(d), '%Y%m%d').weekday()
if weekday <= _wd_prev:
_xticks.append(_x)
_xlabels.append(datetime.datetime.strptime(str(d), '%Y%m%d').strftime('%m/%d'))
_wd_prev = weekday
axes[1].set_xticks(_xticks)
axes[1].set_xticklabels(_xlabels, rotation=45, minor=False)
# MDD 그리기
axes[0].plot(mdd[:2], df.loc[mdd[:2], 'close'], 'k')
plt.tight_layout()
plt.show()

mdd

이 차트에서는 약 -21%의 MDD가 발생했습니다. 포트폴리오를 구성할 때 보유하고자 하는 종목들의 MDD를 전체적으로 확인해보는 것이 좋습니다. 특히 보유하고자 하는 종목 수가 적을수록 MDD가 낮은 종목을 보유하는 것이 안전할 것입니다.

Matplotlib으로 봉차트(Candlestick Chart) 그리기

봉차트(Candlestick Chart)는 한국 금융권에서 아주 친숙하게 사용되고 있는 차트 입니다. 하나의 봉(Candle)은 특정 기간 동안에서의 시가, 고가, 저가, 종가 (Open, High, Low, Close; OHLC)를 한번에 표현해 줍니다. 보통 일봉, 즉 하루 동안의 OHLC를 표현한 봉으로 차트를 그립니다.

파이썬에서 봉차트를 그릴 수 있는 도구는 다양하겠지만 대표적인 파이썬 차트 라이브러리인 (Matplotlib)[https://matplotlib.org/index.html]을 활용하여 봉차트를 그리는 법을 이 포스트에서 다루겠습니다.

Matplotlib 1.X 버전에서는 matplotlib.finance 모듈에서 candlestick_ohlc() 함수를 제공하고 있었으나 이 모듈은 2.0 버전부터 Deprecated 되었고, 이 모듈은 따로 (Gihub Repository)[https://github.com/matplotlib/mpl_finance]로 유지되고 있습니다. 설치는 다음 명령으로 할 수 있습니다.

pip install https://github.com/matplotlib/mpl_finance/archive/master.zip

이제 가시화할 데이터를 준비합니다. 다음과 같은 20일치 OHLC와 거래량 데이터를 봉차트로 그려보겠습니다.

dateopenhighlowclosevolume
02018080714250014550014200014500063103
12018080814400014500014150014350049191
22018080914350014400014200014200063800
32018081014200014650014200014600057283
42018081314500014600014100014200051095
52018081414150014600014150014250042579
62018081614150014200013850014100066262
720180817140500141000137000138500100717
82018082013850014000013750013750053507
92018082113750014000013750014000040399
102018082213850014000013850013900034023
112018082313950014000013500013600095824
1220180824135500137000132000134500118459
132018082713500013650013400013500067489
142018082813550013600013300013500066626
152018082913500013600013400013550064548
1620180830135500136000131500132500141496
1720180831132500133500131000132000100410
182018090313250013350013200013200037159
192018090413250013350013150013350035215

이렇게 데이터가 준비되었으니, 차트 레이아웃을 구성하겠습니다.

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(8, 5))
fig.set_facecolor('w')
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1])
axes = []
axes.append(plt.subplot(gs[0]))
axes.append(plt.subplot(gs[1], sharex=axes[0]))
axes[0].get_xaxis().set_visible(False)

결과는 다음과 두 개의 차트 영역이 위아래에 구성됩니다. 위에는 봉차트를 아래에는 거래량 바차트(Bar Chart)를 그리겠습니다. 위 아래 차트 모두 x 축은 같기 때문에 아래쪽 차트에만 x 축의 틱 레이블(Tick Label)을 표시합니다.

layout

이제 여기에 봉차트와 거래량 차트를 그리면 됩니다. mpl_finance 모듈에서 candlestick_ohlc() 함수를 임포트하고 x 축 데이터를 준비한 다음 봉차트와 거래량 차트를 그립니다.

from mpl_finance import candlestick_ohlc
x = np.arange(len(df.index))
ohlc = df[['open', 'high', 'low', 'close']].astype(int).values
dohlc = np.hstack((np.reshape(x, (-1, 1)), ohlc))
# 봉차트
candlestick_ohlc(axes[0], dohlc, width=0.5, colorup='r', colordown='b')
# 거래량 차트
axes[1].bar(x, df.volume, color='k', width=0.6, align='center')
plt.tight_layout()
plt.show()

이 파이썬 코드의 결과는 다음과 같습니다.

layout

여기서 x 축의 틱 레이블이 0에서 59까지 index 값으로 표시되는데, 일자로 표시되도록 해보겠습니다. 아래와 같이 plt.show() 전에 틱 레이블을 추가합니다. 여기서는 월요일에 해당하는 날짜만 표시하도록 했습니다.

# xlabels에 요일 추가
import datetime
_xticks = []
_xlabels = []
_wd_prev = 0
for _x, d in zip(x, df.date.values):
weekday = datetime.datetime.strptime(str(d), '%Y%m%d').weekday()
if weekday <= _wd_prev:
_xticks.append(_x)
_xlabels.append(datetime.datetime.strptime(str(d), '%Y%m%d').strftime('%m/%d'))
_wd_prev = weekday
axes[1].set_xticks(_xticks)
axes[1].set_xticklabels(_xlabels, rotation=45, minor=False)

이렇게 해주면 아래와 같이 index 값 대신 날짜가 x축 틱의 레이블로 표시됩니다.

layout

코스피, 코스닥, 코스피200 5일 평균 대비 현재가

주식 투자를 함에 있어 시장 상황을 파악하는 것은 매우 중요한 일입니다. 코스피, 코스닥, 코스피200 등의 지수가 안좋을 때는 매도의 관점에서 리스크를 관리해야 할 것입니다.

이 포스트에서는 지수의 5일 평균선에서의 현재 지수의 위치를 확인하는 간단한 파이썬 코드를 공유합니다. 데이터는 키움증권 Open API를 통해 수집했습니다. 물론 어떠한 소스에서 데이터를 획득해도 상관 없습니다.

5일 간의 코스피 데이터가 다음과 같을 때, 최근 5일 종가(현재가)의 평균 대비 당일 현재가의 위치를 확인해 보겠습니다.

[{'code': '001',
'date': '20181102',
'open': 2046.61,
'high': 2096.63,
'low': 2044.94,
'close': 2096.0,
'volume': 371978.0},
{'code': '001',
'date': '20181101',
'open': 2035.11,
'high': 2054.22,
'low': 2023.43,
'close': 2024.46,
'volume': 343813.0},
{'code': '001',
'date': '20181031',
'open': 2022.84,
'high': 2030.5,
'low': 2006.63,
'close': 2029.69,
'volume': 373027.0},
{'code': '001',
'date': '20181030',
'open': 1985.95,
'high': 2028.84,
'low': 1985.95,
'close': 2014.69,
'volume': 441585.0},
{'code': '001',
'date': '20181029',
'open': 2026.68,
'high': 2045.76,
'low': 1993.77,
'close': 1996.05,
'volume': 427304.0}]

우선 20181029에서 20181102까지 5일 동안의 코스피 평균은 약 2032.18 입니다. 그리고 20181102의 종가는 2096.0 입니다. 5일 평균과의 비율로 보면 약 1.03으로 1보다는 커서 매수를 해볼만한 상황이라 볼 수 있습니다.

이런 단순한 지표로 투자를 결정할 수는 없지만 돌다리도 두들겨보고 건넌다는 생각으로, 시장 지수의 5일 평균 대비 현재가가 1 미만인 경우에는 관망하는 것이 좋습니다.

아래는 이 값을 구하는 매우 간단한 파이썬 코드 입니다.

import numpy as np
data = [{'code': '001', 'date': '20181102', 'open': 204661.0, 'high': 209663.0, 'low': 204494.0, 'close': 209600.0, 'volume': 371978.0}, {'code': '001', 'date': '20181101', 'open': 203511.0, 'high': 205422.0, 'low': 202343.0, 'close': 202446.0, 'volume': 343813.0}, {'code': '001', 'date': '20181031', 'open': 202284.0, 'high': 203050.0, 'low': 200663.0, 'close': 202969.0, 'volume': 373027.0}, {'code': '001', 'date': '20181030', 'open': 198595.0, 'high': 202884.0, 'low': 198595.0, 'close': 201469.0, 'volume': 441585.0}, {'code': '001', 'date': '20181029', 'open': 202668.0, 'high': 204576.0, 'low': 199377.0, 'close': 199605.0, 'volume': 427304.0}]
for d in data:
d['open'] = d.get('open') / 100
d['high'] = d.get('high') / 100
d['low'] = d.get('low') / 100
d['close'] = d.get('close') / 100
m = np.mean(list(map(lambda d: d.get('close'), data)))
print(m)