v1.3.3: стресс-таблица как основная (EBITDA ≤ 0 / ∞ / nf2), stressRow — guar тоже ×1.10 (долг вместе с гарантиями +10%), agentContext: коэффициенты 6 знаков + готовые на_экране 2 знака, инструкция ИИ использовать на_экране
This commit is contained in:
parent
f1cacb5c73
commit
6a494319dd
53
index.html
53
index.html
@ -509,16 +509,18 @@ function calcRow(r){
|
||||
/* Стресс-тест (та же логика, что используется в расчёте): долг вместе с гарантиями +10%, EBITDA −15%, проценты +20%, капитал −5%.
|
||||
Расчёт — исходный calcRow() на изменённой копии строки; null-значения сохраняются. */
|
||||
function stressRow(r){
|
||||
/* «Долг вместе с гарантиями +10%» — оба компонента (debt и guar) растут ровно на 10%,
|
||||
поэтому c.debt + c.guar = (r.debt + r.guar) * 1.10 */
|
||||
const d = r.debt==null ? 0 : r.debt;
|
||||
const g = r.guar==null ? 0 : r.guar;
|
||||
const c = {...r};
|
||||
c.debt = (isNum(r.debt) ? r.debt : 0) * 1.10;
|
||||
if(isNum(r.debt)) c.debt = d*1.10; else c.debt = (d+g)*1.10;
|
||||
if(isNum(r.guar)) c.guar = g*1.10;
|
||||
if(isNum(r.ebitda)) c.ebitda = r.ebitda * 0.85;
|
||||
if(isNum(r.interest)) c.interest = r.interest * 1.20;
|
||||
if(isNum(r.equity)) c.equity = r.equity * 0.95;
|
||||
const res = calcRow(c);
|
||||
return {...res, debtStress: res.debt==null?null:+res.debt.toFixed(3),
|
||||
ebitdaStress: res.e==null?null:+res.e.toFixed(3),
|
||||
interestStress: res.i==null?null:+res.i.toFixed(3),
|
||||
equityStress: res.q==null?null:+res.q.toFixed(3)};
|
||||
return {...res};
|
||||
}
|
||||
|
||||
/* ---------- registry ---------- */
|
||||
@ -618,7 +620,6 @@ function renderCalc(){
|
||||
t.innerHTML = h;
|
||||
$('#kpi').innerHTML = `<div><b>${S.registry.length}</b><span>компаний/периодов</span></div><div class="green"><b>${cnt.green}</b><span>зелёная зона</span></div><div class="yellow"><b>${cnt.yellow}</b><span>жёлтая зона</span></div><div class="red"><b>${cnt.red}</b><span>красная зона</span></div><div><b>${cnt.na}</b><span>без оценки (нет целей/порогов)</span></div>`;
|
||||
}
|
||||
function kfmt(v){ if(v==null) return '—'; if(v===Infinity) return '∞'; if(typeof v==='string') return esc(v); return nf2.format(v); }
|
||||
function zoneChip(z){ const nm = z==='na' ? 'н/д' : ZNAME[z]; return `<span class="zone ${z}">${nm}</span>`; }
|
||||
function renderStress(){
|
||||
const t = $('#stressTable'); if(!t) return;
|
||||
@ -626,11 +627,15 @@ function renderStress(){
|
||||
let h = `<tr><th>Компания</th><th class="num">К1 Долг/EBITDA (база → стресс)</th><th>Зона стресс</th><th class="num">К2 EBITDA/Проценты (база → стресс)</th><th>Зона стресс</th><th class="num">К3 Долг/Капитал (база → стресс)</th><th>Зона стресс</th><th>Итоговая зона (стресс)</th></tr>`;
|
||||
for(const r of S.registry){
|
||||
const b = calcRow(r), s = stressRow(r);
|
||||
const arrow = (base, stress) => `${kfmt(base)} → <b>${kfmt(stress)}</b>`;
|
||||
const arrow = (base, stress, ebBase, ebStress) => {
|
||||
const fb = base===Infinity ? '∞' : (b.ebNeg?'EBITDA ≤ 0':fmtX(base));
|
||||
const fs = stress===Infinity ? '∞' : (s.ebNeg?'EBITDA ≤ 0':fmtX(stress));
|
||||
return `<td class="num"><span class="muted">${fb}</span> → <b class="${stress===null?'muted':''}">${fs}</b></td>`;
|
||||
};
|
||||
h += `<tr class="overall-${s.worst}"><td><b>${esc(displayName(r.key,r.name))}</b></td>
|
||||
<td class="num">${arrow(b.k1, s.k1)}</td><td>${zoneChip(s.z1)}</td>
|
||||
<td class="num">${arrow(b.k2, s.k2)}</td><td>${zoneChip(s.z2)}</td>
|
||||
<td class="num">${arrow(b.k3, s.k3)}</td><td>${zoneChip(s.z3)}</td>
|
||||
${arrow(b.k1, s.k1, b.ebNeg, s.ebNeg)}<td>${zoneChip(s.z1)}</td>
|
||||
${arrow(b.k2, s.k2, b.ebNeg, s.ebNeg)}<td>${zoneChip(s.z2)}</td>
|
||||
${arrow(b.k3, s.k3, false, false)}<td>${zoneChip(s.z3)}</td>
|
||||
<td>${zoneChip(s.worst)}</td></tr>`;
|
||||
}
|
||||
t.innerHTML = h;
|
||||
@ -1381,16 +1386,29 @@ $('#btnAiMap').onclick = async () => {
|
||||
};
|
||||
|
||||
/* ===================== AGENT CHAT ===================== */
|
||||
/* Готовое экранные отображение (2 знака) + точное значение (6 знаков) — без повторного округления со стороны ИИ */
|
||||
const f3 = v => !isNum(v) ? null : +v.toFixed(3);
|
||||
const f6 = v => v==null ? null : +v.toFixed(6);
|
||||
const k6 = v => (v===Infinity?'inf':f6(v));
|
||||
const k2 = v => (v===Infinity?'∞':(v==null?'':nf2.format(v)));
|
||||
function stressRows(){
|
||||
return S.registry.map(r=>({компания: displayName(r.key,r.name), период:r.period, ...stressRow(r)}));
|
||||
return S.registry.map(r=>{ const s=stressRow(r); return {
|
||||
компания: displayName(r.key,r.name), период:r.period,
|
||||
К1:{значение:k6(s.k1), на_экране:s.ebNeg?'EBITDA ≤ 0':k2(s.k1), зона:s.z1},
|
||||
К2:{значение:k6(s.k2), на_экране:s.ebNeg?'EBITDA ≤ 0':k2(s.k2), зона:s.z2},
|
||||
К3:{значение:k6(s.k3), на_экране:k2(s.k3), зона:s.z3},
|
||||
итог:s.worst }; });
|
||||
}
|
||||
function agentContext(){
|
||||
const rows = S.registry.map(r=>{ const c=calcRow(r); return {
|
||||
компания: displayName(r.key,r.name), период:r.period, источник:r.source,
|
||||
долг_млрд:+((r.debt||0)).toFixed(3), гарантии_млрд:+((r.guar||0)).toFixed(3), итого_долг_млрд:+c.debt.toFixed(3),
|
||||
ebitda_12м_млрд: isNum(r.ebitda)?+r.ebitda.toFixed(3):null, проценты_12м_млрд:isNum(r.interest)?+r.interest.toFixed(3):null, капитал_млрд:isNum(r.equity)?+r.equity.toFixed(3):null,
|
||||
К1: c.ebNeg?'EBITDA<=0':(isNum(c.k1)?+c.k1.toFixed(3):null), К2: c.ebNeg?'EBITDA<=0':(c.k2===Infinity?'inf':(isNum(c.k2)?+c.k2.toFixed(3):null)), К3: isNum(c.k3)?+c.k3.toFixed(3):null,
|
||||
цели:{К1:c.p.t1,К2:c.p.t2,К3:c.p.t3}, пороги:{К1:c.p.p1,К2:c.p.p2,К3:c.p.p3}, зоны:{К1:c.z1,К2:c.z2,К3:c.z3,итог:c.worst},
|
||||
долг_млрд:f3((r.debt||0)), гарантии_млрд:f3((r.guar||0)), итого_долг_млрд:f3(c.debt),
|
||||
ebitda_12м_млрд:f3(r.ebitda), проценты_12м_млрд:f3(r.interest), капитал_млрд:f3(r.equity),
|
||||
К1:{значение:c.ebNeg?'EBITDA<=0':k6(c.k1), на_экране:c.ebNeg?'EBITDA ≤ 0':k2(c.k1)},
|
||||
К2:{значение:c.ebNeg?'EBITDA<=0':k6(c.k2), на_экране:c.ebNeg?'EBITDA ≤ 0':k2(c.k2)},
|
||||
К3:{значение:k6(c.k3), на_экране:k2(c.k3)},
|
||||
цели:{К1:c.p.t1,К2:c.p.t2,К3:c.p.t3}, пороги:{К1:c.p.p1,К2:c.p.p2,К3:c.p.p3},
|
||||
зоны:{К1:c.z1,К2:c.z2,К3:c.z3,итог:c.worst},
|
||||
месяцев:r.months||12, ltm:r.ltm!==false, примечание:r.note||'' }; });
|
||||
return JSON.stringify(rows);
|
||||
}
|
||||
@ -1402,8 +1420,9 @@ ${$('#methodText').value}
|
||||
${$('#agentExtra').value.trim()?'Дополнительные указания пользователя: '+$('#agentExtra').value.trim():''}
|
||||
Данные реестра и расчёта (JSON):
|
||||
${agentContext()}
|
||||
Стресс-тест (уже пересчитан: долг +10%, EBITDA −15%, процентные расходы +20%, капитал −5%) — используй только эти значения, не пересчитывай самостоятельно (JSON):
|
||||
${JSON.stringify(stressRows())}`;
|
||||
Стресс-тест (уже пересчитан: долг вместе с гарантиями +10%, EBITDA −15%, процентные расходы +20%, капитал −5%) — используй только эти значения, не пересчитывай самостоятельно (JSON):
|
||||
${JSON.stringify(stressRows())}
|
||||
Правило вывода коэффициентов: при упоминании значений К1–К3 в ответе бери готовые на_экране (2 знака, запятая — десятичный разделитель, «x» не добавлять), не округляй «значение» (6 знаков) самостоятельно; «значение» использовать только для вычислений в формулах.`;
|
||||
}
|
||||
function chatAppend(role, text){ const log=$('#chatLog'); const d=document.createElement('div'); d.className='msg '+role; d.textContent=text; log.appendChild(d); log.scrollTop=log.scrollHeight; return d; }
|
||||
async function chatSend(q){
|
||||
|
||||
Loading…
Reference in New Issue
Block a user