XMの取引報告書から年間収支だす

from flask import Flask, render_template_string
from bs4 import BeautifulSoup
import pandas as pd
from datetime import datetime

def extract_all_transactions(html_content):
    """HTMLから全ての取引を抽出"""
    soup = BeautifulSoup(html_content, 'html.parser')
    rows = soup.find_all('tr', align='right')
    
    transactions = []
    for row in rows:
        cols = row.find_all('td')
        if len(cols) >= 3:
            try:
                transaction_type = cols[2].text.strip()
                if transaction_type == 'balance':
                    # Balance取引の場合
                    amount = float(cols[-1].text.strip().replace(',', ''))
                    date = datetime.strptime(cols[1].text.strip(), '%Y.%m.%d %H:%M:%S')
                    transactions.append({
                        'date': date,
                        'type': transaction_type,
                        'amount': amount,
                        'swap': 0,
                        'profit': 0
                    })
                else:
                    # 取引の場合(スワップと損益を分離)
                    swap = float(cols[-2].text.strip().replace(',', ''))
                    profit = float(cols[-1].text.strip().replace(',', ''))
                    date = datetime.strptime(cols[1].text.strip(), '%Y.%m.%d %H:%M:%S')
                    transactions.append({
                        'date': date,
                        'type': transaction_type,
                        'amount': profit,  # 損益を amount として扱う
                        'swap': swap,
                        'profit': profit
                    })
            except (ValueError, IndexError):
                continue
    
    df = pd.DataFrame(transactions)
    if not df.empty:
        df = df.sort_values('date', ascending=False)
    return df

def calculate_detailed_summary(df):
    """詳細な取引集計の計算"""
    # Balance取引の集計
    balance_df = df[df['type'] == 'balance']
    deposits = balance_df[balance_df['amount'] > 0]['amount'].sum()
    withdrawals = abs(balance_df[balance_df['amount'] < 0]['amount'].sum())
    
    # スワップ損益の計算
    swap_profit = df['swap'].sum()
    
    # 取引損益の計算(balanceタイプ以外の取引の損益)
    trading_profit = df[df['type'] != 'balance']['profit'].sum()
    
    # 総合収支(入金 - 出金 + 取引損益 + スワップ)
    total = deposits - withdrawals + trading_profit + swap_profit
    
    return {
        'deposits': deposits if pd.notnull(deposits) else 0,
        'withdrawals': withdrawals if pd.notnull(withdrawals) else 0,
        'trading_profit': trading_profit if pd.notnull(trading_profit) else 0,
        'swap_profit': swap_profit if pd.notnull(swap_profit) else 0,
        'total': total if pd.notnull(total) else 0
    }

HTML_TEMPLATE = '''
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>取引分析</title>
    <style>
        body {
            font-family: "Yu Gothic", "メイリオ", sans-serif;
            margin: 20px;
            background-color: #f5f5f5;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        .summary {
            margin-top: 20px;
            padding: 20px;
            background-color: #f8f9fa;
            border-radius: 5px;
            font-weight: bold;
        }
        .summary-item {
            display: grid;
            grid-template-columns: 2fr 1fr;
            gap: 10px;
            margin: 10px 0;
            padding: 5px 0;
            border-bottom: 1px solid #eee;
        }
        .summary-total {
            margin-top: 20px;
            padding-top: 10px;
            border-top: 2px solid #333;
        }
        .amount { text-align: right; }
        .positive { color: #28a745; }
        .negative { color: #dc3545; }
    </style>
</head>
<body>
    <div class="container">
        <h1>取引分析サマリー</h1>
        
        <div class="summary">
            <h2>取引集計</h2>
            <div class="summary-item">
                <div>入金合計</div>
                <div class="amount positive">
                    ¥{{ "{:,.0f}".format(summary['deposits']) }}
                </div>
            </div>
            <div class="summary-item">
                <div>出金合計</div>
                <div class="amount negative">
                    ¥{{ "{:,.0f}".format(summary['withdrawals']) }}
                </div>
            </div>
            <div class="summary-item">
                <div>取引損益</div>
                <div class="amount {{ 'positive' if summary['trading_profit'] >= 0 else 'negative' }}">
                    ¥{{ "{:,.0f}".format(summary['trading_profit']) }}
                </div>
            </div>
            <div class="summary-item">
                <div>スワップ損益</div>
                <div class="amount {{ 'positive' if summary['swap_profit'] >= 0 else 'negative' }}">
                    ¥{{ "{:,.0f}".format(summary['swap_profit']) }}
                </div>
            </div>
            <div class="summary-item summary-total">
                <div>総合収支(入金 - 出金 + 取引損益 + スワップ)</div>
                <div class="amount {{ 'positive' if summary['total'] >= 0 else 'negative' }}">
                    ¥{{ "{:,.0f}".format(summary['total']) }}
                </div>
            </div>
        </div>
    </div>
</body>
</html>
'''

app = Flask(__name__)

@app.route('/')
def home():
    with open('44103576_tax_report.html', 'r', encoding='utf-8') as file:
        html_content = file.read()
    
    df = extract_all_transactions(html_content)
    summary = calculate_detailed_summary(df)
    
    return render_template_string(HTML_TEMPLATE, summary=summary)

if __name__ == '__main__':
    app.run(debug=True, port=8000)


いいなと思ったら応援しよう!

ホソノP
よろしければサポートお願いします! いただいたサポートはクリエイターとしての活動費に使わせていただきます!