Toy Project

나만의 주식 대시보드 만들기 (streamlit, altair_chart, yfinance)

Super-Son 2024. 7. 23. 12:11

시작배경

나스닥 종합주가지수, 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