利用python twstock套件做台股爬蟲並記錄收支

股票
圖片取自pexels免費圖庫

個人對於股市沒有這麼關心,短時間內股價怎麼起起落落,市場上什麼樣的消息可能會導致怎麼樣的變動,這些我都沒什麼概念,目前的操作也僅止於把一些閒錢拿去投資ETF或金融股這樣而已。

對於股市,我會想知道我什麼時候買(賣)了什麼股票,當時購入(出售)的成本如何,什麼時間點配息以及配息之後我的總投資成本如何變化等等,但這些在APP上不容易找到,APP提供的資訊也不是我需要的,所以還是自己刻一下吧。

需求是

  • 只看指定的股票,並抓取指定的時間後的資料。
  • 可以隨時增加新的股票代號。
  • 可以手動登記每支股票買入、賣出、稅金、手續費與股息的紀錄,可以自動計算盈虧。
  • 有一個總覽頁面可以快速地知道當下的狀況。

讓python來處理這些事情,先安裝python套件

$ pip3 install twstock pandas openpyxl

寫一支名為get_stock.py的script,第一次執行前修改12~16欄的資訊,執行get_stock.py後會得到一個stock.xlsx的檔案。之後執行視情況修改12~16欄的資訊,比如說多加一支股票代碼,這個script會去更新原來的stock.xlsx檔案並存檔。

import shutil
from datetime import datetime
from time import sleep
import twstock
import pandas as pd
from openpyxl import Workbook
from openpyxl import load_workbook
from openpyxl.utils import get_column_letter
from openpyxl.utils.dataframe import dataframe_to_rows
from openpyxl.styles import Font

#輸入以下資訊############################################################################
stock_array = ['2886', '5880', '0056', '00713', '00878', '00919', '00929', '00679B']
data_year = 2023
data_month = 3
########################################################################################

#更新台股股票代碼
twstock.__update_codes()

#檢查stock.xlsx是否存在
try:
    wb = load_workbook('stock.xlsx')
except FileNotFoundError:
    wb = Workbook()
    wb.save('stock.xlsx')
else:
    #複製一份原始檔當備份
    current_date = datetime.now().strftime('%Y-%m-%d')
    new_filename = f'stock_{current_date}.xlsx'
    shutil.copyfile('stock.xlsx', new_filename)

#更新每檔股票內容
for stock in stock_array:
    stock_name = twstock.realtime.get(stock)['info']['name']
    stock_data = twstock.Stock(stock)
    stock_history = stock_data.fetch_from(data_year, data_month)
    stock_table = pd.DataFrame(data=stock_history, columns=['日期', '總成交股數', '總成交金額', '開盤價', '最高價', '最低價', '收盤價', '漲跌價差', '成交筆數'])
    stock_table['日期'] = stock_table['日期'].dt.strftime('%Y-%m-%d')
    rows = dataframe_to_rows(stock_table, index=False, header=True)
    
    #若無列表中的股票則新增對應的分頁
    if stock in wb.sheetnames:
        sheet = wb[stock]
    else:
        sheet = wb.create_sheet(title=stock)
        sheet.cell(row=1, column=10, value='購入/出售')
        sheet.cell(row=1, column=11, value='購入價/出售價')
        sheet.cell(row=1, column=12, value='股息')
        sheet.cell(row=1, column=13, value='稅金/手續費')
        sheet.cell(row=1, column=14, value='成本')
        sheet.cell(row=1, column=15, value='總成本')
        sheet.cell(row=1, column=16, value='股票市值')
        sheet.cell(row=1, column=17, value='盈虧')

    #清空分頁內的原有資料
    for row in sheet.iter_rows(min_row=1, max_row=sheet.max_row, min_col=1, max_col=9):
        for cell in row:
            cell.value = None

    #更新每日股票資訊
    for r_idx, row in enumerate(rows, 1):
        for c_idx, value in enumerate(row, 1):
             sheet.cell(row=r_idx, column=c_idx, value=value)
    
    sheet.cell(row=1, column=1, value=stock_name)

    #計算與指定格式
    for row in range(3, sheet.max_row + 1):
        for col in sheet.iter_cols(min_row=row, max_row=row, min_col=11, max_col=17):
            for cell in col:
                if col[0].column == 11:
                    cell.number_format = '"$"#,##0.00'
                elif col[0].column == 12:
                    cell.number_format = '"$"#,##0'
                elif col[0].column == 13:
                    cell.number_format = '"$"#,##0'
                elif col[0].column == 14:
                    cell.value = f'=J{row}*K{row}+L{row}+M{row}'
                    cell.number_format = '"$"#,##0'
                elif col[0].column == 15:
                    cell.value = f'=N{row}+O{row-1}'
                    cell.number_format = '"$"#,##0'
                elif col[0].column == 16:
                    cell.value = f'=SUM($J$2:$J{row})*G{row}'
                    cell.number_format = '"$"#,##0'
                elif col[0].column == 17:
                    cell.value = f'=P{row}-O{row}'
                    cell.number_format = '"$"#,##0'

    #統一字體
    for row in sheet.iter_rows(min_row=1, max_row=sheet.max_row, min_col=1, max_col=17):
        for cell in row:
            cell.font = Font(name='Arial', size = "10")

    #統一列距
    for col_num in range(1, 4):
        col_letter = get_column_letter(col_num)
        sheet.column_dimensions[col_letter].width = 15
    for col_num in range(4, 10):
        col_letter = get_column_letter(col_num)
        sheet.column_dimensions[col_letter].width = 10
    for col_num in range(10, 18):
        col_letter = get_column_letter(col_num)
        sheet.column_dimensions[col_letter].width = 15

    sleep(3)

#確認summary分頁是否存在
if 'summary' in wb.sheetnames:
    new_ws = wb['summary']
else:
    new_ws = wb.create_sheet(title='summary')
    new_ws['B2'] = '股票代號'
    new_ws['C2'] = '股票名稱'
    new_ws['D2'] = '持有股數'
    new_ws['E2'] = '持有成本'
    new_ws['F2'] = '股票市值'
    new_ws['G2'] = '目前收益'
    new_ws['H2'] = '股息收益'

#更新summary分頁內容
z = 4
for sheet in wb:
    if sheet.title == 'Sheet':
        wb.remove(wb['Sheet'])
    if sheet.title != 'summary' and sheet.title != 'Sheet':
        new_ws[f'B{z}'] = sheet.title
        new_ws[f'C{z}'] = f'=\'{sheet.title}\'!A1'
        new_ws[f'D{z}'] = f'=SUM(\'{sheet.title}\'!J:J)'
        new_ws[f'E{z}'] = f'=OFFSET(\'{sheet.title}\'!O1,COUNT(\'{sheet.title}\'!O:O)+1,0)'
        new_ws[f'F{z}'] = f'=OFFSET(\'{sheet.title}\'!P1,COUNT(\'{sheet.title}\'!P:P)+1,0)'
        new_ws[f'G{z}'] = f'=F{z}-E{z}'
        new_ws[f'H{z}'] = f'=-SUM(\'{sheet.title}\'!L:L)'
        z += 1

#統一字體
for row in new_ws.iter_rows(min_row=1, max_row=new_ws.max_row, min_col=2, max_col=8):
    for cell in row:
        cell.font = Font(name='Arial', size = "10")

wb.save('stock.xlsx')

stock.xlsx裡各個股票需要手動維護的是登記買入、賣出、稅金、手續費與股息的部分,在各支股票分頁裡的J~M欄,例如我在2023-07-04購入20張00929,當時購入價為15.85元。

購入範例

比如說在2023-08-14領了2190元的股息,收入以負數表示。

股息範例

在summary分頁裡可以清楚地看到各支股票的狀況。

總覽範例

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

返回頂端