-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.js
288 lines (253 loc) · 11.6 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
const today = new Date().toISOString().split("T")[0];
let endDate = today;
// デフォルト値の設定
function setDefaultDates() {
document.getElementById("startDate").max = today;
const endDateInput = document.getElementById("endDate");
endDateInput.value = today;
endDateInput.max = today;
endDateInput.style.visibility = "visible";
}
// イベントハンドラーの設定
function setupEventHandlers() {
document.getElementById("amount").addEventListener("focus", selectInputText);
document.getElementById("amount").addEventListener("blur", formatAmountInput);
document.getElementById("amount").addEventListener("keydown", (e) => closeKeyboardOnEnter(e));
document.getElementById("investmentForm").addEventListener("submit", (e) => handleFormSubmit(e));
}
// ページ読み込み時の処理
document.addEventListener("DOMContentLoaded", () => {
setDefaultDates();
setupEventHandlers();
setupDateExamples();
});
function setupDateExamples() {
document.querySelectorAll(".date-example").forEach((item) => {
item.addEventListener("click", function () {
const dateText = this.textContent.match(/\d{4}年\d{1,2}月\d{1,2}日/);
if (dateText) {
const dateParts = dateText[0].split(/年|月|日/);
const formattedDate = `${dateParts[0]}-${dateParts[1].padStart(2, "0")}-${dateParts[2].padStart(2, "0")}`;
document.getElementById("startDate").value = formattedDate;
}
});
});
}
function formatAmountInput(event) {
const numericValue = event.target.value.replace(/\D/g, "");
if (numericValue) {
event.target.value = parseInt(numericValue, 10).toLocaleString("ja-JP");
} else {
event.target.value = "";
}
}
function selectInputText(event) {
event.target.select();
}
function closeKeyboardOnEnter(event) {
if (event.key === "Enter") {
event.preventDefault();
event.target.blur();
}
}
function handleFormSubmit(event) {
event.preventDefault();
showLoadingAnimation(); // ローディングアニメーションを表示
const startDate = document.getElementById("startDate").value;
endDate = document.getElementById("endDate").value;
const investmentAmount = parseInt(document.getElementById("amount").value.replace(/,/g, ""), 10);
fetchBitcoinData(startDate, endDate, investmentAmount);
}
// ローディングアニメーションを表示
function showLoadingAnimation() {
const submitButton = document.getElementById("submitBtn");
const spinnerWrapper = submitButton.querySelector(".spinner-wrapper"); // スピナーのラッパー要素を取得
const buttonText = submitButton.querySelector(".button-text");
spinnerWrapper.style.display = "block"; // スピナーラッパーを表示
buttonText.style.display = "none"; // ボタンのテキストを非表示
// ボタンを非活性化するのを少し遅らせる
setTimeout(() => {
submitButton.disabled = true;
}, 100); // 100ミリ秒後に実行
}
// ローディングアニメーションを非表示
function hideLoadingAnimation() {
const submitButton = document.getElementById("submitBtn");
const spinnerWrapper = submitButton.querySelector(".spinner-wrapper"); // スピナーのラッパー要素を取得
const buttonText = submitButton.querySelector(".button-text");
submitButton.disabled = false;
spinnerWrapper.style.display = "none"; // スピナーラッパーを非表示
buttonText.style.display = "block"; // ボタンのテキストを表示
}
// 最新のビットコイン価格を取得する関数
async function fetchLatestPrice() {
const apiUrl = "https://public.bitbank.cc/btc_jpy/ticker";
try {
const response = await fetch(apiUrl);
const data = await response.json();
const timestamp = new Date(data.data.timestamp); // タイムスタンプを取得してDateオブジェクトに変換
return { price: parseInt(data.data.last, 10), timestamp }; // 最終取引価格とタイムスタンプを返す
} catch (error) {
console.error("ビットコイン価格の取得に失敗しました:", error);
return { price: 0, timestamp: null }; // エラー時は0とnullを返す
}
}
async function fetchLocalData(year) {
const response = await fetch(`data/${year}.json`);
return await response.json();
}
function formatDailyDataBitBank(ohlcv) {
return {
prices: ohlcv.map((item) => [item[5], item[0]]), // Unixタイムスタンプとオープン価格の配列に変換
};
}
// 各年のビットコイン価格を取得する関数
async function fetchBitcoinData(startDate, endDate, investmentAmount) {
const startTime = Date.now();
const startYear = new Date(startDate).getFullYear();
const endYear = new Date(endDate).getFullYear();
const apiPromises = [];
const localDataPromises = [];
const latestData = await fetchLatestPrice();
const latestPrice = latestData.price;
const latestTimestamp = latestData.timestamp;
// 2017年より前のデータをローカルから取得
for (let year = startYear; year < 2017 && year <= endYear; year++) {
localDataPromises.push(fetchLocalData(year));
}
// 2017年以降のデータをAPIから取得
for (let year = Math.max(startYear, 2017); year <= endYear; year++) {
const apiUrl = `https://public.bitbank.cc/btc_jpy/candlestick/1day/${year}`;
apiPromises.push(
fetch(apiUrl)
.then((response) => response.json())
.catch((error) => {
console.error(`Error fetching data for year ${year}:`, error);
return null; // エラーが発生した場合はnullを返して処理を続ける
})
);
}
// すべてのプロミスが完了したら、データを結合して処理
Promise.all([...localDataPromises, ...apiPromises])
.then((results) => {
const validResults = results.filter((result) => result !== null);
const allData = validResults.flatMap((result) => (result.data ? result.data.candlestick[0].ohlcv : result));
const formattedData = formatDailyDataBitBank(allData);
displayResults(formattedData, investmentAmount, startDate, endDate, latestPrice, latestTimestamp);
})
.catch((error) => {
console.error("Error fetching data: ", error);
})
.finally(() => {
const elapsedTime = Date.now() - startTime; // 経過時間を計算
const delay = Math.max(250 - elapsedTime, 0);
setTimeout(hideLoadingAnimation, delay); // 計算した遅延時間後にローディングアニメーションを非表示にする
});
}
function createPriceMap(prices) {
return prices.reduce((acc, [date, value]) => {
acc[new Date(date).toISOString().split("T")[0]] = value;
return acc;
}, {});
}
async function displayResults(data, investmentAmount, startDate, endDate, latestPrice, latestTimestamp) {
const priceMap = createPriceMap(data.prices);
const { totalInvestment, totalBitcoinPurchased, investmentCount, investmentDetails } = processInvestments(investmentAmount, startDate, endDate, priceMap);
updateResultsDisplay(totalInvestment, totalBitcoinPurchased, investmentCount, investmentDetails, latestPrice, latestTimestamp);
}
function getNextInvestmentDate(currentDate, purchaseFrequency) {
let nextDate = new Date(currentDate);
if (purchaseFrequency === "weekly") {
nextDate.setDate(currentDate.getDate() + 7);
} else if (purchaseFrequency === "monthly") {
nextDate.setMonth(currentDate.getMonth() + 1);
} else {
nextDate.setDate(currentDate.getDate() + 1);
}
return nextDate;
}
function processInvestments(investmentAmount, startDate, endDate, priceMap) {
let totalInvestment = 0,
totalBitcoinPurchased = 0,
investmentCount = 0;
let investmentDetails = "";
let currentDate = new Date(startDate);
const endDateObj = new Date(endDate);
const purchaseFrequency = document.querySelector('input[name="purchaseFrequency"]:checked').value;
while (currentDate <= endDateObj) {
const dateString = currentDate.toISOString().split("T")[0];
// 指定日、翌日、翌々日の価格をチェック
for (let i = 0; i <= 2; i++) {
let tryDate = new Date(currentDate);
tryDate.setDate(tryDate.getDate() + i);
const tryDateString = tryDate.toISOString().split("T")[0];
if (tryDateString in priceMap) {
const investmentDetail = calculateInvestmentDetails(tryDate, priceMap[tryDateString], investmentAmount);
const result = updateInvestmentResult(investmentDetail, investmentDetails, totalInvestment, totalBitcoinPurchased, investmentCount, investmentAmount);
totalInvestment = result.totalInvestment;
totalBitcoinPurchased = result.totalBitcoinPurchased;
investmentCount = result.investmentCount;
investmentDetails = result.investmentDetails;
break; // 価格が見つかったらその日で投資を行い、ループを抜ける
}
}
// 次の購入可能日へ移動
currentDate = getNextInvestmentDate(currentDate, purchaseFrequency);
}
return { totalInvestment, totalBitcoinPurchased, investmentCount, investmentDetails };
}
function calculateInvestmentDetails(date, value, investmentAmount) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const btcAmount = investmentAmount / value;
return { year, month, day, btcAmount, value };
}
function updateInvestmentResult(investmentDetail, investmentDetails, totalInvestment, totalBitcoinPurchased, investmentCount, investmentAmount) {
const { year, month, day, btcAmount, value } = investmentDetail;
totalInvestment += investmentAmount;
totalBitcoinPurchased += btcAmount;
investmentCount += 1;
const formattedDate = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
const roundedValue = Math.round(value);
investmentDetails += `
<tr>
<td>${formattedDate}</td>
<td>${roundedValue.toLocaleString("ja-JP")}</td>
<td>${btcAmount.toFixed(8)}</td>
</tr>`;
return { totalInvestment, totalBitcoinPurchased, investmentCount, investmentDetails };
}
function updateResultsDisplay(totalInvestment, totalBitcoinPurchased, investmentCount, investmentDetails, latestPrice, latestTimestamp) {
const totalValue = totalBitcoinPurchased * latestPrice;
const investmentMultiple = totalValue / totalInvestment;
const roundedTotalValue = Math.round(totalValue);
const formattedTimestamp = latestTimestamp ? `${latestTimestamp.getFullYear()}年${latestTimestamp.getMonth() + 1}月${latestTimestamp.getDate()}日 ${latestTimestamp.getHours()}時${latestTimestamp.getMinutes()}分${latestTimestamp.getSeconds()}秒` : "取得不可";
document.getElementById("results").innerHTML = `
<h2 class="results-header">結果</h2>
<p class="results-text">購入回数:${investmentCount} 回</p>
<p class="results-text">総購入金額:${totalInvestment.toLocaleString("ja-JP")} 円</p>
<p class="results-timestamp">${formattedTimestamp}</p>
<p class="results-important1">現在の評価額:${roundedTotalValue.toLocaleString("ja-JP")} 円</p>
<p class="results-important2">倍率:${investmentMultiple.toFixed(2)}倍</p>
<p class="results-btc">保有BTC:${totalBitcoinPurchased.toFixed(8)} BTC</p>
<details class="detail-history-d">
<summary class="detail-history-s">購入履歴の詳細</summary>
<table>
<thead>
<tr>
<th>日付</th>
<th>BTC価格 (円)</th>
<th>購入量 (BTC)</th>
</tr>
</thead>
<tbody>
${investmentDetails}
</tbody>
</table>
<p style="font-size: small; margin-top: 20px; margin-bottom: 20px;">
※ 価格データが存在しない日付は翌日に二回購入しています
</p>
</details>
`;
}