コードです。

HTML

                    <div class="form-group">
                        <label>投資元本(万円):</label>
                        <input type="range" class="initial-investment" min="0" max="1000" step="10" value="100">
                        <span class="initial-investment-value">100万円</span>
                    </div>

                    <div class="form-group">
                        <label>月間投資額(万円):</label>
                        <input type="range" class="monthly-investment" min="0" max="100" step="1" value="0">
                        <span class="monthly-investment-value">0万円</span>
                    </div>

                    <div class="form-group">
                        <label>年利率 (%):</label>
                        <select class="annual-return-rate">
                            <option value="3">3%</option>
                            <option value="5">5%</option>
                            <option value="8">8%</option>
                        </select>
                    </div>

                    <div class="form-group">
                        <label>リスクレベル:</label>
                        <select class="risk-level">
                            <option value="low">低リスク</option>
                            <option value="medium" selected>中リスク</option>
                            <option value="high">高リスク</option>
                        </select>
                    </div>
                </div>
            </div>

            <button type="button" id="add-product-button">投資商品を追加</button>

            <div class="form-group">
                <label for="target-amount">目標金額(万円):</label>
                <input type="range" id="target-amount" name="targetAmount" min="0" max="10000" step="100" value="2000">
                <span id="target-amount-value">2000万円</span>
            </div>

            <button type="button" id="simulate-button">シミュレーション開始</button>
            <button type="reset">リセット</button>
        </form>
    </div>

    <!-- ポートフォリオ表示 -->
    <div class="portfolio-container">
        <h2>ポートフォリオ</h2>
        <table id="portfolio-table">
            <thead>
                <tr>
                    <th>商品名</th>
                    <th>投資元本(万円)</th>
                    <th>月間投資額(万円)</th>
                    <th>年利率(%)</th>
                    <th>リスクレベル</th>
                </tr>
            </thead>
            <tbody id="portfolio-body">
            </tbody>
        </table>
    </div>

    <div class="result-container">
        <h2>シミュレーション結果</h2>
        <div class="tabs">
            <button class="tab-button active" data-scenario="normal">通常</button>
            <button class="tab-button" data-scenario="expansion">景気拡大</button>
            <button class="tab-button" data-scenario="recession">リセッション</button>
        </div>
        <p id="result-text"></p>
        <canvas id="result-chart"></canvas>
        <button id="download-pdf">結果をPDFでダウンロード</button>
        <table id="result-table">
            <thead>
                <tr>
                    <th>年数</th>
                    <th>元本</th>
                    <th>追加投資</th>
                    <th>利息収益</th>
                    <th>合計 (元本+追加投資+利息収益)</th>
                </tr>
            </thead>
            <tbody id="result-body">
            </tbody>
        </table>
    </div>
</div>

<!-- JavaScriptファイル -->
<script src="script.js"></script>



CSS

/* styles.css / body { font-family: ‘Roboto’, sans-serif; margin: 0; background-color: #f5f7fa; color: #333; font-size: 16px; / 文字サイズを大きく */
}

.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}

h1, h2, h3 {
text-align: center;
color: #333;
}

.form-container, .portfolio-container, .result-container {
background-color: #fff;
padding: 20px;
border-radius: 5px;
margin-top: 20px;
}

.form-group {
margin-bottom: 15px;
}

label {
display: block;
font-weight: bold;
margin-bottom: 5px;
}

input[type=”range”], input[type=”text”], select {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 5px;
}

span {
display: block;
margin-top: 5px;
color: #555;
}

button {
margin-top: 20px;
padding: 12px 24px;
background-color: #007BFF;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}

button[type=”reset”] {
background-color: #6c757d;
margin-left: 10px;
}

download-pdf {

background-color: #28a745;

}

.result-container {
margin-top: 30px;
}

result-text {

text-align: center;
font-size: 20px;
margin-bottom: 20px;

}

result-chart {

max-width: 100%;
height: 400px;

}

table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}

table th, table td {
border: 1px solid #dee2e6;
padding: 12px;
text-align: center;
}

table th {
background-color: #e9ecef;
}

.tabs {
display: flex;
margin-bottom: 20px;
}

.tab-button {
flex: 1;
padding: 10px;
cursor: pointer;
background-color: #e9ecef;
border: none;
outline: none;
transition: background-color 0.3s;
font-size: 16px;
}

.tab-button.active {
background-color: #007BFF;
color: #fff;
}

.investment-product {
border: 1px solid #dee2e6;
padding: 15px;
border-radius: 5px;
margin-bottom: 15px;
background-color: #f8f9fa;
}

.portfolio-container {
margin-top: 30px;
}

Java

// script.js

// 投資商品のカウンター
let productCount = 1;

// スライダーの値を表示する関数
function updateSliderValue(slider, valueDisplay, unit) {
valueDisplay.textContent = slider.value + unit;
slider.addEventListener(‘input’, function() {
valueDisplay.textContent = this.value + unit;
});
}

// 投資商品を追加する関数
function addInvestmentProduct() {
productCount++;
const investmentProductsDiv = document.getElementById(‘investment-products’);

const productDiv = document.createElement('div');
productDiv.classList.add('investment-product');

const productTitle = document.createElement('h3');
productTitle.textContent = `投資商品${productCount}`;
productDiv.appendChild(productTitle);

// 商品名
const nameGroup = document.createElement('div');
nameGroup.classList.add('form-group');
const nameLabel = document.createElement('label');
nameLabel.textContent = '商品名:';
nameGroup.appendChild(nameLabel);
const nameInput = document.createElement('input');
nameInput.type = 'text';
nameInput.classList.add('product-name');
nameInput.value = `投資商品${productCount}`;
nameGroup.appendChild(nameInput);
productDiv.appendChild(nameGroup);

// 投資元本
const initialInvestmentGroup = document.createElement('div');
initialInvestmentGroup.classList.add('form-group');
const initialInvestmentLabel = document.createElement('label');
initialInvestmentLabel.textContent = '投資元本(万円):';
initialInvestmentGroup.appendChild(initialInvestmentLabel);
const initialInvestmentSlider = document.createElement('input');
initialInvestmentSlider.type = 'range';
initialInvestmentSlider.classList.add('initial-investment');
initialInvestmentSlider.min = '0';
initialInvestmentSlider.max = '1000';
initialInvestmentSlider.step = '10';
initialInvestmentSlider.value = '100';
initialInvestmentGroup.appendChild(initialInvestmentSlider);
const initialInvestmentValue = document.createElement('span');
initialInvestmentValue.classList.add('initial-investment-value');
initialInvestmentValue.textContent = '100万円';
initialInvestmentGroup.appendChild(initialInvestmentValue);
productDiv.appendChild(initialInvestmentGroup);

// 月間投資額
const monthlyInvestmentGroup = document.createElement('div');
monthlyInvestmentGroup.classList.add('form-group');
const monthlyInvestmentLabel = document.createElement('label');
monthlyInvestmentLabel.textContent = '月間投資額(万円):';
monthlyInvestmentGroup.appendChild(monthlyInvestmentLabel);
const monthlyInvestmentSlider = document.createElement('input');
monthlyInvestmentSlider.type = 'range';
monthlyInvestmentSlider.classList.add('monthly-investment');
monthlyInvestmentSlider.min = '0';
monthlyInvestmentSlider.max = '100';
monthlyInvestmentSlider.step = '1';
monthlyInvestmentSlider.value = '0';
monthlyInvestmentGroup.appendChild(monthlyInvestmentSlider);
const monthlyInvestmentValue = document.createElement('span');
monthlyInvestmentValue.classList.add('monthly-investment-value');
monthlyInvestmentValue.textContent = '0万円';
monthlyInvestmentGroup.appendChild(monthlyInvestmentValue);
productDiv.appendChild(monthlyInvestmentGroup);

// 年利率
const annualReturnGroup = document.createElement('div');
annualReturnGroup.classList.add('form-group');
const annualReturnLabel = document.createElement('label');
annualReturnLabel.textContent = '年利率 (%):';
annualReturnGroup.appendChild(annualReturnLabel);
const annualReturnSelect = document.createElement('select');
annualReturnSelect.classList.add('annual-return-rate');
const options = [
    { value: '3', text: '3%' },
    { value: '5', text: '5%' },
    { value: '8', text: '8%' }
];
options.forEach(optionData => {
    const option = document.createElement('option');
    option.value = optionData.value;
    option.textContent = optionData.text;
    annualReturnSelect.appendChild(option);
});
annualReturnGroup.appendChild(annualReturnSelect);
productDiv.appendChild(annualReturnGroup);

// リスクレベル
const riskGroup = document.createElement('div');
riskGroup.classList.add('form-group');
const riskLabel = document.createElement('label');
riskLabel.textContent = 'リスクレベル:';
riskGroup.appendChild(riskLabel);
const riskSelect = document.createElement('select');
riskSelect.classList.add('risk-level');
const riskOptions = [
    { value: 'low', text: '低リスク' },
    { value: 'medium', text: '中リスク' },
    { value: 'high', text: '高リスク' }
];
riskOptions.forEach(optionData => {
    const option = document.createElement('option');
    option.value = optionData.value;
    option.textContent = optionData.text;
    riskSelect.appendChild(option);
});
riskSelect.value = 'medium'; // デフォルトを中リスクに
riskGroup.appendChild(riskSelect);
productDiv.appendChild(riskGroup);

investmentProductsDiv.appendChild(productDiv);

// スライダーの値を初期化
updateSliderValue(initialInvestmentSlider, initialInvestmentValue, '万円');
updateSliderValue(monthlyInvestmentSlider, monthlyInvestmentValue, '万円');

}

// 初期投資商品のスライダー値を初期化
document.querySelectorAll(‘.investment-product’).forEach(product => {
const initialInvestmentSlider = product.querySelector(‘.initial-investment’);
const initialInvestmentValue = product.querySelector(‘.initial-investment-value’);
updateSliderValue(initialInvestmentSlider, initialInvestmentValue, ‘万円’);

const monthlyInvestmentSlider = product.querySelector('.monthly-investment');
const monthlyInvestmentValue = product.querySelector('.monthly-investment-value');
updateSliderValue(monthlyInvestmentSlider, monthlyInvestmentValue, '万円');

});

// 目標金額のスライダー値を初期化
const targetAmountSlider = document.getElementById(‘target-amount’);
const targetAmountValue = document.getElementById(‘target-amount-value’);
updateSliderValue(targetAmountSlider, targetAmountValue, ‘万円’);

// 投資商品追加ボタンのイベントリスナー
document.getElementById(‘add-product-button’).addEventListener(‘click’, addInvestmentProduct);

// タブの機能を追加
const tabButtons = document.querySelectorAll(‘.tab-button’);
tabButtons.forEach(button => {
button.addEventListener(‘click’, () => {
tabButtons.forEach(btn => btn.classList.remove(‘active’));
button.classList.add(‘active’);
const scenario = button.getAttribute(‘data-scenario’);
simulate(scenario);
});
});

// シミュレーション開始ボタンのイベントリスナー
document.getElementById(‘simulate-button’).addEventListener(‘click’, function() {
// 最初のタブをアクティブに
tabButtons.forEach(btn => btn.classList.remove(‘active’));
tabButtons[0].classList.add(‘active’);
simulate(‘normal’);
});

// シミュレーションを実行する関数
function simulate(scenario) {
const targetAmount = parseFloat(targetAmountSlider.value) * 10000; // 万円を円に変換

// 経済シナリオに基づく年利の調整
let annualReturnAdjustment = 0;
if (scenario === 'expansion') {
    annualReturnAdjustment = 0.02; // 年利+2%
} else if (scenario === 'recession') {
    annualReturnAdjustment = -0.02; // 年利-2%
}

// 投資商品の情報を取得
const products = [];
document.querySelectorAll('.investment-product').forEach(product => {
    const name = product.querySelector('.product-name').value;
    const initialInvestment = parseFloat(product.querySelector('.initial-investment').value) * 10000;
    const monthlyInvestment = parseFloat(product.querySelector('.monthly-investment').value) * 10000;
    let annualReturnRate = parseFloat(product.querySelector('.annual-return-rate').value) / 100;
    const riskLevel = product.querySelector('.risk-level').value;

    // リスクレベルに応じてリターン率を調整
    if (riskLevel === 'low') {
        annualReturnRate -= 0.01; // 年利-1%
    } else if (riskLevel === 'high') {
        annualReturnRate += 0.01; // 年利+1%
    }

    // 経済シナリオによる調整を適用
    annualReturnRate += annualReturnAdjustment;

    products.push({
        name,
        initialInvestment,
        monthlyInvestment,
        annualReturnRate,
        riskLevel
    });
});

// ポートフォリオを表示
const portfolioBody = document.getElementById('portfolio-body');
portfolioBody.innerHTML = '';
products.forEach(product => {
    const row = document.createElement('tr');

    const nameCell = document.createElement('td');
    nameCell.textContent = product.name;
    row.appendChild(nameCell);

    const initialInvestmentCell = document.createElement('td');
    initialInvestmentCell.textContent = (product.initialInvestment / 10000).toLocaleString() + '万円';
    row.appendChild(initialInvestmentCell);

    const monthlyInvestmentCell = document.createElement('td');
    monthlyInvestmentCell.textContent = (product.monthlyInvestment / 10000).toLocaleString() + '万円';
    row.appendChild(monthlyInvestmentCell);

    const annualReturnCell = document.createElement('td');
    annualReturnCell.textContent = (product.annualReturnRate * 100).toFixed(2) + '%';
    row.appendChild(annualReturnCell);

    const riskLevelCell = document.createElement('td');
    if (product.riskLevel === 'low') {
        riskLevelCell.textContent = '低リスク';
    } else if (product.riskLevel === 'medium') {
        riskLevelCell.textContent = '中リスク';
    } else {
        riskLevelCell.textContent = '高リスク';
    }
    row.appendChild(riskLevelCell);

    portfolioBody.appendChild(row);
});

// シミュレーション期間を20年に設定
const maxYears = 20;

// シミュレーション結果の初期化
const resultBody = document.getElementById('result-body');
resultBody.innerHTML = '';
const labels = [];
const principalData = [];
const additionalInvestmentData = [];
const interestData = [];
const totalData = [];

let totalPrincipal = 0;
let totalAdditionalInvestment = 0;
let totalAmount = 0;
let achieved = false;
let achievedYear = null;

for (let year = 0; year <= maxYears; year++) {
    let yearlyPrincipal = 0;
    let yearlyAdditionalInvestment = 0;
    let yearlyInterest = 0;
    let yearlyAmount = 0;

    products.forEach(product => {
        let principal = product.initialInvestment;
        let totalInvestment = product.initialInvestment + product.monthlyInvestment * 12 * year;
        let amount = 0;

        // 元本と追加投資の合計額
        let totalContributions = product.initialInvestment + product.monthlyInvestment * 12 * year;

        // 合計金額を計算
        amount = totalContributions * Math.pow(1 + product.annualReturnRate, year);

        // 利息収益を計算
        let interest = amount - totalContributions;

        yearlyPrincipal += product.initialInvestment;
        yearlyAdditionalInvestment += product.monthlyInvestment * 12 * year;
        yearlyInterest += interest;
        yearlyAmount += amount;
    });

    // テーブルにデータを追加
    let row = document.createElement('tr');

    let yearCell = document.createElement('td');
    yearCell.textContent = `${year}年目`;
    row.appendChild(yearCell);

    let principalCell = document.createElement('td');
    principalCell.textContent = Math.round(yearlyPrincipal).toLocaleString() + '円';
    row.appendChild(principalCell);

    let additionalInvestmentCell = document.createElement('td');
    additionalInvestmentCell.textContent = Math.round(yearlyAdditionalInvestment).toLocaleString() + '円';
    row.appendChild(additionalInvestmentCell);

    let interestCell = document.createElement('td');
    interestCell.textContent = Math.round(yearlyInterest).toLocaleString() + '円';
    row.appendChild(interestCell);

    let totalCell = document.createElement('td');
    totalCell.textContent = Math.round(yearlyAmount).toLocaleString() + '円';
    row.appendChild(totalCell);

    resultBody.appendChild(row);

    // グラフ用データを追加
    labels.push(`${year}`);
    principalData.push(Math.round(yearlyPrincipal));
    additionalInvestmentData.push(Math.round(yearlyAdditionalInvestment));
    interestData.push(Math.round(yearlyInterest));
    totalData.push(Math.round(yearlyAmount));

    // 目標金額に到達したか確認
    if (!achieved && yearlyAmount >= targetAmount) {
        achieved = true;
        achievedYear = year;
    }
}

// 結果テキストを表示
let resultText = '';
if (achieved) {
    resultText = `目標金額に${achievedYear}年で到達します。`;
} else {
    resultText = `目標金額に到達できませんでした。`;
}
document.getElementById('result-text').innerText = resultText;

// グラフを描画
const ctx = document.getElementById('result-chart').getContext('2d');
if (window.resultChart) {
    window.resultChart.destroy();
}
window.resultChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: labels,
        datasets: [
            {
                label: '元本',
                data: principalData,
                backgroundColor: '#007BFF',
            },
            {
                label: '追加投資',
                data: additionalInvestmentData,
                backgroundColor: '#28a745',
            },
            {
                label: '利息収益',
                data: interestData,
                backgroundColor: '#ffc107',
            },
        ]
    },
    options: {
        responsive: true,
        scales: {
            x: {
                stacked: true,
                title: {
                    display: true,
                    text: '年数',
                    font: {
                        size: 16
                    }
                }
            },
            y: {
                stacked: true,
                title: {
                    display: true,
                    text: '金額(円)',
                    font: {
                        size: 16
                    }
                },
                ticks: {
                    callback: function(value) {
                        return value.toLocaleString();
                    },
                    font: {
                        size: 14
                    }
                }
            },
        },
        plugins: {
            tooltip: {
                callbacks: {
                    label: function(context) {
                        let label = context.dataset.label || '';
                        if (label) {
                            label += ': ';
                        }
                        label += context.parsed.y.toLocaleString() + '円';
                        return label;
                    }
                }
            },
            legend: {
                labels: {
                    font: {
                        size: 16
                    }
                }
            }
        }
    }
});

}

// PDF出力機能の実装
document.getElementById(‘download-pdf’).addEventListener(‘click’, function() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();

// 結果テキストをPDFに追加
doc.setFontSize(16);
doc.text(document.getElementById('result-text').innerText, 10, 20);

// キャンバスを画像として取得
const canvas = document.getElementById('result-chart');
const canvasImg = canvas.toDataURL('image/png', 1.0);

// グラフをPDFに追加
doc.addImage(canvasImg, 'PNG', 10, 30, 190, 80);

// テーブルの内容をPDFに追加
let finalY = 120;
doc.autoTable({ html: '#result-table', startY: finalY });

// PDFをダウンロード
doc.save('simulation_result.pdf');

});

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

当サイト管理人です。youtube登録者5,800人で「大切な資産を着実に増やし投資も楽しむ」をモットーに日々投資情報を発信しています。

目次