ECMQuantAI / static /script.js
AJAYKASU's picture
Logic Upgrade: Dynamic Target Raise + Percentage Pricing
88cde5b verified
async function runAnalysis() {
const query = document.getElementById('query').value;
const lastPrivate = document.getElementById('last-private').value;
// NEW STRUCTURING INPUTS
const ipoDiscount = document.getElementById('ipo-discount').value;
const greenshoe = document.getElementById('greenshoe').checked;
const primaryShares = document.getElementById('primary-shares').value;
const loader = document.getElementById('loader');
const dashboard = document.getElementById('dashboard');
// UI State: Loading
dashboard.style.display = 'none';
loader.style.display = 'block';
// Prepare form data
const formData = new FormData();
formData.append('query', query);
if (lastPrivate) formData.append('last_private', lastPrivate);
// Append Structuring Levers
formData.append('ipo_discount', ipoDiscount);
formData.append('greenshoe', greenshoe);
formData.append('primary_shares', primaryShares);
// NEW: Capture Target Raise
const targetRaise = document.getElementById('target-raise').value;
formData.append('target_raise', targetRaise);
try {
const response = await fetch('/analyze', {
method: 'POST',
body: formData
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Server Error (${response.status}): ${errorText}`);
}
const data = await response.json();
if (data.error) {
alert("Analysis failed: " + data.error);
return;
}
updateDashboard(data);
// UI State: Done
loader.style.display = 'none';
dashboard.style.display = 'flex'; // Flex layout
} catch (e) {
console.error(e);
alert("System Error: " + e.message);
loader.style.display = 'none';
}
}
function updateDashboard(data) {
// 1. Executive Commentary
if (data.advisory) {
document.getElementById('exec-summary-content').innerHTML = data.advisory.commentary;
document.getElementById('m-price').textContent = `$${data.advisory.low} - $${data.advisory.high}`;
const statusEl = document.getElementById('m-status');
statusEl.textContent = data.advisory.sentiment;
statusEl.style.color = data.advisory.color;
// --- METRICS ---
document.getElementById('m-momentum').textContent = (data.metrics.avg_momentum ? data.metrics.avg_momentum.toFixed(1) + "%" : "--");
document.getElementById('m-beta').textContent = (data.metrics.avg_beta ? data.metrics.avg_beta.toFixed(2) : "--");
document.getElementById('m-rule-40').textContent = (data.metrics.avg_rule_40 ? data.metrics.avg_rule_40.toFixed(0) : "--");
document.getElementById('m-vix').textContent = (data.macro ? data.macro.vix.toFixed(2) : "--");
// --- RISK CHECKLIST ---
const riskBody = document.querySelector('#risk-table tbody');
if (riskBody && data.advisory.risk_matrix) {
riskBody.innerHTML = '';
data.advisory.risk_matrix.forEach(r => {
const row = `
<tr style="border-bottom: 1px solid rgba(255,255,255,0.05);">
<td style="padding:8px 0; color:#ccc;">${r.factor}</td>
<td style="padding:8px 0;">${r.status}</td>
<td style="padding:8px 0; color:var(--text-muted); font-style:italic;">${r.impact}</td>
</tr>
`;
riskBody.innerHTML += row;
});
}
}
// 2. IPO STRUCTURING (The Pro Layer)
if (data.structure) {
document.getElementById('s-final-price').textContent = `$${data.structure.final_price.toFixed(2)}`;
document.getElementById('s-raise').textContent = `$${data.structure.capital_raised.toFixed(0)}M`;
// Down-Round Alert
const drAlert = document.getElementById('dr-alert');
if (data.down_round && data.down_round.is_active) {
drAlert.style.display = 'block';
drAlert.innerHTML = data.down_round.text;
} else {
drAlert.style.display = 'none';
}
// --- VISUALIZATION: PIE CHART & TABLE ---
const existingShares = data.structure.ownership["Existing Shareholders"];
const newShares = data.structure.ownership["New Public Investors"];
const totalShares = data.structure.total_shares;
// 1. Generate Table HTML
const tableHTML = `
<table style="width:100%; border-collapse:collapse; color:#eee;">
<thead>
<tr style="border-bottom:1px solid #444; color:#888;">
<th style="text-align:left; padding-bottom:5px;">Group</th>
<th style="text-align:right; padding-bottom:5px;">Shares (M)</th>
<th style="text-align:right; padding-bottom:5px;">% Own</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding:8px 0; color:#a0c4ff;">Existing</td>
<td style="text-align:right;">${existingShares.toFixed(2)}</td>
<td style="text-align:right;">${((existingShares / totalShares) * 100).toFixed(1)}%</td>
</tr>
<tr>
<td style="padding:8px 0; color:#FFD700;">New Investors</td>
<td style="text-align:right;">${newShares.toFixed(2)}</td>
<td style="text-align:right;">${((newShares / totalShares) * 100).toFixed(1)}%</td>
</tr>
<tr style="border-top:1px solid #444; font-weight:bold;">
<td style="padding-top:8px;">Total</td>
<td style="text-align:right; padding-top:8px;">${totalShares.toFixed(2)}</td>
<td style="text-align:right; padding-top:8px;">100%</td>
</tr>
</tbody>
</table>
`;
document.getElementById('ownership-table').innerHTML = tableHTML;
// 2. Render Pie Chart (Navy & Gold)
const ownerData = [{
values: [existingShares, newShares],
labels: ['Existing Holders', 'New Public Investors'],
type: 'pie',
marker: { colors: ['#001F3F', '#D4AF37'] }, // Navy vs Gold
textinfo: 'percent',
hoverinfo: 'label+value+percent',
hole: 0.6
}];
const ownerLayout = {
paper_bgcolor: 'rgba(0,0,0,0)',
plot_bgcolor: 'rgba(0,0,0,0)',
font: { color: '#ccc', family: 'Inter' },
showlegend: false,
margin: { t: 0, b: 0, l: 0, r: 0 },
height: 220
};
Plotly.newPlot('ownership-chart', ownerData, ownerLayout, { responsive: true, displayModeBar: false });
}
// 3. Main Chart
const layout = {
plot_bgcolor: '#0E1117',
paper_bgcolor: '#1E1E1E',
font: { color: '#FAFAFA' },
margin: { t: 20, r: 20, b: 40, l: 40 },
xaxis: { showgrid: false },
yaxis: { showgrid: true, gridcolor: '#333' },
height: 350,
showlegend: true,
legend: { orientation: 'h', y: 1.1 }
};
Plotly.newPlot('main-chart', data.chart_json, layout, { responsive: true });
// 4. Table
const tbody = document.querySelector('#comps-table tbody');
tbody.innerHTML = '';
if (data.comparables) {
data.comparables.forEach(c => {
const r40Color = c.rule_40 < 40 ? '#fa5c5c' : '#5cfa85';
const row = `
<tr>
<td style="font-weight:bold; color:var(--primary-gold)">${c.ticker}</td>
<td>$${(c.market_cap / 1e9).toFixed(1)}B</td>
<td>${c.ev_rev ? c.ev_rev.toFixed(1) + 'x' : 'N/A'}</td>
<td style="color:${r40Color}">${c.rule_40 ? c.rule_40.toFixed(0) : '--'}</td>
<td>${c.growth ? (c.growth * 100).toFixed(0) + '%' : 'N/A'}</td>
<td>${c.beta ? c.beta.toFixed(2) : '1.00'}</td>
</tr>
`;
tbody.innerHTML += row;
});
}
}