Streamlit¶
Streamlit可以以簡單的python後端語法建構前端的網頁APP,並且可以結合Github,將編寫好的 streamlit app 免費部署到官方的伺服器上,詳細的安裝方法、各個元件語法可參考Streamlit Ducument。在此篇中,我將以實際的專案出發,介紹如何透過串連Finmind API取得股票與股利資訊,並以streamlit製作計算機APP。
FindMind¶
FindMind API套用官網的說明,主要提供以台股為主,超過 50 種金融開源數據( open data ),希望讓大數據、資料分析,減少資料收集的門檻。 而我們免費仔要做就只是註冊一組帳密,便可以取得一小時600次的request額度,對於我們開發這種個人小型測試專案應該是很夠用了,當然也可以根據自己的需求購買更高階的方案,畢竟經營與維護資料庫不容易,使用者付費也是合情合理。
FinMind有著輕量、簡易的特點,使用者要做的就只是登入FinMind後將個人的token輸入以下的程式碼即可輕鬆的連接資料庫API。
from FinMind.data import DataLoader
import streamlit as st
import pandas as pd
#use token login package
# 初始化 FinMind DataLoader
FinMindapi = DataLoader()
FinMindapi.login_by_token(api_token='your token')
以下我以自己的需求為例,透過API取得台股的股息資訊,並包成一個函式方便後續使用。
#FinMind API 查詢股票現金股利
def query_dividend_data(stock_code, selected_year,start_date, end_date):
try:
#連接台股股利資料庫
df = FinMindapi.taiwan_stock_dividend(stock_id=stock_code, start_date=start_date, end_date=end_date)
if not df.empty:
# 返回 API 查詢的整個 DataFrame
df=df[["stock_id","date","CashEarningsDistribution"]]
df=df.rename(columns={"stock_id":"股票代號","date":"除息日期","CashEarningsDistribution":"現金股利"})
return df
else:
st.warning(f"{selected_year}年度找不到股票代號 {stock_code} 的相關資料")
return df
except Exception as e:
# st.error(f"查詢股票代號 {stock_code} 時發生錯誤:{e}")
return pd.DataFrame(columns=["股票代號","除息日期","現金股利","股數","總額"])
Calculator APP¶
話不多說,先看看成品。Stock Calculator by Streamlit 這只是一個簡單的範例,初步來說分成三個部分,pages分頁、左側投資組合出入欄、右側內容圖表輸出。 以下將分別說明。
Multipages¶
Streamlit 如假包換,支援分頁切換!想知道其他應用歡迎收看官方document,簡單來說只要在專案的根目錄建立一個名叫pages的資料夾。
├── Chart.py
├── Query_FinMind_Data.py
├── README.md
├── pages
│ └── 選擇持有時間.py
├── requirements.txt
└── 依年度計算.py
並在其中放置另一個頁面的內容,streamlit將以檔名呈現在頁面上。是的,你沒看錯,streamlit竟然支援中文的檔名,如果過去有邊寫過python 的朋友應該都被中文編碼、空格等檔名陷阱給坑過,但streamlit竟然可以支援並正確顯示,雖然不知道它怎麼做到的,但著實令我驚豔,在發現後便與大家分享。
Stock Input¶
以下分享如何在左側欄位建立彈性的股票代號與股數輸入,雖然不是什麼複雜的演算法,但當初在進行開發時著實思考了良久,也參考了官方相關的論壇與前輩們的討論才完成,分享給大家。
def add_field():
st.session_state.fields_size += 1
def delete_field():
st.session_state.fields_size -= 1
# 年度的 Slide bar
selected_year = st.sidebar.slider("選擇年度", 2019, 2023, value=2022)
# 計算 start_date 和 end_date
start_date = f"{selected_year}-01-01"
end_date = f"{selected_year}-12-31"
#region 左側的sidebar
with st.sidebar:
st.title('投資組合')
if "fields_size" not in st.session_state:
st.session_state.fields_size = 0
st.session_state.fields = []
st.session_state.deletes = []
# c_up contains the stock input
# c_down contains the add and remove buttons
c_up = st.container()
c_down = st.container()
with c_up:
c1 = st.container() # c1 contains input choices
c2 = st.container() # c2 contains submit button
with c_down:
col_l,_,col_r = st.columns((6,10,6))
with col_l:
st.button("➕增加一筆", on_click=add_field)
with col_r:
if st.session_state.fields_size>0:
st.button("❌刪除一筆", on_click=delete_field)
for i in range(st.session_state.fields_size):
with c1:
col_stock_code, col_shares= st.columns((8,6))
with col_stock_code:
# 輸入股票代號
st.session_state.fields.append(st.text_input(f"股票代號_ {i+1}", key=f"Stock_Code_{i+1}"))
with col_shares:
# 輸入股票股數
st.session_state.fields.append(st.number_input(f"股數_ {i+1}", key=f"Shares_{i+1}", min_value=0, value=0))
Content¶
在右側的內容,我簡單安排了DataFrame的展示、長條圖、圓餅圖。streamlit可以透過st.dataframe輕易的展示dataframe表格,充分的運用了python的一大利器--pandas的功能,還可以調整各式參數。
st.dataframe(stocks_df, hide_index=True, use_container_width=True)
if st.session_state.fields_size!=0:
# 以長條圖顯示現金股利金額
st.subheader("現金股利金額長條圖")
Chart.plot_dividends_bar_chart(stocks_df)
# 圓餅圖顯示各股票的現金股利總額分布
st.subheader("各股票現金股利佔比分布圖")
st.write(f"{selected_year} 年度共拿到"+str(round(stocks_df["總額"].sum()))+"元")
Chart.plot_dividends_pie_chart(stocks_df)
除此之外還可以結合視覺化套件,這裡我使用plotly進行繪圖,主要是看中套件的網頁互動性,有強大UI功能,可以方便使用者進行調整。在這裡我依據個人習慣與程式碼簡潔包成了函式物件,看似粉複雜但其中的繪圖關鍵語法卻相當簡單。
import plotly.graph_objects as go
###繪製長條圖
# 創建 Figure
fig = go.Figure()
# 加入長條圖
fig.add_trace(go.Bar(
x=grouped_df['月份'],
y=grouped_df["總額"].fillna(0),
name='',#去掉名稱
hovertemplate='%{x}月: %{y}元',
marker_color='skyblue'
))
# 設定標題和圖例
fig.update_layout(xaxis_title='月份',yaxis_title='總額 (元)',title_text="每月股利現金流統計表", title_x=0.45, xaxis=dict(tickmode='linear'))
# 顯示圖表
st.plotly_chart(fig, use_container_width=True)
###繪製長條圖end
###繪製圓餅圖
fig_pie = go.Figure(data=[go.Pie(
labels=dividends_df["股票代號"],
values=dividends_df["總額"],
hovertemplate="股票代號: %{label} <br>股利: %{value}元 <br>佔比: %{percent}%"
)])
st.plotly_chart(fig_pie, use_container_width=True)
###繪製圓餅圖
以上只是點出一些關鍵之處,至於資料如何彙整,欄位如何整理不在本本文討論的範圍,若有興趣可以直接參考Github專案 Stock Calculator。
Deployment¶
到這裡,你應該已經能成功部署在local終端機上,但若想將APP直接分享給別人,進行雲端的部署就相當重要,然而應該不是每個人都能力維護local端的伺服器,所幸stramlit官方與Github提供了免費的部署方式,可以讓大家將存在github的streamlit repository部署到strealit cloud中,詳細可以看Streamlit Deploy。 簡單來說只要準備相關depadency 準備好即可。
your-repository/
├── your_app.py
└── requirements.txt
值得注意的是,streamlit 與 Github 秉持著開源的精神,官方的說明是只能部署一個Github的private專案,但public專案則沒有限制。然而經過的實測若先部署public repository再將專案改為private,部署的APP似乎仍算public,因此沒有數量限制XDD。
結語¶
到這裡,你已經成功學會如何串連 FinMind API 建立一個streamlit的Web APP。建立存股計算機只是依據我的需求而建,我也不是財經相關背景,只是有感於自己存股時的痛點--要來回翻查各個股票代號,一直無法方便的統計自己可以領多少股利!於是這個小專案便誕生了。我相信這絕對只是一個簡單的應用,在未來也許可以開發選股策略、程式交易等APP,也歡迎各位先進開發屬於自己的APP,也歡迎各位與我交流。