trading-record / index.html
alterzick's picture
Add 3 files
f4f418d verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Trading Record Pro</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://kit.fontawesome.com/a2f8a9e4e3.js" crossorigin="anonymous"></script>
<link href="https://cdn.jsdelivr.net/npm/flowbite@2.3.0/dist/flowbite.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.bg-gradient-primary {
background: linear-gradient(135deg, #1e3a8a, #3b82f6);
}
.card-hover {
transition: transform 0.3s, box-shadow 0.3s;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
.active-tab {
@apply border-b-2 border-blue-500 text-blue-600 font-semibold;
}
.tab {
@apply cursor-pointer py-3 px-4 text-gray-500 hover:text-blue-600 transition;
}
.news-card {
background: linear-gradient(to right, #f8fafc, #e2e8f0);
}
</style>
</head>
<body class="bg-gray-50 min-h-screen font-sans">
<!-- Navbar -->
<nav class="bg-white shadow-lg fixed w-full top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<div class="flex items-center">
<i class="fas fa-chart-line text-blue-600 text-2xl mr-2"></i>
<span class="font-bold text-xl text-gray-800">Trading Record Pro</span>
</div>
<div class="flex space-x-4">
<button id="dashboard-tab" class="tab active-tab">Dashboard</button>
<button id="record-tab" class="tab">Catat Saham</button>
<button id="history-tab" class="tab">Riwayat</button>
</div>
</div>
</div>
</nav>
<div class="pt-20 pb-10 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto">
<!-- Dashboard Tab -->
<div id="dashboard-content" class="tab-content">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="bg-white p-6 rounded-xl shadow-md card-hover text-center">
<i class="fas fa-coins text-4xl text-green-500 mb-3"></i>
<h3 class="text-gray-500 text-sm font-medium">Total Investasi</h3>
<p id="total-investment" class="text-2xl font-bold text-gray-800">Rp 0</p>
</div>
<div class="bg-white p-6 rounded-xl shadow-md card-hover text-center">
<i class="fas fa-chart-line text-4xl text-blue-500 mb-3"></i>
<h3 class="text-gray-500 text-sm font-medium">Gain Rata-rata</h3>
<p id="avg-gain" class="text-2xl font-bold text-gray-800">0%</p>
</div>
<div class="bg-white p-6 rounded-xl shadow-md card-hover text-center">
<i class="fas fa-list-ul text-4xl text-purple-500 mb-3"></i>
<h3 class="text-gray-500 text-sm font-medium">Total Rekor</h3>
<p id="total-records" class="text-2xl font-bold text-gray-800">0</p>
</div>
<div class="bg-white p-6 rounded-xl shadow-md card-hover text-center">
<i class="fas fa-bullseye text-4xl text-yellow-500 mb-3"></i>
<h3 class="text-gray-500 text-sm font-medium">Win Rate</h3>
<p id="win-rate" class="text-2xl font-bold text-gray-800">0%</p>
</div>
</div>
<!-- Chart & Recent Activity -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
<div class="bg-white p-6 rounded-xl shadow">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Perkembangan Portofolio</h3>
<canvas id="portfolioChart" height="250"></canvas>
</div>
<div class="bg-white p-6 rounded-xl shadow">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Rekomendasi Berita Saat Ini</h3>
<div id="news-container" class="space-y-4 h-72 overflow-y-auto">
<!-- News items will be injected here -->
<div class="news-card p-4 rounded-lg shadow-sm flex">
<img src="https://via.placeholder.com/60" alt="News" class="rounded mr-3 object-cover">
<div>
<h4 class="font-medium text-gray-800">IHSG Diramal Menguat, Ini Rekomendasi Saham Hari Ini</h4>
<p class="text-sm text-gray-600">Bisnis.com - 2 jam lalu</p>
<a href="#" class="text-blue-600 text-xs mt-1 block">Baca selengkapnya</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Record Tab -->
<div id="record-content" class="tab-content hidden">
<div class="bg-white p-6 rounded-xl shadow mb-8">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Catat Transaksi Saham Baru</h2>
<form id="trade-form" class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Nama Saham</label>
<input type="text" id="stock-name" required placeholder="Contoh: BBCA" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Harga Perolehan (Rp)</label>
<input type="number" id="entry-price" required class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Konfirmasi Entry</label>
<select id="entry-confirm" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="Terkonfirmasi">Terkonfirmasi</option>
<option value="Pending">Pending</option>
<option value="Batal">Batal</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Gain Saat Ini (%)</label>
<input type="number" id="gain" placeholder="Contoh: 12.5" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"/>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-1">Analisis Fundamental</label>
<textarea id="fundamental" rows="3" placeholder="Kualitas manajemen, margin laba, rasio P/E, dsb." class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"></textarea>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-1">Sentimen Pasar</label>
<textarea id="sentiment" rows="3" placeholder="Berita makroekonomi, sentimen investor, analisis media sosial, dsb." class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"></textarea>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-1">Analisis Teknikal</label>
<textarea id="technical" rows="3" placeholder="Polusi chart, RSI, MACD, volume, level support/resistance, dsb." class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"></textarea>
</div>
<div class="md:col-span-2 flex justify-end">
<button type="submit" class="bg-gradient-primary text-white px-6 py-3 rounded-lg shadow hover:shadow-lg transform hover:scale-105 transition">Simpan Rekaman</button>
</div>
</form>
</div>
</div>
<!-- History Tab -->
<div id="history-content" class="tab-content hidden">
<div class="bg-white p-6 rounded-xl shadow mb-6 flex justify-between items-center">
<h2 class="text-2xl font-bold text-gray-800">Riwayat Trading</h2>
<button id="export-btn" class="bg-green-600 text-white px-4 py-2 rounded text-sm hover:bg-green-700 flex items-center">
<i class="fas fa-file-export mr-2"></i> Ekspor ke CSV
</button>
</div>
<div class="bg-white rounded-xl shadow overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Saham</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Entry</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Fundamental</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Sentimen</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Teknikal</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Gain</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th>
</tr>
</thead>
<tbody id="history-body" class="bg-white divide-y divide-gray-200">
<!-- Dynamic rows will be inserted here -->
<tr>
<td colspan="8" class="text-center py-8 text-gray-500">Belum ada data</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Toast Notification -->
<div id="toast" class="hidden fixed bottom-5 right-5 bg-gray-800 text-white px-6 py-3 rounded-lg shadow-lg flex items-center z-50">
<i class="fas fa-check-circle mr-2"></i>
<span id="toast-message">Rekaman berhasil disimpan!</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/flowbite@2.3.0/dist/flowbite.min.js"></script>
<script>
// Tab Navigation
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('active-tab'));
tabContents.forEach(c => c.classList.add('hidden'));
tab.classList.add('active-tab');
document.getElementById(`${tab.id.replace('-tab', '-content')}`).classList.remove('hidden');
});
});
// Local Storage
const saveToStorage = (key, data) => localStorage.setItem(key, JSON.stringify(data));
const getFromStorage = (key) => JSON.parse(localStorage.getItem(key)) || [];
let trades = getFromStorage('tradingRecords');
// Initialize Chart
const ctx = document.getElementById('portfolioChart').getContext('2d');
let portfolioChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Nilai Portofolio (Rp)',
data: [],
borderColor: '#3b82f6',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false }
}
}
});
// Update Dashboard Stats
const updateDashboard = () => {
const totalInvestment = trades.reduce((sum, t) => sum + parseFloat(t.entryPrice || 0), 0);
const avgGain = trades.length > 0 ? (trades.reduce((sum, t) => sum + parseFloat(t.gain || 0), 0) / trades.length).toFixed(2) : 0;
const totalRecords = trades.length;
const profitable = trades.filter(t => parseFloat(t.gain) > 0).length;
const winRate = totalRecords > 0 ? ((profitable / totalRecords) * 100).toFixed(1) : 0;
document.getElementById('total-investment').textContent = `Rp ${totalInvestment.toLocaleString()}`;
document.getElementById('avg-gain').textContent = `${avgGain}%`;
document.getElementById('total-records').textContent = totalRecords;
document.getElementById('win-rate').textContent = `${winRate}%`;
// Update Chart
portfolioChart.data.labels = trades.map(t => t.stockName);
portfolioChart.data.datasets[0].data = trades.map(t => parseFloat(t.entryPrice) * (1 + parseFloat(t.gain) / 100));
portfolioChart.update();
};
// Show Toast
const showToast = (message) => {
const toast = document.getElementById('toast');
document.getElementById('toast-message').textContent = message;
toast.classList.remove('hidden');
setTimeout(() => toast.classList.add('hidden'), 3000);
};
// Submit Form
document.getElementById('trade-form').addEventListener('submit', function(e) {
e.preventDefault();
const newTrade = {
id: Date.now(),
stockName: document.getElementById('stock-name').value,
entryConfirm: document.getElementById('entry-confirm').value,
fundamental: document.getElementById('fundamental').value,
sentiment: document.getElementById('sentiment').value,
technical: document.getElementById('technical').value,
entryPrice: document.getElementById('entry-price').value,
gain: document.getElementById('gain').value || 0,
date: new Date().toLocaleDateString('id-ID')
};
trades.push(newTrade);
saveToStorage('tradingRecords', trades);
this.reset();
showToast('Rekaman berhasil disimpan!');
// Refresh UI
renderHistory();
updateDashboard();
// Switch to history tab
document.getElementById('history-tab').click();
});
// Render History Table
const renderHistory = () => {
const tbody = document.getElementById('history-body');
if (trades.length === 0) {
tbody.innerHTML = `<tr><td colspan="8" class="text-center py-8 text-gray-500">Belum ada data</td></tr>`;
return;
}
tbody.innerHTML = '';
trades.slice().reverse().forEach(t => {
const row = document.createElement('tr');
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap font-medium text-gray-900">${t.stockName}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${t.entryConfirm}</td>
<td class="px-6 py-4 text-sm text-gray-500 max-w-xs truncate" title="${t.fundamental}">${t.fundamental || '-'}</td>
<td class="px-6 py-4 text-sm text-gray-500 max-w-xs truncate" title="${t.sentiment}">${t.sentiment || '-'}</td>
<td class="px-6 py-4 text-sm text-gray-500 max-w-xs truncate" title="${t.technical}">${t.technical || '-'}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Rp ${parseFloat(t.entryPrice).toLocaleString()}</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${parseFloat(t.gain) >= 0 ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}">
${t.gain}%
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<button onclick="deleteTrade(${t.id})" class="text-red-600 hover:text-red-900 mr-3"><i class="fas fa-trash"></i></button>
</td>
`;
tbody.appendChild(row);
});
};
// Delete Trade
window.deleteTrade = (id) => {
if (confirm('Hapus rekaman ini?')) {
trades = trades.filter(t => t.id !== id);
saveToStorage('tradingRecords', trades);
renderHistory();
updateDashboard();
showToast('Rekaman dihapus');
}
};
// Export to CSV
document.getElementById('export-btn').addEventListener('click', () => {
const headers = ['Nama Saham', 'Konfirmasi Entry', 'Fundamental', 'Sentimen', 'Teknikal', 'Harga Perolehan', 'Gain (%)', 'Tanggal'];
const csvContent = [
headers.join(','),
...trades.map(t => [
t.stockName,
t.entryConfirm,
`"${t.fundamental.replace(/"/g, '""')}"`,
`"${t.sentiment.replace(/"/g, '""')}"`,
`"${t.technical.replace(/"/g, '""')}"`,
t.entryPrice,
t.gain,
t.date
].join(','))
].join('\n');
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', `trading-record-${new Date().toISOString().slice(0,10)}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showToast('Data diekspor ke CSV');
});
// Simulasi Rekomendasi Berita dari API (mock)
const fetchNewsRecommendations = () => {
const newsContainer = document.getElementById('news-container');
newsContainer.innerHTML = '';
const mockNews = [
{ title: 'Saham Teknologi Diprediksi Melonjak Setelah Laporan Kuartal', source: 'CNBC Indonesia', time: '1 jam lalu', link: '#' },
{ title: 'Analisis: Potensi Saham Batubara di Tengah Lonjakan Harga Komoditas Global', source: 'Investing.com', time: '3 jam lalu', link: '#' },
{ title: 'Rekomendasi Minggu Ini: 5 Saham dengan Dividen Tinggi untuk Portofolio Stabil', source: 'Bisnis.com', time: '5 jam lalu', link: '#' },
];
mockNews.forEach(news => {
const newsCard = document.createElement('div');
newsCard.className = 'news-card p-4 rounded-lg shadow-sm flex';
newsCard.innerHTML = `
<img src="https://via.placeholder.com/60/3b82f6/FFFFFF?text=${news.title.charAt(0)}" alt="News" class="rounded mr-3 object-cover">
<div>
<h4 class="font-medium text-gray-800">${news.title}</h4>
<p class="text-sm text-gray-600">${news.source} - ${news.time}</p>
<a href="${news.link}" class="text-blue-600 text-xs mt-1 block hover:underline">Baca selengkapnya</a>
</div>
`;
newsContainer.appendChild(newsCard);
});
};
// Initial Load
renderHistory();
updateDashboard();
fetchNewsRecommendations();
// Recommended Stocks based on current trades (simple logic)
const recommendStocks = () => {
// In real app: use AI or sentiment analysis
const sectorCount = {};
trades.forEach(t => {
const sector = t.stockName.substring(0,2); // mock: first 2 letters as sector
sectorCount[sector] = (sectorCount[sector] || 0) + 1;
});
const topSector = Object.keys(sectorCount).reduce((a, b) => sectorCount[a] > sectorCount[b] ? a : b, "");
localStorage.setItem('recommendedSector', topSector);
};
recommendStocks();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-qwensite.hf.space/logo.svg" alt="qwensite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-qwensite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >QwenSite</a> - 🧬 <a href="https://enzostvs-qwensite.hf.space?remix=alterzick/trading-record" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>