/home/devscfvi/crypto.devsquantum.com/js/calculator.js
// ============================================
// TRADE CALCULATOR MODULE
// ============================================
// Calculator State
const calculatorState = {
positionSize: '',
entryPrice: '',
stopLossPercent: '0.5',
takeProfitPercent: '1.5',
slPrice: '',
tpPrice: '',
coin: '',
orderType: 'market',
category: '',
emotion: '',
disciplineScore: '',
mistakeType: '',
preTradePlan: '',
tradeComment: '',
showCoinInput: false,
entryScreenshot: null,
results: null
};
// Render Calculator View
function renderCalculator() {
const budget = state.user.budget || 0;
return `
<div class="min-h-screen p-4">
<div class="max-w-lg mx-auto">
<!-- Header -->
<div class="mb-6 pt-4">
<h1 class="text-2xl font-bold text-white mb-1">Trade Calculator</h1>
<p class="text-sm text-violet-300">Plan your trade with precision</p>
${budget > 0 ? `
<div class="mt-2 inline-flex items-center gap-2 px-3 py-1 bg-violet-500/20 rounded-full text-sm">
<span class="text-violet-300">Budget:</span>
<span class="font-bold text-white">${formatCurrency(budget)}</span>
</div>
` : ''}
</div>
<!-- Main Calculator Card -->
<div class="glass rounded-3xl p-6 mb-6 border border-violet-500/20 shadow-2xl">
<div class="space-y-4">
<!-- Coin Selection -->
<div>
<label class="block text-sm font-medium text-violet-200 mb-2">Coin</label>
${!calculatorState.showCoinInput ? `
<div class="relative">
<select onchange="handleCoinSelect(this.value)"
class="w-full px-4 py-3.5 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white text-lg focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20 appearance-none cursor-pointer">
<option value="">Select Coin</option>
${state.user.favoriteCoins.map(c => `
<option value="${c}" ${calculatorState.coin === c ? 'selected' : ''}>${c}</option>
`).join('')}
<option value="__custom__">+ Add Custom Coin</option>
</select>
<div class="absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none">
<svg class="w-5 h-5 text-violet-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</div>
</div>
${calculatorState.coin ? `
<div class="mt-2 flex items-center justify-between">
<span class="text-sm text-violet-400">Selected: <strong>${calculatorState.coin}</strong></span>
<button onclick="clearCoin()" class="text-sm text-red-400 hover:text-red-300">Clear</button>
</div>
` : ''}
` : `
<div class="flex gap-2">
<input type="text"
value="${calculatorState.coin}"
oninput="calculatorState.coin = this.value.toUpperCase();"
class="flex-1 px-4 py-3.5 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white text-lg placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
placeholder="Enter coin symbol"
autofocus>
<button onclick="calculatorState.showCoinInput = false; render();"
class="px-4 bg-violet-600 hover:bg-violet-700 text-white rounded-xl active:scale-95 transition">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
`}
</div>
<!-- Order Type -->
<div>
<label class="block text-sm font-medium text-violet-200 mb-2">Order Type</label>
<div class="grid grid-cols-2 gap-3">
<button onclick="calculatorState.orderType = 'market'; render();"
class="py-3 rounded-xl font-semibold transition ${calculatorState.orderType === 'market' ? 'bg-violet-600 text-white' : 'bg-slate-900/50 text-slate-400 border border-violet-500/30'}">
Market
</button>
<button onclick="calculatorState.orderType = 'limit'; render();"
class="py-3 rounded-xl font-semibold transition ${calculatorState.orderType === 'limit' ? 'bg-violet-600 text-white' : 'bg-slate-900/50 text-slate-400 border border-violet-500/30'}">
Limit
</button>
</div>
</div>
<!-- Position Size & Entry Price -->
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-violet-200 mb-2">Position ($)</label>
<input type="number"
inputmode="decimal"
value="${calculatorState.positionSize}"
oninput="calculatorState.positionSize = this.value;"
class="w-full px-4 py-3.5 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white text-lg placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
placeholder="1000">
</div>
<div>
<label class="block text-sm font-medium text-violet-200 mb-2">Entry ($)</label>
<input type="number"
inputmode="decimal"
value="${calculatorState.entryPrice}"
oninput="calculatorState.entryPrice = this.value;"
class="w-full px-4 py-3.5 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white text-lg placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
placeholder="50000">
</div>
</div>
<!-- Stop Loss & Take Profit -->
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-red-300 mb-2">Stop Loss %</label>
<input type="number"
inputmode="decimal"
step="0.1"
value="${calculatorState.stopLossPercent}"
oninput="calculatorState.stopLossPercent = this.value;"
class="w-full px-4 py-3.5 bg-slate-900/50 border border-red-500/30 rounded-xl text-white text-lg placeholder-slate-500 focus:border-red-400 focus:ring-2 focus:ring-red-400/20"
placeholder="0.5">
</div>
<div>
<label class="block text-sm font-medium text-green-300 mb-2">Take Profit %</label>
<input type="number"
inputmode="decimal"
step="0.1"
value="${calculatorState.takeProfitPercent}"
oninput="calculatorState.takeProfitPercent = this.value;"
class="w-full px-4 py-3.5 bg-slate-900/50 border border-green-500/30 rounded-xl text-white text-lg placeholder-slate-500 focus:border-green-400 focus:ring-2 focus:ring-green-400/20"
placeholder="1.5">
</div>
</div>
<!-- Calculate Button -->
<button onclick="calculateTrade()"
class="w-full bg-violet-600 hover:bg-violet-700 text-white py-4 rounded-2xl font-bold text-lg shadow-lg active:scale-95 transition">
Calculate Trade
</button>
</div>
</div>
<!-- Results Container -->
<div id="calculator-results"></div>
</div>
</div>
`;
}
// Handle coin selection
function handleCoinSelect(value) {
if (value === '__custom__') {
calculatorState.showCoinInput = true;
} else {
calculatorState.coin = value;
}
render();
}
// Clear coin selection
function clearCoin() {
calculatorState.coin = '';
render();
}
// Calculate Trade
function calculateTrade() {
const metrics = calculateTradeMetrics(
calculatorState.positionSize,
calculatorState.entryPrice,
calculatorState.stopLossPercent,
calculatorState.takeProfitPercent
);
if (!metrics) {
showToast('Please enter position size and entry price', 'error');
return;
}
calculatorState.results = metrics;
calculatorState.slPrice = metrics.slPrice;
calculatorState.tpPrice = metrics.tpPrice;
renderCalculatorResults();
}
// Render Calculator Results
function renderCalculatorResults() {
const container = document.getElementById('calculator-results');
if (!container) return;
if (!calculatorState.results) {
container.innerHTML = '';
return;
}
const r = calculatorState.results;
container.innerHTML = `
<div class="space-y-4 slide-up">
<!-- Price Results -->
<div class="grid grid-cols-3 gap-3">
<div class="glass rounded-2xl p-4 border border-violet-500/20">
<p class="text-xs text-violet-300 mb-1">Quantity</p>
<p class="text-lg font-bold text-white">${parseFloat(r.quantity).toFixed(4)}</p>
</div>
<div class="glass rounded-2xl p-4 border border-red-500/20">
<p class="text-xs text-red-300 mb-1">SL Price</p>
<p class="text-lg font-bold text-white">$${r.slPrice}</p>
</div>
<div class="glass rounded-2xl p-4 border border-green-500/20">
<p class="text-xs text-green-300 mb-1">TP Price</p>
<p class="text-lg font-bold text-white">$${r.tpPrice}</p>
</div>
</div>
<!-- P&L Card -->
<div class="glass rounded-3xl p-6 border border-violet-500/20">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-white">Profit & Loss</h3>
<span class="px-3 py-1 bg-violet-500/20 rounded-full text-sm font-medium text-violet-300">
R:R 1:${r.rewardRiskRatio}
</span>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div class="bg-red-500/10 rounded-2xl p-4 border border-red-500/20">
<p class="text-xs text-red-300 mb-2">Max Loss</p>
<p class="text-3xl font-bold text-red-400">-$${r.lossAmount}</p>
<p class="text-sm text-red-300/70 mt-1">-${calculatorState.stopLossPercent}%</p>
</div>
<div class="bg-green-500/10 rounded-2xl p-4 border border-green-500/20">
<p class="text-xs text-green-300 mb-2">Max Profit</p>
<p class="text-3xl font-bold text-green-400">+$${r.profitAmount}</p>
<p class="text-sm text-green-300/70 mt-1">+${calculatorState.takeProfitPercent}%</p>
</div>
</div>
<div class="bg-slate-900/50 rounded-xl p-3 text-center">
<p class="text-xs text-slate-400">Total Fees (Buy + Sell)</p>
<p class="text-lg font-bold text-yellow-400">$${r.fees}</p>
</div>
</div>
<!-- Enhanced Trade Details (Expandable) -->
<button onclick="toggleTradeDetails()"
class="w-full glass rounded-2xl p-4 border border-violet-500/20 flex items-center justify-between active:scale-95 transition">
<span class="text-white font-semibold">Add Trade Details (Optional)</span>
<svg id="details-arrow" class="w-5 h-5 text-violet-400 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div id="trade-details" class="hidden space-y-4">
<!-- Category Selection -->
<div class="glass rounded-2xl p-4 border border-violet-500/20">
<label class="block text-sm font-medium text-violet-200 mb-2">Trade Category</label>
<select onchange="calculatorState.category = this.value"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20">
<option value="">Select category (optional)</option>
${CATEGORIES.map(c => `
<option value="${c.value}" ${calculatorState.category === c.value ? 'selected' : ''}>
${c.label}
</option>
`).join('')}
</select>
</div>
<!-- Emotion Before Trade -->
<div class="glass rounded-2xl p-4 border border-violet-500/20">
<label class="block text-sm font-medium text-violet-200 mb-2">Emotion Before Trade</label>
<div class="grid grid-cols-2 gap-2">
${EMOTIONS.map(e => `
<button onclick="calculatorState.emotion = '${e.value}'; renderCalculatorResults();"
class="py-2 px-3 rounded-lg text-sm font-medium transition ${
calculatorState.emotion === e.value
? 'bg-' + e.color + '-500/30 text-' + e.color + '-300 border-2 border-' + e.color + '-500/50'
: 'bg-slate-900/50 text-slate-400 border border-violet-500/20'
}">
${e.label}
</button>
`).join('')}
</div>
</div>
<!-- Pre-Trade Plan -->
<div class="glass rounded-2xl p-4 border border-violet-500/20">
<label class="block text-sm font-medium text-violet-200 mb-2">Pre-Trade Plan</label>
<textarea
oninput="calculatorState.preTradePlan = this.value"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
rows="3"
placeholder="Why are you taking this trade? What's your analysis?">${calculatorState.preTradePlan}</textarea>
</div>
<!-- General Comments -->
<div class="glass rounded-2xl p-4 border border-violet-500/20">
<label class="block text-sm font-medium text-violet-200 mb-2">Comments (Optional)</label>
<textarea
oninput="calculatorState.tradeComment = this.value"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
rows="2"
placeholder="Any additional notes...">${calculatorState.tradeComment}</textarea>
</div>
<!-- Entry Screenshot Upload -->
<div class="mt-6 p-4 bg-slate-900/30 rounded-xl border border-violet-500/20">
<label class="block text-sm font-medium text-violet-200 mb-3">
📸 Entry Screenshot (Optional)
</label>
<p class="text-xs text-slate-400 mb-3">Upload a screenshot of your chart at entry</p>
<input type="file"
id="entry-screenshot-input"
accept="image/*"
onchange="handleEntryScreenshot(this)"
class="hidden">
<div id="entry-screenshot-preview" class="hidden mb-3">
<img id="entry-screenshot-img"
class="w-full rounded-lg border-2 border-violet-500/30 mb-2"
alt="Entry Screenshot Preview">
<button onclick="clearEntryScreenshot()"
class="text-sm text-red-400 hover:text-red-300">
✕ Remove Screenshot
</button>
</div>
<button type="button"
onclick="document.getElementById('entry-screenshot-input').click()"
class="w-full bg-violet-600 hover:bg-violet-700 text-white py-3 rounded-xl font-semibold active:scale-95 transition">
<span id="screenshot-button-text">📷 Upload Screenshot</span>
</button>
</div>
</div>
<!-- Save Trade Button -->
<button onclick="saveCalculatedTrade()"
class="w-full gradient-bg text-white py-4 rounded-2xl font-bold text-lg shadow-lg active:scale-95 transition mt-6">
Save Trade
</button>
</div>
`;
}
// Toggle trade details section
function toggleTradeDetails() {
const details = document.getElementById('trade-details');
const arrow = document.getElementById('details-arrow');
if (details.classList.contains('hidden')) {
details.classList.remove('hidden');
details.classList.add('slide-up');
arrow.style.transform = 'rotate(180deg)';
} else {
details.classList.add('hidden');
arrow.style.transform = 'rotate(0deg)';
}
}
// Save calculated trade
async function saveCalculatedTrade() {
if (!calculatorState.results) {
showToast('Please calculate trade first', 'error');
return;
}
if (!calculatorState.coin) {
showToast('Please select a coin', 'error');
return;
}
const trade = {
coin: calculatorState.coin,
positionSize: calculatorState.positionSize,
entryPrice: calculatorState.entryPrice,
stopLossPercent: calculatorState.stopLossPercent,
takeProfitPercent: calculatorState.takeProfitPercent,
quantity: calculatorState.results.quantity,
slPrice: calculatorState.results.slPrice,
tpPrice: calculatorState.results.tpPrice,
lossAmount: calculatorState.results.lossAmount,
profitAmount: calculatorState.results.profitAmount,
rewardRiskRatio: calculatorState.results.rewardRiskRatio,
fees: calculatorState.results.fees,
orderType: calculatorState.orderType,
category: calculatorState.category || null,
emotion: calculatorState.emotion || null,
disciplineScore: calculatorState.disciplineScore || null,
mistakeType: calculatorState.mistakeType || null,
lessonLearned: null,
preTradePlan: calculatorState.preTradePlan || null,
entryScreenshot: calculatorState.entryScreenshot || null,
comment: calculatorState.tradeComment || null
};
console.log('=== SAVING TRADE ===');
console.log('calculatorState.entryScreenshot:', calculatorState.entryScreenshot);
console.log('trade.entryScreenshot:', trade.entryScreenshot);
console.log('Full trade object:', trade);
const result = await apiPost('saveTrade', { trade });
if (result.success) {
showToast('Trade saved successfully!', 'success');
// Reset calculator
Object.assign(calculatorState, {
positionSize: '',
entryPrice: '',
stopLossPercent: '0.5',
takeProfitPercent: '1.5',
coin: '',
category: '',
emotion: '',
preTradePlan: '',
tradeComment: '',
entryScreenshot: null,
results: null
});
// Refresh trades
await loadTrades();
// Re-render calculator
render();
} else {
showToast('Failed to save trade', 'error');
}
}
// Screenshot Handling Functions
async function handleEntryScreenshot(input) {
console.log('=== handleEntryScreenshot called ===');
if (input.files && input.files[0]) {
const file = input.files[0];
console.log('File selected:', file.name, 'Size:', file.size);
// Check file size (max 5MB)
if (file.size > 5 * 1024 * 1024) {
showToast('Image too large! Max 5MB', 'error');
return;
}
// Show preview immediately using FileReader
const reader = new FileReader();
reader.onload = function(e) {
console.log('Preview loaded');
const preview = document.getElementById('entry-screenshot-preview');
const img = document.getElementById('entry-screenshot-img');
if (preview && img) {
img.src = e.target.result;
preview.classList.remove('hidden');
console.log('Preview displayed');
} else {
console.error('Preview elements not found!');
}
};
reader.readAsDataURL(file);
// Upload file to server
const formData = new FormData();
formData.append('screenshot', file);
try {
console.log('Starting upload...');
showToast('Uploading screenshot...', 'info');
const response = await fetch('upload_screenshot.php', {
method: 'POST',
body: formData
});
console.log('Upload response status:', response.status);
const result = await response.json();
console.log('Upload result:', result);
if (result.success) {
// Store file path instead of base64
calculatorState.entryScreenshot = result.filepath;
console.log('✅ Screenshot uploaded successfully!');
console.log('📁 Filepath:', result.filepath);
console.log('💾 Stored in calculatorState:', calculatorState.entryScreenshot);
// Update button text
const buttonText = document.getElementById('screenshot-button-text');
if (buttonText) {
buttonText.textContent = '✓ Screenshot Uploaded';
console.log('Button text updated');
} else {
console.warn('Button text element not found');
}
showToast('Screenshot uploaded!', 'success');
} else {
console.error('❌ Upload failed:', result.error);
showToast('Upload failed: ' + result.error, 'error');
clearEntryScreenshot();
}
} catch (error) {
console.error('❌ Screenshot upload error:', error);
showToast('Screenshot upload error: ' + error.message, 'error');
clearEntryScreenshot();
}
} else {
console.log('No file selected');
}
}
function clearEntryScreenshot() {
calculatorState.entryScreenshot = null;
// Hide preview
const preview = document.getElementById('entry-screenshot-preview');
if (preview) {
preview.classList.add('hidden');
}
// Clear file input
const input = document.getElementById('entry-screenshot-input');
if (input) {
input.value = '';
}
// Reset button text
const buttonText = document.getElementById('screenshot-button-text');
if (buttonText) {
buttonText.textContent = '📷 Upload Screenshot';
}
showToast('Screenshot removed', 'success');
}
// Make functions global
window.handleCoinSelect = handleCoinSelect;
window.clearCoin = clearCoin;
window.calculateTrade = calculateTrade;
window.toggleTradeDetails = toggleTradeDetails;
window.saveCalculatedTrade = saveCalculatedTrade;
window.handleEntryScreenshot = handleEntryScreenshot;
window.clearEntryScreenshot = clearEntryScreenshot;
window.renderCalculator = renderCalculator;
window.calculatorState = calculatorState;