修复数据采集和清理的bug,新增持仓排名功能,日线数据分页
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -10,15 +10,13 @@
|
||||
<p style="font-size:0.78rem;color:var(--sub);margin:0;">
|
||||
振幅 = 近 5 日 (最高−最低) 均值取整 · 点击振幅值查看计算过程
|
||||
</p>
|
||||
{% if rows|length > 7 %}
|
||||
<button id="toggle-all" class="btn" style="font-size:0.78rem;padding:4px 14px;background:var(--surface);color:var(--accent);border:1px solid var(--accent);border-radius:5px;cursor:pointer;" onclick="toggleAll()">显示全部 ({{ rows|length }}条)</button>
|
||||
{% endif %}
|
||||
<span style="font-size:0.78rem;color:var(--sub);">共 {{ total_rows }} 条</span>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap">
|
||||
<table id="bars-table">
|
||||
<tr><th>日期</th><th>星期</th><th>开盘</th><th>收盘</th><th>最高</th><th>最低</th><th>波幅</th><th>5日均振幅</th></tr>
|
||||
{% if next_date %}
|
||||
{% if page == 1 and next_date %}
|
||||
<tr style="background:var(--accent-light);font-weight:500;">
|
||||
<td>{{ next_date }}</td>
|
||||
<td>{{ next_weekday }}</td>
|
||||
@@ -31,7 +29,7 @@
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% for row in rows %}
|
||||
<tr data-index="{{ loop.index0 }}" data-diff="{{ row.diff }}" data-date="{{ row.date }}" data-weekday="{{ row.weekday }}"{% if loop.index0 >= 7 %} class="extra-row" style="display:none;"{% endif %}>
|
||||
<tr data-index="{{ row.global_idx }}" data-diff="{{ row.diff }}" data-date="{{ row.date }}" data-weekday="{{ row.weekday }}">
|
||||
<td>{{ row.date }}</td>
|
||||
<td>{{ row.weekday }}</td>
|
||||
<td>{{ row.open }}</td>
|
||||
@@ -51,48 +49,101 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% if total_pages > 1 %}
|
||||
<div style="display:flex;align-items:center;justify-content:center;gap:6px;margin-bottom:24px;">
|
||||
{% if page > 1 %}
|
||||
<a class="btn" href="?page={{ page - 1 }}{% if pos_date %}&pos_date={{ pos_date }}{% endif %}" style="font-size:0.82rem;padding:5px 14px;background:var(--surface);color:var(--fg);border:1px solid var(--border);border-radius:5px;text-decoration:none;">← 上一页</a>
|
||||
{% endif %}
|
||||
|
||||
var allShown = false;
|
||||
function toggleAll() {
|
||||
allShown = !allShown;
|
||||
var btn = document.getElementById('toggle-all');
|
||||
var rows = document.querySelectorAll('.extra-row');
|
||||
rows.forEach(function(r) { r.style.display = allShown ? '' : 'none'; });
|
||||
btn.textContent = allShown ? '收起' : '显示全部 ({{ rows|length }}条)';
|
||||
{% for p in range(1, total_pages + 1) %}
|
||||
{% if p == page %}
|
||||
<span style="font-size:0.82rem;padding:5px 12px;background:var(--accent);color:#fff;border-radius:5px;font-weight:600;">{{ p }}</span>
|
||||
{% elif p <= 3 or p > total_pages - 3 or (p >= page - 1 and p <= page + 1) %}
|
||||
<a class="btn" href="?page={{ p }}{% if pos_date %}&pos_date={{ pos_date }}{% endif %}" style="font-size:0.82rem;padding:5px 12px;background:var(--surface);color:var(--fg);border:1px solid var(--border);border-radius:5px;text-decoration:none;">{{ p }}</a>
|
||||
{% elif p == 4 or p == total_pages - 3 %}
|
||||
<span style="color:var(--sub);padding:5px 4px;">…</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page < total_pages %}
|
||||
<a class="btn" href="?page={{ page + 1 }}{% if pos_date %}&pos_date={{ pos_date }}{% endif %}" style="font-size:0.82rem;padding:5px 14px;background:var(--surface);color:var(--fg);border:1px solid var(--border);border-radius:5px;text-decoration:none;">下一页 →</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if pos_dates %}
|
||||
<div class="section-title" style="margin-top:28px;display:flex;align-items:center;gap:12px;">
|
||||
<span>持仓排名 · 前20</span>
|
||||
<input type="date" id="pos-date-select" value="{{ selected_pos_date }}" min="{{ pos_dates[-1] }}" max="{{ pos_dates[0] }}" onchange="changePosDate(this.value)" style="font-size:0.82rem;padding:4px 10px;border:1px solid var(--border);border-radius:5px;background:var(--surface);color:var(--fg);cursor:pointer;">
|
||||
</div>
|
||||
|
||||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:24px;">
|
||||
|
||||
{% set types = [('volume', '成交量'), ('long', '多单持仓'), ('short', '空单持仓')] %}
|
||||
{% for dtype, dlabel in types %}
|
||||
<div class="table-wrap" style="margin-bottom:0;">
|
||||
<table style="font-size:0.82rem;">
|
||||
<tr><th colspan="4" style="font-size:0.8rem;color:var(--accent);">{{ dlabel }}</th></tr>
|
||||
<tr><th>#</th><th>会员</th><th>持仓</th><th>增减</th></tr>
|
||||
{% for row in pos_data[dtype] %}
|
||||
<tr>
|
||||
<td style="color:var(--sub);">{{ row.rank }}</td>
|
||||
<td style="text-align:left;">{{ row.institution }}</td>
|
||||
<td>{{ row.value }}</td>
|
||||
<td style="color:{% if row.change > 0 %}var(--success-fg){% elif row.change < 0 %}var(--danger-fg){% else %}var(--sub){% endif %};">
|
||||
{% if row.change > 0 %}+{% endif %}{{ row.change }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
function changePosDate(d) {
|
||||
var url = new URL(window.location);
|
||||
url.searchParams.set('pos_date', d);
|
||||
window.location = url;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.amp-cell').forEach(function(cell) {
|
||||
cell.addEventListener('click', function() {
|
||||
var row = cell.parentElement.parentElement;
|
||||
var tbl = document.getElementById('bars-table');
|
||||
var dataRows = tbl.querySelectorAll('tr[data-index]');
|
||||
|
||||
// Next-day row: use latest 5 data rows
|
||||
// Next-day prediction row
|
||||
if (cell.dataset.next) {
|
||||
var dataRows = document.querySelectorAll('#bars-table tr[data-index]');
|
||||
if (dataRows.length < 5) return;
|
||||
showDrawer(cell, '{{ contract }}', dataRows, 0, 4);
|
||||
var rows = Array.from(dataRows).slice(0, 5);
|
||||
showDrawer(cell, '{{ contract }}', rows, true);
|
||||
return;
|
||||
}
|
||||
|
||||
var idx = parseInt(row.dataset.index);
|
||||
// Need 5 older rows (higher index, since newest first)
|
||||
if (idx + 5 >= dataRows.length) return;
|
||||
showDrawer(cell, '{{ contract }}', dataRows, idx + 1, idx + 5);
|
||||
// Collect 5 older rows via DOM navigation (next siblings in the table)
|
||||
var row = cell.parentElement.parentElement;
|
||||
var olderRows = [];
|
||||
var cursor = row;
|
||||
for (var j = 0; j < 5; j++) {
|
||||
cursor = cursor.nextElementSibling;
|
||||
if (!cursor || !cursor.dataset.index) return;
|
||||
olderRows.push(cursor);
|
||||
}
|
||||
showDrawer(cell, '{{ contract }}', olderRows, false);
|
||||
});
|
||||
});
|
||||
|
||||
function showDrawer(cell, product, dataRows, fromIdx, toIdx) {
|
||||
function showDrawer(cell, product, calcRows, isNext) {
|
||||
document.getElementById('drawer-title').textContent = product;
|
||||
var label = cell.dataset.next ? '次日预测' : '目标';
|
||||
var label = isNext ? '次日预测' : '目标';
|
||||
document.getElementById('drawer-date').textContent = label + ' 振幅 ' + cell.textContent.trim();
|
||||
|
||||
var html = '<tr><td>日期</td><td>最高−最低</td></tr>';
|
||||
var sum = 0;
|
||||
for (var j = fromIdx; j <= toIdx; j++) {
|
||||
var r = dataRows[j];
|
||||
calcRows.forEach(function(r) {
|
||||
html += '<tr><td>' + r.dataset.date + ' ' + r.dataset.weekday + '</td><td>' + r.dataset.diff + '</td></tr>';
|
||||
sum += parseInt(r.dataset.diff);
|
||||
}
|
||||
});
|
||||
document.getElementById('drawer-table').innerHTML = html;
|
||||
document.getElementById('drawer-result').innerHTML = '合计 <b>' + sum + '</b> ÷ 5 = <b>' + (sum / 5).toFixed(1) + '</b><br>四舍五入 → <b>' + Math.round(sum / 5) + '</b>';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user