나만의 주식 대시보드 만들기 (streamlit, altair_chart, yfinance)
시작배경
나스닥 종합주가지수, S&P 500 ETF 상품으로 재테크를 하고 있는데 매일 아침에 일어나 스마트폰으로 MTS 앱을 열어서 각 상품들을 하나하나 들어가 확인하는데 너무 귀찮았습니다. 아침에 일어났을 때 얼마나 변동이 있었는지, 20일 평균선은 터치했는지 등을 한번에 보고 싶었는데 마음에 드는 사이트가 없어서 직접 만들게 되었습니다.
제작과정
전반적인 제작 과정은 ChatGPT의 도움을 받았습니다. (대화기록)
super-son-sideproject/pages/3_Index and ETF Trend Viewer.py at main · geunsu-son/super-son-sideproject
Main page of pages created as a side project. Contribute to geunsu-son/super-son-sideproject development by creating an account on GitHub.
github.com
1. 데이터 가져오기 : yfinance
저는 해외 주식 정보를 가져오고 싶었기 때문에 yfinance를 사용했습니다. 1년치의 데이터를 확보할 수 있도록 기간을 설정했습니다. 데이터는 날짜, 오픈가, 마감가, 하한가, 상한가 등의 정보를 포함하고 있었습니다.
import pandas as pd
import yfinance as yf
# Calculate the date one year ago from today
end_date = pd.Timestamp.today()
start_date = end_date - pd.DateOffset(years=1)
# Define a function to fetch data
def fetch_data(ticker):
data = yf.download(ticker, start=start_date, end=end_date)
data.reset_index(inplace=True)
return data
# Fetch data for NASDAQ and S&P 500
nasdaq_data = fetch_data('^IXIC')
sp500_data = fetch_data('^GSPC')
2. 주식 차트 그리기
이제 확보한 데이터로 일봉 차트와 이동평균선을 구현했습니다.
* 차트 그리기 : streamlit의 st.altair_chart
* 이동평균 계산 : DataFrame.rolling 사용
import streamlit as st
import altair as alt
# Function to create moving average
def add_moving_averages(data):
data['MA_5'] = data['Close'].rolling(window=5).mean()
data['MA_20'] = data['Close'].rolling(window=20).mean()
data['MA_40'] = data['Close'].rolling(window=40).mean()
data['MA_60'] = data['Close'].rolling(window=60).mean()
return data
nasdaq_data = add_moving_averages(nasdaq_data)
sp500_data = add_moving_averages(sp500_data)
# Function to create candlestick chart with moving averages
def create_candlestick_chart(data, title):
base = alt.Chart(data).encode(
alt.X('Date:T', title='Date')
)
open_close_color = alt.condition(
"datum.Open <= datum.Close",
alt.value("#FF0000"), # Red for increasing
alt.value("#0000FF") # Blue for decreasing
)
rule = base.mark_rule().encode(
alt.Y('Low:Q', title='Price', scale=alt.Scale(zero=False)),
alt.Y2('High:Q'),
color=open_close_color
)
bar = base.mark_bar().encode(
alt.Y('Open:Q'),
alt.Y2('Close:Q'),
color=open_close_color
)
ma5 = base.mark_line(color='green').encode(
alt.Y('MA_5:Q', title='Price')
)
ma20 = base.mark_line(color='orange').encode(
alt.Y('MA_20:Q', title='Price')
)
ma40 = base.mark_line(color='purple').encode(
alt.Y('MA_40:Q', title='Price')
)
ma60 = base.mark_line(color='brown').encode(
alt.Y('MA_60:Q', title='Price')
)
chart = (rule + bar + ma5 + ma20 + ma40 + ma60).properties(
width=800,
height=400,
title=title
)
return chart
# Create and display NASDAQ chart
st.subheader('NASDAQ Index')
nasdaq_chart = create_candlestick_chart(nasdaq_data, 'NASDAQ Index')
st.altair_chart(nasdaq_chart, use_container_width=True)
# Create and display S&P 500 chart
st.subheader('S&P 500 Index')
sp500_chart = create_candlestick_chart(sp500_data, 'S&P 500 Index')
st.altair_chart(sp500_chart, use_container_width=True)
3. 사이드바에 기간을 선택하는 슬라이더 삽입
저는 최근 동향만 관심이 있었기 때문에 1년의 데이터가 모두 보이는게 아닌 최근 4개월치만 보이도록 사이드바에 슬라이더를 삽입했습니다.
with st.sidebar:
# Slider for selecting time period in months
months = st.slider("Select Time Period (months)", 1, 12, 4)
---
# Function to filter data based on the selected period
def filter_data(data, months):
start_filter_date = end_date - pd.DateOffset(months=months)
filtered_data = data[data["Date"] >= start_filter_date.strftime("%Y-%m-%d")]
return filtered_data
nasdaq_data_filtered = filter_data(nasdaq_data, months)
sp500_data_filtered = filter_data(sp500_data, months)
4. 레이아웃, 디자인 수정 및 표시되는 정보 추가
만들고 난 뒤에는 이제 마음에 드는 디자인이 되도록, 생각난 기능들이 추가되도록 수정했습니다.
- 그래프 2열 배치
- 그래프 상단 최신 3일치 데이터 표기
- 20일 이동평균선과 가까워짐에 따른 이모지 표시
- 이동평균선보다 낮아진 지수, 상품은 사이드바에 별도 표시
웹 배포 결과
Streamlit Cloud에 만든 결과물을 게시했습니다. 이제 아침마다 배포한 사이트를 통해 동향을 파악하고 있는데, MTS 앱 켜고, 인증하고 하나하나 들어가는 것과 비교가 안될 정도로 편하게 사용하고 있습니다. 아주 만족 👏
Index and ETF Trend Viewer
Main page of pages created as a side project
super-son-sideproject.streamlit.app
만났던 문제들
거래 기록이 없는 주말의 데이터 그래프 표시
그래프의 x축이 '날짜' 데이터로 인식되어 DataFrame에도 데이터가 없음에도 불구하고 주말이 그래프에 반영되었었습니다. x축이 날짜가 아니도록 코드를 수정하여 해결했습니다.
# "Date:T" => "Date:N"
base = alt.Chart(data).encode(
alt.X("Date:N", title="Date")
)
BrokenPipeError - yfinance.download
Streamlit Cloud를 사용할 경우 yfinance.download를 사용하면 BrokenPipeError가 발생합니다. 관련해서는 따로 블로깅하였습니다.
[Streamlit] yfinance - BrokenPipeError 오류 해결
이번 포스팅에서는 Streamlit Cloud를 통해 yfinance 패키지를 사용하면서 만난 오류를 소개하고자 합니다. 나스닥, S&P 500 지수의 20일선 그래프를 한 페이지에서 보고 싶어서 시작했는데 오류 해결하
super-son.tistory.com