Compare commits

..

8 Commits

2 changed files with 58 additions and 16 deletions
+16 -3
View File
@@ -1,4 +1,4 @@
from datetime import date from datetime import date, timedelta
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@@ -59,7 +59,7 @@ def contract_detail(request: Request, contract: str, db: Session = Depends(get_d
bars = ( bars = (
db.query(DailyBar) db.query(DailyBar)
.filter(DailyBar.contract == contract.upper()) .filter(DailyBar.contract == contract.upper())
.order_by(DailyBar.date) .order_by(DailyBar.date.desc())
.all() .all()
) )
@@ -77,8 +77,18 @@ def contract_detail(request: Request, contract: str, db: Session = Depends(get_d
"has_amp": bar.amp_5d is not None, "has_amp": bar.amp_5d is not None,
}) })
latest = bars[-1] if bars else None latest = bars[0] if bars else None
# Predict next trading day amplitude: mean of latest 5 diffs
# Compute next trading date
next_date = latest.date + timedelta(days=1) if latest else None
if next_date and next_date.weekday() >= 5:
next_date += timedelta(days=7 - next_date.weekday())
# Predict next trading day amplitude
next_amp = None
if len(bars) >= 5:
next_amp = round(sum(b.diff for b in bars[:5]) / 5)
template = request.app.state.templates.get_template("contract.html") template = request.app.state.templates.get_template("contract.html")
return HTMLResponse( return HTMLResponse(
template.render( template.render(
@@ -88,6 +98,9 @@ def contract_detail(request: Request, contract: str, db: Session = Depends(get_d
contracts=active_contracts, contracts=active_contracts,
rows=rows, rows=rows,
latest=latest, latest=latest,
next_amp=next_amp,
next_date=next_date.strftime("%Y/%-m/%-d") if next_date else None,
next_weekday=WEEKDAY_ZH.get(next_date.weekday(), "") if next_date else "",
row_count=len(rows), row_count=len(rows),
) )
) )
+41 -12
View File
@@ -12,27 +12,45 @@
<div class="trend">日期 {{ latest.date }}</div> <div class="trend">日期 {{ latest.date }}</div>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<div class="label">当日振幅</div> <div class="label">次日预测振幅</div>
<div class="value">{{ latest.amp_5d|int if latest.amp_5d else '—' }}</div> <div class="value" style="color:var(--accent);">{{ next_amp if next_amp else '—' }}</div>
<div class="trend">日波幅 {{ latest.diff|int }} 点</div> <div class="trend">近5日均波幅</div>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<div class="label">数据条数</div> <div class="label">最新波幅</div>
<div class="value">{{ row_count }}</div> <div class="value">{{ latest.diff|int }}</div>
<div class="trend">{{ latest.date }}</div>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="section-title">日线数据</div> <div class="section-title">日线数据</div>
<p style="font-size:0.78rem;color:var(--sub);margin-bottom:12px;"> <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;">
<p style="font-size:0.78rem;color:var(--sub);margin:0;">
振幅 = 近 5 日 (最高−最低) 均值取整 · 点击振幅值查看计算过程 振幅 = 近 5 日 (最高−最低) 均值取整 · 点击振幅值查看计算过程
</p> </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 %}
</div>
<div class="table-wrap"> <div class="table-wrap">
<table id="bars-table"> <table id="bars-table">
<tr><th>日期</th><th>星期</th><th>开盘</th><th>收盘</th><th>最高</th><th>最低</th><th>波幅</th><th>5日均振幅</th></tr> <tr><th>日期</th><th>星期</th><th>开盘</th><th>收盘</th><th>最高</th><th>最低</th><th>波幅</th><th>5日均振幅</th></tr>
{% if next_date %}
<tr style="background:var(--accent-light);font-weight:500;">
<td>{{ next_date }}</td>
<td>{{ next_weekday }}</td>
<td class="na"></td>
<td class="na"></td>
<td class="na"></td>
<td class="na"></td>
<td class="na"></td>
<td><span style="color:var(--accent);font-weight:700;">{{ next_amp }}</span></td>
</tr>
{% endif %}
{% for row in rows %} {% for row in rows %}
<tr data-index="{{ loop.index0 }}" data-diff="{{ row.diff }}" data-date="{{ row.date }}" data-weekday="{{ row.weekday }}"> <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 %}>
<td>{{ row.date }}</td> <td>{{ row.date }}</td>
<td>{{ row.weekday }}</td> <td>{{ row.weekday }}</td>
<td>{{ row.open }}</td> <td>{{ row.open }}</td>
@@ -53,21 +71,32 @@
</div> </div>
<script> <script>
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 }}条)';
}
document.querySelectorAll('.amp-cell').forEach(function(cell) { document.querySelectorAll('.amp-cell').forEach(function(cell) {
cell.addEventListener('click', function() { cell.addEventListener('click', function() {
var row = cell.parentElement.parentElement; var row = cell.parentElement.parentElement;
var tbl = document.getElementById('bars-table'); var tbl = document.getElementById('bars-table');
var rows = tbl.querySelectorAll('tr'); // Only rows with data-index (skip header + next-day row)
var dataRows = tbl.querySelectorAll('tr[data-index]');
var idx = parseInt(row.dataset.index); var idx = parseInt(row.dataset.index);
if (idx < 5) return; // Need 5 older rows (higher index, since newest first)
if (idx + 5 >= dataRows.length) return;
document.getElementById('drawer-title').textContent = '{{ contract }}'; document.getElementById('drawer-title').textContent = '{{ contract }}';
document.getElementById('drawer-date').textContent = '目标: ' + row.dataset.date + ' ' + row.dataset.weekday + ' 振幅 ' + cell.textContent.trim(); document.getElementById('drawer-date').textContent = '目标: ' + row.dataset.date + ' ' + row.dataset.weekday + ' 振幅 ' + cell.textContent.trim();
var html = '<tr><td>日期</td><td>最高−最低</td></tr>'; var html = '<tr><td>日期</td><td>最高−最低</td></tr>';
var sum = 0; var sum = 0;
for (var j = idx - 5; j < idx; j++) { for (var j = idx + 1; j <= idx + 5; j++) {
var r = rows[j + 1]; var r = dataRows[j];
html += '<tr><td>' + r.dataset.date + ' ' + r.dataset.weekday + '</td><td>' + r.dataset.diff + '</td></tr>'; html += '<tr><td>' + r.dataset.date + ' ' + r.dataset.weekday + '</td><td>' + r.dataset.diff + '</td></tr>';
sum += parseInt(r.dataset.diff); sum += parseInt(r.dataset.diff);
} }