网络请求加超时防止卡死,持仓同步限制最近20天,次日预测移出表格

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 22:23:59 +08:00
parent 18575d1c55
commit 3a32429a5f
4 changed files with 55 additions and 28 deletions
+8 -3
View File
@@ -1,7 +1,10 @@
"""Data collector — fetch OHLCV from akshare and upsert into daily_bars.""" """Data collector — fetch OHLCV from akshare and upsert into daily_bars."""
import socket
import threading import threading
import time import time
from datetime import date from datetime import date
socket.setdefaulttimeout(15)
from app.database import SessionLocal from app.database import SessionLocal
from app.models import DailyBar, Contract from app.models import DailyBar, Contract
from app.engine.lock_strategy import compute_amp_5d from app.engine.lock_strategy import compute_amp_5d
@@ -111,7 +114,9 @@ def sync_positions_bg(contract_code: str) -> bool:
r[0] for r in r[0] for r in
db.query(DailyBar.date) db.query(DailyBar.date)
.filter(DailyBar.contract == code) .filter(DailyBar.contract == code)
.order_by(DailyBar.date).all() .order_by(DailyBar.date.desc())
.limit(20)
.all()
] ]
missing = [d for d in bar_dates if d not in existing_dates] missing = [d for d in bar_dates if d not in existing_dates]
total_missing = len(missing) total_missing = len(missing)
@@ -341,10 +346,10 @@ def _sync_positions_for_contract(db, contract_code: str) -> int:
r[0] for r in r[0] for r in
db.query(DailyBar.date) db.query(DailyBar.date)
.filter(DailyBar.contract == code) .filter(DailyBar.contract == code)
.order_by(DailyBar.date) .order_by(DailyBar.date.desc())
.limit(20)
.all() .all()
] ]
inserted = 0 inserted = 0
for d in bar_dates: for d in bar_dates:
if d in existing_dates: if d in existing_dates:
+24 -3
View File
@@ -245,16 +245,24 @@ function pollProgress() {
clearInterval(syncTimer); clearInterval(syncTimer);
syncTimer = null; syncTimer = null;
document.getElementById('sync-progress-bar').style.width = '100%'; document.getElementById('sync-progress-bar').style.width = '100%';
var resultText = data.result > 0 ? ' · +' + data.result : ''; if (data.result > 0) {
document.getElementById('sync-progress-label').textContent = data.label + ' ✓' + resultText; document.getElementById('sync-progress-label').textContent = data.label + ' ✓ · +' + data.result;
} else if (data.total > 0) {
document.getElementById('sync-progress-label').textContent = data.label + ' ⚠ 无新数据,历史日期无可用持仓';
} else {
document.getElementById('sync-progress-label').textContent = data.label + ' ✓ 已是最新';
}
document.getElementById('sync-progress-pct').textContent = ''; document.getElementById('sync-progress-pct').textContent = '';
// Remember which product headers are expanded before reload document.getElementById('sync-progress').insertAdjacentHTML('beforeend',
'<button onclick="hideProgress()" style="display:block;margin-top:10px;padding:4px 14px;border:1px solid var(--border);border-radius:5px;background:var(--surface);color:var(--fg);cursor:pointer;font-size:0.8rem;">关闭</button>');
if (data.result > 0) {
var expanded = []; var expanded = [];
document.querySelectorAll('.product-body').forEach(function(b, i) { document.querySelectorAll('.product-body').forEach(function(b, i) {
if (b.style.display === 'block') expanded.push(i); if (b.style.display === 'block') expanded.push(i);
}); });
try { sessionStorage.setItem('ft-expanded', JSON.stringify(expanded)); } catch(e) {} try { sessionStorage.setItem('ft-expanded', JSON.stringify(expanded)); } catch(e) {}
setTimeout(function() { location.reload(); }, 1200); setTimeout(function() { location.reload(); }, 1200);
}
}, delay); }, delay);
} }
}); });
@@ -317,5 +325,18 @@ function toggleProduct(header) {
} catch(e) {} } catch(e) {}
})(); })();
// Auto-show progress bar if a sync is already running
(function() {
fetch('/admin/sync-status')
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.running) {
showProgress();
syncTimer = setInterval(pollProgress, 500);
pollProgress();
}
});
})();
</script> </script>
{% endblock %} {% endblock %}
+11 -12
View File
@@ -13,21 +13,18 @@
<span style="font-size:0.78rem;color:var(--sub);">共 {{ total_rows }} 条</span> <span style="font-size:0.78rem;color:var(--sub);">共 {{ total_rows }} 条</span>
</div> </div>
{% if page == 1 and next_date %}
<div style="background:var(--accent-light);border:1px solid var(--accent);border-radius:8px;padding:12px 18px;margin-bottom:14px;display:flex;align-items:center;gap:16px;">
<span style="font-size:0.78rem;color:var(--sub);">📅 次日预测</span>
<span style="font-weight:600;">{{ next_date }} {{ next_weekday }}</span>
<span style="color:var(--sub);">预计振幅</span>
<span class="amp-cell" style="color:var(--accent);font-weight:700;font-size:1.1rem;cursor:pointer;" data-next="1">{{ next_amp }}</span>
</div>
{% endif %}
<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 page == 1 and 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 class="amp-cell" style="color:var(--accent);font-weight:700;" data-next="1">{{ next_amp }}</span></td>
</tr>
{% endif %}
{% for row in rows %} {% for row in rows %}
<tr data-index="{{ row.global_idx }}" data-diff="{{ row.diff }}" data-date="{{ row.date }}" data-weekday="{{ row.weekday }}"> <tr data-index="{{ row.global_idx }}" data-diff="{{ row.diff }}" data-date="{{ row.date }}" data-weekday="{{ row.weekday }}">
<td>{{ row.date }}</td> <td>{{ row.date }}</td>
@@ -150,10 +147,12 @@ function showDrawer(cell, product, calcRows, isNext) {
document.getElementById('overlay').classList.add('show'); document.getElementById('overlay').classList.add('show');
document.getElementById('drawer').classList.add('show'); document.getElementById('drawer').classList.add('show');
if (!isNext) {
var row = cell.parentElement.parentElement; var row = cell.parentElement.parentElement;
if (activeRow) activeRow.classList.remove('active'); if (activeRow) activeRow.classList.remove('active');
row.classList.add('active'); row.classList.add('active');
activeRow = row; activeRow = row;
} }
}
</script> </script>
{% endblock %} {% endblock %}
+2
View File
@@ -1,6 +1,8 @@
services: services:
app: app:
build: . build: .
image: ft-app
container_name: app
ports: ports:
- "8000:8000" - "8000:8000"
volumes: volumes: