Files
Bridge-and-Join-s/internal/lkgateway/web/templates/admin_npr.html
T
zuevav da12a9ec1c feat(bj): модуль ЛК на PostgreSQL, обмен с DEPO, боевой ЛК с имитатором НРД, QUIK, НПР
- bj_exchange и «кран» Fancy убраны: данные модуля ЛК в PostgreSQL (схема lk)
- трансферные таблицы FTV_M2M: создание при установке, события, справочник участников
- сверка с DEPO: счёт, ФИО, дата рождения, ИИС по договору, квал-инвестор из опции DEPO
- боевой ЛК ЕСИА Финанс при тестовом ИШ: журнал, отказ через DEPO, статусы в ЛК
- онлайн-лицензирование в bj, план тестирования M2M для контрагента (интерактивный HTML)
- модули QUIK МНП, экспорт QUIK, НПР/УДС; документы и тестовые материалы

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 19:41:12 +03:00

33 lines
6.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "content"}}
{{template "npr_css"}}
<script src="/admin/npr/static/htmx.min.js"></script>
<div class="doc-header" style="margin-bottom:12px"><h1>📊 Риск-монитор: НПР и УДС клиентов</h1><p class="lede">НПР1 / НПР2 / УДС клиентов маржинальной торговли по данным экспорта QUIK. Модуль перенесён как есть, доработки — по мере необходимости.</p></div>
<nav class="ntabs">
<a href="?tab=margin" class="{{if eq .Tab "margin"}}active{{end}}">Маржинальный риск <span class="count" id="tab_margin_count">—</span></a>
<a href="?tab=debt" class="{{if eq .Tab "debt"}}active{{end}}">Дебиторская задолженность <span class="count" id="tab_debt_count">—</span></a>
</nav>
{{if eq .Tab "margin"}}<p class="tab-desc">Клиенты с открытыми маржинальными позициями (есть начальная или минимальная маржа). У кого УДС близок к 1 — под маржин-колл, у кого &lt;0 — под принудительное закрытие.</p>
{{else}}<p class="tab-desc">Клиенты без открытых позиций, но с отрицательной стоимостью портфеля (должны брокеру). УДС у таких счетов в QUIK уходит в −9,99 — это задолженность, а не маржин-колл.</p>{{end}}
<div class="nhelp"><strong>УДС</strong> — уровень достаточности средств (чем меньше, тем опаснее). <strong>НПР1</strong> — запас относительно начальной маржи (отрицательный — запрещено наращивать позиции). <strong>НПР2</strong> — запас относительно минимальной маржи (отрицательный — начинается принудительное закрытие).
<div class="legend"><span class="legend-item"><span class="legend-box" style="background:#fee2e2"></span> маржин-колл (УДС&lt;1)</span><span class="legend-item"><span class="legend-box" style="background:#fef3c7"></span> внимание (УДС&lt;1,5)</span><span class="legend-item"><span class="legend-box" style="background:#d1fae5"></span> «∞» избыточно (УДС≥9,99)</span></div></div>
<section class="nsummary" hx-get="/admin/npr/api/summary?tab={{.Tab}}" hx-trigger="load, every 60s" hx-swap="none" hx-on::after-request="try{const d=JSON.parse(event.detail.xhr.responseText);for(const [k,v] of Object.entries({sum_total:d.total,sum_calls:d.calls,sum_warn:d.warn,sum_ok:d.ok,sum_inf:d.inf,tab_margin_count:d.tab_margin,tab_debt_count:d.tab_debt})){const e=document.getElementById(k);if(e)e.textContent=v;}}catch(e){}">
<article><header>всего клиентов</header><strong id="sum_total">—</strong></article>
<article class="tile-call"><header>маржин-колл (УДС&lt;1)</header><strong id="sum_calls">—</strong></article>
<article class="tile-warn"><header>внимание (УДС&lt;1,5)</header><strong id="sum_warn">—</strong></article>
<article class="tile-ok"><header>норма</header><strong id="sum_ok">—</strong></article>
<article class="tile-ok"><header>избыточно (∞)</header><strong id="sum_inf">—</strong></article>
</section>
<div class="nfresh" hx-get="/admin/npr/api/freshness" hx-trigger="load, every 15s" hx-swap="none" hx-on::after-request="try{const d=JSON.parse(event.detail.xhr.responseText);const now=d.server_now?new Date(d.server_now):new Date();const tick=d.last_tick?new Date(d.last_tick):null;const ago=tick?Math.round((now-tick.getTime())/1000):null;const ok=ago!==null&&ago<90;document.getElementById('fresh_dot').style.background=ok?'#10b981':'#dc2626';document.getElementById('fresh_text').textContent=tick?('Последнее обновление из QUIK: '+tick.toLocaleString('ru-RU')+' ('+ago+' сек. назад)'):'Ждём первый тик от экспортёра…';const ev=d.last_event_ts?new Date(d.last_event_ts):null;document.getElementById('fresh_detail').textContent='События в истории: всего '+d.total_rows.toLocaleString('ru-RU')+' · за час: '+d.events_last_hour+' · за сутки: '+d.events_last_24h+(ev?' · последнее: '+ev.toLocaleString('ru-RU'):'');}catch(e){}">
<span id="fresh_dot" style="width:10px;height:10px;border-radius:50%;background:#9ca3af;display:inline-block"></span><span id="fresh_text">Проверяю актуальность данных…</span><span class="muted" id="fresh_detail" style="margin-left:auto"></span></div>
<form id="filters" class="nfilters" hx-get="/admin/npr/rows" hx-trigger="input changed delay:300ms, change" hx-target="#rows" hx-swap="innerHTML" onsubmit="return false">
<label>Поиск по коду клиента<input type="search" name="q" placeholder="например, BR22…" value="{{.Q}}"></label>
<label>Тип клиента<select name="mt" multiple size="3">{{range $v := list "МД" "МД+" "С"}}<option value="{{$v}}" {{if has $.MT $v}}selected{{end}}>{{$v}}</option>{{end}}</select></label>
<label>УДС не менее<input type="number" step="0.01" name="umn" value="{{.UMN}}"></label>
<label>УДС не более<input type="number" step="0.01" name="umx" value="{{.UMX}}"></label>
<label style="align-self:center;white-space:nowrap;display:flex;gap:6px;align-items:center"><input type="checkbox" name="only_calls" value="true" {{if .OnlyCalls}}checked{{end}} style="width:auto"> только маржин-колл</label>
<label>Сортировка<select name="sort">{{range $k, $t := dict "uds_asc" "УДС ↑ (опаснее сверху)" "uds_desc" "УДС ↓" "client_asc" "Код клиента ↑" "client_desc" "Код клиента ↓" "ts_desc" "Время обновления ↓"}}<option value="{{$k}}" {{if eq $.Sort $k}}selected{{end}}>{{$t}}</option>{{end}}</select></label>
<input type="hidden" name="page" value="{{.Page}}"><input type="hidden" name="tab" value="{{.Tab}}">
</form>
<div id="rows" hx-get="/admin/npr/rows" hx-trigger="load, every 60s" hx-include="#filters" hx-swap="innerHTML"><p class="muted"><em>Загружаю клиентов…</em></p></div>
{{end}}