税金・確定申告の自動化:株の損益計算Pythonツール

自動化・運用

株式投資の確定申告で一番面倒なのが「損益計算」です。複数の証券会社で取引している場合、損益通算を手作業で行うのは大変です。PythonでCSVを自動集計するツールを作ります。

日本の株式税制の基本

  • 株式の譲渡益・配当金の税率:20.315%(所得税15.315%+住民税5%)
  • 特定口座(源泉徴収あり):証券会社が自動計算・納付
  • 一般口座:確定申告が必要
  • 損益通算:同じ年の利益と損失を相殺できる
  • 繰越控除:損失を翌年から3年間繰り越せる

取引履歴CSVの読み込み

import pandas as pd
import numpy as np
from datetime import datetime
import os

def load_trades(filepath, encoding="shift-jis"):
    """
    証券会社の取引履歴CSVを読み込む
    SBI証券: マイページ > 取引履歴 > CSVダウンロード
    """
    df = pd.read_csv(filepath, encoding=encoding, skiprows=1)
    df["約定日"] = pd.to_datetime(df["約定日"])
    return df

損益計算(総平均法)

def calculate_profit_loss(trades_df):
    """
    総平均法で株式の損益を計算
    """
    results = []
    
    for code in trades_df["銘柄コード"].unique():
        stock_trades = trades_df[trades_df["銘柄コード"] == code].sort_values("約定日")
        
        held_shares = 0
        total_cost = 0
        realized_pnl = 0
        
        for _, row in stock_trades.iterrows():
            tx_type = str(row.get("取引区分", ""))
            shares = float(row["数量"])
            price = float(row["単価"])
            fee = float(row.get("手数料", 0))
            
            if "買" in tx_type:
                cost = price * shares + fee
                held_shares += shares
                total_cost += cost
            
            elif "売" in tx_type and held_shares > 0:
                avg_cost = total_cost / held_shares
                sell_amount = price * shares - fee
                profit = sell_amount - avg_cost * shares
                realized_pnl += profit
                held_shares -= shares
                total_cost -= avg_cost * shares
        
        results.append({
            "銘柄コード": code,
            "銘柄名": stock_trades["銘柄名"].iloc[0] if "銘柄名" in stock_trades.columns else code,
            "実現損益": round(realized_pnl, 0),
            "残保有株数": round(held_shares, 0),
        })
    
    return pd.DataFrame(results)

確定申告サマリーの生成

def generate_tax_report(pnl_df, year=None):
    """確定申告用のサマリーレポートを生成"""
    year = year or datetime.now().year
    TAX_RATE = 0.20315
    
    total_profit = pnl_df[pnl_df["実現損益"] > 0]["実現損益"].sum()
    total_loss = pnl_df[pnl_df["実現損益"] < 0]["実現損益"].sum()
    net_pnl = pnl_df["実現損益"].sum()
    tax_amount = max(net_pnl * TAX_RATE, 0)
    
    sep = "=" * 50
    print(sep)
    print(f"  {year}年 株式取引 損益計算書")
    print(sep)
    print(f"総利益 (プラス銘柄計): {total_profit:,.0f}円")
    print(f"総損失 (マイナス銘柄計): {total_loss:,.0f}円")
    print(f"損益通算後 純損益: {net_pnl:,.0f}円")
    print("-" * 50)
    
    if net_pnl > 0:
        tax_pct = TAX_RATE * 100
        print(f"概算税額 ({tax_pct:.3f}%): {tax_amount:,.0f}円")
        print(f"手取り利益: {net_pnl - tax_amount:,.0f}円")
    else:
        loss_carry = abs(net_pnl)
        print(f"損失のため課税なし(翌年繰越可: {loss_carry:,.0f}円)")
    
    print(sep)
    print("\n銘柄別損益(上位10銘柄):")
    print(pnl_df.sort_values("実現損益", ascending=False).head(10).to_string(index=False))
    
    return {
        "year": year, "gross_profit": total_profit,
        "gross_loss": total_loss, "net_pnl": net_pnl,
        "estimated_tax": tax_amount
    }

サンプルデータでのテスト

def create_sample_trades():
    """テスト用のサンプル取引データを生成"""
    trades = [
        {"約定日": "2024-01-15", "銘柄コード": "7203", "銘柄名": "トヨタ",       "取引区分": "現物買", "数量": 100, "単価": 2500, "手数料": 550},
        {"約定日": "2024-03-20", "銘柄コード": "7203", "銘柄名": "トヨタ",       "取引区分": "現物売", "数量": 100, "単価": 2800, "手数料": 550},
        {"約定日": "2024-02-01", "銘柄コード": "9984", "銘柄名": "ソフトバンクG", "取引区分": "現物買", "数量": 50,  "単価": 7000, "手数料": 550},
        {"約定日": "2024-04-10", "銘柄コード": "9984", "銘柄名": "ソフトバンクG", "取引区分": "現物売", "数量": 50,  "単価": 6500, "手数料": 550},
    ]
    df = pd.DataFrame(trades)
    df["約定日"] = pd.to_datetime(df["約定日"])
    return df

sample = create_sample_trades()
pnl = calculate_profit_loss(sample)
report = generate_tax_report(pnl)
# 期待結果: トヨタ +29,450円, ソフバン -26,100円, 純損益 +3,350円

損益をCSV・Excelにエクスポート

def export_tax_report(pnl_df, report_dict, output_dir="tax_reports"):
    """確定申告用データをCSV・Excelに出力"""
    os.makedirs(output_dir, exist_ok=True)
    year = report_dict["year"]
    
    # CSVで保存
    csv_path = os.path.join(output_dir, f"{year}_pnl.csv")
    pnl_df.to_csv(csv_path, index=False, encoding="utf-8-sig")
    print(f"CSV出力: {csv_path}")
    
    # Excelで保存(複数シート)
    excel_path = os.path.join(output_dir, f"{year}_tax_report.xlsx")
    with pd.ExcelWriter(excel_path) as writer:
        pnl_df.to_excel(writer, sheet_name="銘柄別損益", index=False)
        
        summary = pd.DataFrame([{
            "年度": year,
            "総利益": report_dict["gross_profit"],
            "総損失": report_dict["gross_loss"],
            "純損益": report_dict["net_pnl"],
            "概算税額": report_dict["estimated_tax"],
        }])
        summary.to_excel(writer, sheet_name="サマリー", index=False)
    
    print(f"Excel出力: {excel_path}")

export_tax_report(pnl, report)

まとめ

Pythonで損益計算を自動化することで、確定申告シーズンの大変な手作業から解放されます。実際に使う場合は各証券会社のCSVフォーマットに合わせてカラム名の調整が必要です。

⚠️ 免責事項:このツールはあくまで参考計算です。実際の確定申告には証券会社発行の年間取引報告書を使用し、税務上の判断は税理士にご相談ください。

タイトルとURLをコピーしました