處置股股期結算計算
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}")Source: pythonstock/stock