#118·stock

處置股股期結算計算

Author: edgoosaCreated Jan 21, 2026Updated Jan 21, 2026

import streamlit as st import pandas as pd from datetime import datetime, timedelta from decimal import Decimal, ROUND_HALF_UP

設定網頁標題與圖示

st.set_page_config(page_title="期貨結算計算機", page_icon="")

st.title(" 股票期貨結算價計算") st.markdown("請輸入時間範圍與數據,支援 CSV 上傳或手動表格輸入。")

--- 1. 時間設定區 ---

st.header("1. 設定採樣時間") col1, col2 = st.columns(2) with col1: start_time = st.time_input("開始時間", value=datetime.strptime("13:00", "%H:%M").time()) with col2: end_time = st.time_input("結束時間", value=datetime.strptime("13:30", "%H:%M").time())

計算預期次數

將時間轉換為今天的完整 datetime 物件以便計算

today = datetime.now().date() dt_start = datetime.combine(today, start_time) dt_end = datetime.combine(today, end_time)

if dt_end <= dt_start: st.error("⚠️ 結束時間必須晚於開始時間") expected_count = 0 else: delta = dt_end - dt_start total_minutes = delta.total_seconds() / 60 expected_count = int(total_minutes * 12) st.info(f"⏱️ 總時長: {int(total_minutes)} 分鐘 | 預期數據筆數: {expected_count} 筆")

--- 2. 數據輸入區 ---

st.header("2. 輸入數據") input_method = st.radio("選擇輸入方式:", ["上傳 CSV 檔案", "手動輸入/貼上"])

df_data = None

if input_method == "上傳 CSV 檔案": uploaded_file = st.file_uploader("請上傳 CSV (無標題,A欄價格,B欄次數)", type=["csv"]) if uploaded_file is not None: try: # 讀取 CSV,假設沒有標題 df_data = pd.read_csv(uploaded_file, header=None, names=["Price", "Count"]) except Exception as e: st.error(f"讀取錯誤: {e}")

else: # 手動輸入 st.caption("請在下方表格直接輸入,或從 Excel 複製貼上 (點擊表格右上角可放大)") # 建立一個空的 DataFrame 供用戶編輯 # num_rows="dynamic" 允許用戶添加行 df_input = pd.DataFrame([{"Price": 150.0, "Count": 1}]) df_data = st.data_editor(df_input, num_rows="dynamic", use_container_width=True)

--- 3. 計算核心 ---

if st.button(" 開始計算結算價", type="primary"): if df_data is None or df_data.empty: st.warning("請先輸入或上傳數據。") elif expected_count == 0: st.warning("時間設定有誤。") else: try: total_weighted_sum = Decimal('0.0') total_count = 0

        # 遍歷數據 (轉換為 Decimal 以確保精度)
        # 使用 iterrows 雖然慢一點,但對於幾百筆數據來說沒差,且方便做型別轉換
        for index, row in df_data.iterrows():
            try:
                p = Decimal(str(row['Price']).strip())
                c = int(row['Count'])
                total_weighted_sum += p * c
                total_count += c
            except:
                continue # 跳過無效行
        
        if total_count == 0:
            st.error("沒有有效的數據行 (請檢查格式)。")
        else:
            st.divider()
            
            # 驗證數量
            diff = expected_count - total_count
            if diff != 0:
                status = "少於" if diff > 0 else "多於"
                st.warning(f"⚠️ 警告: 實際樣本 {total_count} 筆,{status}預期 ({abs(diff)} 筆)。")
            else:
                st.success(f"✅ 樣本數量正確 ({total_count} 筆)")
            
            # 計算
            average = total_weighted_sum / total_count
            settlement_price = average.quantize(Decimal("0.001"), rounding=ROUND_HALF_UP)
            
            # 顯示大字體結果
            st.metric(label="最終結算價", value=f"{settlement_price}")
            st.caption(f"原始加權平均: {average}")
            
    except Exception as e:
        st.error(f"計算發生錯誤: {e}")