F-Play's picture
Initial DeepSite commit
349c469 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Personal Trading Session Tracker</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<style>
:root {
--bg-color: #0f172a;
--card-bg: #1e293b;
--input-bg: #334155;
--text-color: #f1f5f9;
--border-color: #475569;
--win-color: #10b981;
--loss-color: #ef4444;
--accent-color: #3b82f6;
}
.light-mode {
--bg-color: #f8fafc;
--card-bg: #ffffff;
--input-bg: #f1f5f9;
--text-color: #1e293b;
--border-color: #cbd5e1;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
transition: all 0.3s ease;
}
.trade-card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
transition: all 0.2s;
}
.input-field {
background-color: var(--input-bg);
color: var(--text-color);
border: 1px solid var(--border-color);
}
.input-field:focus {
border-color: var(--accent-color);
outline: none;
ring: 2px solid var(--accent-color);
}
.btn-win {
background-color: var(--win-color);
color: white;
}
.btn-win:hover {
background-color: #059669;
}
.btn-loss {
background-color: var(--loss-color);
color: white;
}
.btn-loss:hover {
background-color: #dc2626;
}
.btn-action {
background-color: var(--accent-color);
color: white;
}
.btn-action:hover {
background-color: #2563eb;
}
.stat-box {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background-color: var(--input-bg);
font-weight: 600;
}
tr:hover {
background-color: rgba(255,255,255,0.05);
}
.win-row {
color: var(--win-color);
}
.loss-row {
color: var(--loss-color);
}
.currency-input {
position: relative;
}
.currency-input::before {
content: '$';
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #94a3b8;
}
.currency-input input {
padding-left: 28px;
}
</style>
</head>
<body class="min-h-screen p-4 md:p-8">
<div class="max-w-6xl mx-auto">
<!-- Header -->
<div class="flex justify-between items-center mb-8">
<div class="flex items-center gap-3">
<div class="w-12 h-12 bg-blue-600 rounded-lg flex items-center justify-center">
<i data-lucide="trending-up" class="text-white w-6 h-6"></i>
</div>
<div>
<h1 class="text-2xl font-bold">Trading Session Tracker</h1>
<p class="text-sm text-gray-400">Personal Trading Calculator</p>
</div>
</div>
<div class="flex gap-3">
<button onclick="exportData()" class="px-4 py-2 bg-slate-700 hover:bg-slate-600 rounded-lg flex items-center gap-2 text-sm transition-colors">
<i data-lucide="download" class="w-4 h-4"></i>
Export Data
</button>
<label class="px-4 py-2 bg-slate-700 hover:bg-slate-600 rounded-lg flex items-center gap-2 text-sm transition-colors cursor-pointer">
<i data-lucide="upload" class="w-4 h-4"></i>
Import Data
<input type="file" id="importFile" accept=".json" class="hidden" onchange="importData(this)">
</label>
<button onclick="toggleTheme()" class="w-10 h-10 bg-slate-700 hover:bg-slate-600 rounded-lg flex items-center justify-center transition-colors">
<i data-lucide="sun" class="w-5 h-5" id="themeIcon"></i>
</button>
</div>
</div>
<!-- Main Grid -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
<!-- Left Column: Controls (C6, C8, C9, C10, C14 equivalent) -->
<div class="lg:col-span-4 space-y-6">
<!-- Starting Capital (C6) -->
<div class="trade-card rounded-xl p-6">
<label class="block text-sm font-medium text-gray-400 mb-2">Starting Capital</label>
<div class="currency-input">
<input type="number" id="startingCapital" class="input-field w-full rounded-lg px-4 py-3 text-xl font-bold"
value="10000" step="0.01" onchange="updateState()">
</div>
<p class="text-xs text-gray-500 mt-2">Updates automatically when starting new session</p>
</div>
<!-- Counters (C8, C9, C10) -->
<div class="trade-card rounded-xl p-6">
<h3 class="font-semibold mb-4 flex items-center gap-2">
<i data-lucide="bar-chart-2" class="w-5 h-5 text-blue-400"></i>
Session Counters
</h3>
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block text-xs text-gray-400 mb-1">Wins</label>
<input type="number" id="winCount" class="input-field w-full rounded-lg px-3 py-2 font-bold text-emerald-400"
value="0" readonly>
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Losses</label>
<input type="number" id="lossCount" class="input-field w-full rounded-lg px-3 py-2 font-bold text-red-400"
value="0" readonly>
</div>
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Total Trades</label>
<input type="number" id="totalCount" class="input-field w-full rounded-lg px-3 py-2 font-bold text-blue-400"
value="0" readonly>
</div>
</div>
<!-- Current Trade PNL (C14) -->
<div class="trade-card rounded-xl p-6 border-blue-500/30 border-2">
<label class="block text-sm font-medium text-blue-400 mb-2">Current Trade PNL</label>
<div class="currency-input">
<input type="number" id="currentTradePnl" class="input-field w-full rounded-lg px-4 py-3 text-xl font-bold"
placeholder="0.00" step="0.01">
</div>
<p class="text-xs text-gray-500 mt-2">Enter profit/loss for this trade, then click Win or Loss</p>
</div>
<!-- Session PNL Display -->
<div class="trade-card rounded-xl p-6 bg-gradient-to-br from-slate-800 to-slate-900">
<label class="block text-sm font-medium text-gray-400 mb-2">Current Session PNL</label>
<div class="text-3xl font-bold" id="sessionPnlDisplay">$0.00</div>
<div class="text-xs text-gray-500 mt-1">Running total for today's session</div>
</div>
<!-- Action Buttons -->
<div class="grid grid-cols-2 gap-3">
<button onclick="addWin()" class="btn-win py-4 rounded-xl font-bold text-lg flex items-center justify-center gap-2 shadow-lg transform active:scale-95 transition-all">
<i data-lucide="plus-circle" class="w-5 h-5"></i>
Add Win
</button>
<button onclick="addLoss()" class="btn-loss py-4 rounded-xl font-bold text-lg flex items-center justify-center gap-2 shadow-lg transform active:scale-95 transition-all">
<i data-lucide="minus-circle" class="w-5 h-5"></i>
Add Loss
</button>
</div>
<div class="grid grid-cols-2 gap-3">
<button onclick="startSession()" class="btn-action py-3 rounded-xl font-semibold flex items-center justify-center gap-2 opacity-80 hover:opacity-100 transition-all">
<i data-lucide="play" class="w-4 h-4"></i>
Start Session
</button>
<button onclick="endSession()" class="bg-amber-600 hover:bg-amber-700 text-white py-3 rounded-xl font-semibold flex items-center justify-center gap-2 transition-all">
<i data-lucide="flag" class="w-4 h-4"></i>
End Session
</button>
</div>
<button onclick="resetAll()" class="w-full py-2 bg-red-900/30 hover:bg-red-900/50 text-red-400 rounded-lg text-sm transition-colors border border-red-800/30">
<i data-lucide="trash-2" class="inline w-4 h-4 mr-2"></i>
Reset All Data
</button>
</div>
<!-- Right Column: Data Tables -->
<div class="lg:col-span-8 space-y-6">
<!-- Current Session Trades (DATA Sheet) -->
<div class="trade-card rounded-xl p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold flex items-center gap-2">
<i data-lucide="list" class="w-5 h-5 text-blue-400"></i>
Session Trade Log
</h3>
<span class="text-xs text-gray-500" id="tradeCountBadge">0 trades</span>
</div>
<div class="overflow-x-auto max-h-96 overflow-y-auto">
<table id="tradeTable">
<thead class="sticky top-0">
<tr>
<th>Trade #</th>
<th>Result</th>
<th>Timestamp</th>
<th>Profit/Loss</th>
<th>Action</th>
</tr>
</thead>
<tbody id="tradeTableBody">
<tr>
<td colspan="5" class="text-center py-8 text-gray-500 italic">
No trades recorded. Start by entering PNL and clicking Win or Loss.
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Daily PNL History (Column L) -->
<div class="trade-card rounded-xl p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold flex items-center gap-2">
<i data-lucide="calendar" class="w-5 h-5 text-amber-400"></i>
Daily PNL History
</h3>
<span class="text-xs text-gray-500">Last 14 sessions</span>
</div>
<div class="overflow-x-auto max-h-64 overflow-y-auto">
<table id="historyTable">
<thead class="sticky top-0">
<tr>
<th>Session Date</th>
<th>Daily PNL</th>
<th>Cumulative Capital</th>
</tr>
</thead>
<tbody id="historyTableBody">
<tr>
<td colspan="3" class="text-center py-8 text-gray-500 italic">
No session history. Click "End Session" to save daily PNL.
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Instructions -->
<div class="trade-card rounded-xl p-6 bg-blue-900/20 border-blue-800/30">
<h4 class="font-semibold text-blue-300 mb-3 flex items-center gap-2">
<i data-lucide="info" class="w-5 h-5"></i>
How to Use (Matching Google Sheets Workflow)
</h4>
<ol class="text-sm text-blue-200 space-y-2 list-decimal list-inside">
<li><strong>Start Session:</strong> Click to clear trade log and add previous day's PNL to Starting Capital</li>
<li><strong>Enter Trade PNL:</strong> Input the profit or loss amount in the Current Trade PNL field</li>
<li><strong>Record Trade:</strong> Click "Add Win" or "Add Loss" to log the trade with timestamp</li>
<li><strong>End Session:</strong> Click to save today's total PNL to the Daily History</li>
<li><strong>Next Day:</strong> Click Start Session again to roll over the capital and reset counters</li>
</ol>
</div>
</div>
</div>
</div>
<script>
// Initialize Lucide icons
lucide.createIcons();
// State Management
let appState = {
startingCapital: 10000,
dailyHistory: [], // Array of {date, pnl, id}
currentSession: {
trades: [], // Array of {tradeNumber, result, timestamp, profit, id}
nextTradeNumber: 1
},
isDarkMode: true
};
// Load from LocalStorage on startup
function loadState() {
const saved = localStorage.getItem('tradingSessionData');
if (saved) {
appState = JSON.parse(saved);
document.getElementById('startingCapital').value = appState.startingCapital || 10000;
updateUI();
}
}
// Save to LocalStorage
function saveState() {
localStorage.setItem('tradingSessionData', JSON.stringify(appState));
}
// Update all UI elements
function updateUI() {
// Update counters (C8, C9, C10)
const wins = appState.currentSession.trades.filter(t => t.result === 'Win').length;
const losses = appState.currentSession.trades.filter(t => t.result === 'Loss').length;
const total = appState.currentSession.trades.length;
document.getElementById('winCount').value = wins;
document.getElementById('lossCount').value = losses;
document.getElementById('totalCount').value = total;
// Calculate Session PNL
const sessionPnl = appState.currentSession.trades.reduce((sum, t) => sum + parseFloat(t.profit), 0);
const pnlElement = document.getElementById('sessionPnlDisplay');
pnlElement.textContent = formatCurrency(sessionPnl);
pnlElement.className = sessionPnl >= 0 ? 'text-3xl font-bold text-emerald-400' : 'text-3xl font-bold text-red-400';
// Update trade table (DATA sheet)
updateTradeTable();
// Update history table (Column L)
updateHistoryTable();
// Update badge
document.getElementById('tradeCountBadge').textContent = `${total} trade${total !== 1 ? 's' : ''}`;
}
function updateTradeTable() {
const tbody = document.getElementById('tradeTableBody');
if (appState.currentSession.trades.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="5" class="text-center py-8 text-gray-500 italic">
No trades recorded. Start by entering PNL and clicking Win or Loss.
</td>
</tr>`;
return;
}
tbody.innerHTML = appState.currentSession.trades.slice().reverse().map(trade => `
<tr class="${trade.result === 'Win' ? 'win-row' : 'loss-row'}">
<td class="font-mono">${trade.tradeNumber}</td>
<td>
<span class="px-2 py-1 rounded text-xs font-bold ${trade.result === 'Win' ? 'bg-emerald-500/20 text-emerald-400' : 'bg-red-500/20 text-red-400'}">
${trade.result}
</span>
</td>
<td class="text-sm">${new Date(trade.timestamp).toLocaleString()}</td>
<td class="font-bold">${formatCurrency(trade.profit)}</td>
<td>
<button onclick="deleteTrade('${trade.id}')" class="text-gray-500 hover:text-red-400 transition-colors">
<i data-lucide="trash-2" class="w-4 h-4"></i>
</button>
</td>
</tr>
`).join('');
lucide.createIcons();
}
function updateHistoryTable() {
const tbody = document.getElementById('historyTableBody');
if (appState.dailyHistory.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="3" class="text-center py-8 text-gray-500 italic">
No session history. Click "End Session" to save daily PNL.
</td>
</tr>`;
return;
}
let cumulative = appState.startingCapital;
tbody.innerHTML = appState.dailyHistory.slice().reverse().map((entry, index, arr) => {
// Calculate cumulative backwards for display
const displayCumulative = arr.slice(index).reduce((sum, e) => sum - e.pnl, appState.startingCapital + appState.dailyHistory.reduce((s, e) => s + e.pnl, 0));
return `
<tr>
<td>${new Date(entry.date).toLocaleDateString()}</td>
<td class="font-bold ${entry.pnl >= 0 ? 'text-emerald-400' : 'text-red-400'}">
${formatCurrency(entry.pnl)}
</td>
<td class="text-gray-400">
${formatCurrency(appState.startingCapital + appState.dailyHistory.slice(0, appState.dailyHistory.indexOf(entry) + 1).reduce((s, e) => s + e.pnl, 0))}
</td>
</tr>
`}).join('');
}
// Core Functions matching Google Sheets logic
function addWin() {
const pnl = parseFloat(document.getElementById('currentTradePnl').value) || 0;
const trade = {
id: Date.now().toString(),
tradeNumber: appState.currentSession.nextTradeNumber++,
result: 'Win',
timestamp: new Date().toISOString(),
profit: pnl
};
appState.currentSession.trades.push(trade);
saveState();
updateUI();
// Clear input after adding
document.getElementById('currentTradePnl').value = '';
document.getElementById('currentTradePnl').focus();
}
function addLoss() {
const pnl = parseFloat(document.getElementById('currentTradePnl').value) || 0;
const trade = {
id: Date.now().toString(),
tradeNumber: appState.currentSession.nextTradeNumber++,
result: 'Loss',
timestamp: new Date().toISOString(),
profit: pnl // Usually negative for losses, but we record what user enters
};
appState.currentSession.trades.push(trade);
saveState();
updateUI();
// Clear input after adding
document.getElementById('currentTradePnl').value = '';
document.getElementById('currentTradePnl').focus();
}
function startSession() {
// 1. Get last PNL from daily history (equivalent to reading column L)
let lastPnl = 0;
if (appState.dailyHistory.length > 0) {
lastPnl = appState.dailyHistory[appState.dailyHistory.length - 1].pnl;
}
// 2. Add to starting capital (C6)
appState.startingCapital += lastPnl;
document.getElementById('startingCapital').value = appState.startingCapital;
// 3. Clear trade log (DATA sheet)
appState.currentSession.trades = [];
appState.currentSession.nextTradeNumber = 1;
// 4. Reset counters (C8, C9, C10)
// Already handled by clearing trades array
saveState();
updateUI();
alert(`Session started!\nPrevious PNL (${formatCurrency(lastPnl)}) added to Starting Capital.\nNew Capital: ${formatCurrency(appState.startingCapital)}`);
}
function endSession() {
// Calculate current session PNL (equivalent to C14)
const sessionPnl = appState.currentSession.trades.reduce((sum, t) => sum + parseFloat(t.profit), 0);
// Save to daily history (equivalent to saving in L11:L24)
appState.dailyHistory.push({
date: new Date().toISOString(),
pnl: sessionPnl,
id: Date.now().toString()
});
// Keep only last 14 entries (L11:L24 = 14 rows)
if (appState.dailyHistory.length > 14) {
appState.dailyHistory = appState.dailyHistory.slice(-14);
}
saveState();
updateUI();
alert(`Session ended!\nDaily PNL ${formatCurrency(sessionPnl)} saved to history.`);
}
function deleteTrade(id) {
if (!confirm('Delete this trade record?')) return;
appState.currentSession.trades = appState.currentSession.trades.filter(t => t.id !== id);
// Renumber trades
appState.currentSession.trades.forEach((t, i) => {
t.tradeNumber = i + 1;
});
appState.currentSession.nextTradeNumber = appState.currentSession.trades.length + 1;
saveState();
updateUI();
}
function updateState() {
appState.startingCapital = parseFloat(document.getElementById('startingCapital').value) || 0;
saveState();
}
function formatCurrency(amount) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(amount);
}
function resetAll() {
if (!confirm('WARNING: This will delete ALL data including trade history and daily PNL records. This cannot be undone. Continue?')) return;
appState = {
startingCapital: 10000,
dailyHistory: [],
currentSession: {
trades: [],
nextTradeNumber: 1
},
isDarkMode: appState.isDarkMode
};
localStorage.removeItem('tradingSessionData');
document.getElementById('startingCapital').value = 10000;
document.getElementById('currentTradePnl').value = '';
updateUI();
}
function exportData() {
const dataStr = JSON.stringify(appState, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `trading-session-data-${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function importData(input) {
const file = input.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const imported = JSON.parse(e.target.result);
if (confirm(`Import ${imported.currentSession?.trades?.length || 0} trades and ${imported.dailyHistory?.length || 0} daily records?`)) {
appState = imported;
document.getElementById('startingCapital').value = appState.startingCapital || 10000;
saveState();
updateUI();
alert('Data imported successfully!');
}
} catch (err) {
alert('Error importing file. Please ensure it is a valid JSON export from this tool.');
}
};
reader.readAsText(file);
input.value = '';
}
function toggleTheme() {
appState.isDarkMode = !appState.isDarkMode;
document.body.classList.toggle('light-mode');
const icon = document.getElementById('themeIcon');
if (appState.isDarkMode) {
icon.setAttribute('data-lucide', 'sun');
} else {
icon.setAttribute('data-lucide', 'moon');
}
lucide.createIcons();
saveState();
}
// Initialize
loadState();
// Check for light mode preference
if (!appState.isDarkMode) {
document.body.classList.add('light-mode');
document.getElementById('themeIcon').setAttribute('data-lucide', 'moon');
lucide.createIcons();
}
</script>
<script src="https://deepsite.hf.co/deepsite-badge.js"></script>
</body>
</html>