修复持仓监控页长时间空白:品种推荐改为异步加载。
页面先渲染三卡片,推荐表并行拉行情,持仓与推荐分别通过 API 加载。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -289,7 +289,6 @@ def install_trading(app, *, login_required, get_db, get_setting, set_setting, fe
|
||||
capital = _capital(conn)
|
||||
risk = get_risk_status(conn)
|
||||
ctp_acc = _ctp_account(mode) if ctp_st.get("connected") else {}
|
||||
recommend_rows = list_product_recommendations(capital, _main_price)
|
||||
active_trend = conn.execute(
|
||||
"SELECT * FROM trend_pullback_plans WHERE status='active' ORDER BY id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
@@ -309,7 +308,6 @@ def install_trading(app, *, login_required, get_db, get_setting, set_setting, fe
|
||||
risk_status=risk,
|
||||
ctp_status=ctp_st,
|
||||
ctp_account=ctp_acc,
|
||||
recommend_rows=recommend_rows,
|
||||
active_trend=dict(active_trend) if active_trend else None,
|
||||
monitor_count=monitor_count,
|
||||
roll_count=roll_count,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""按账户资金推荐可交易品种(期货核心筛选)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import Callable, Optional
|
||||
|
||||
from contract_specs import get_contract_spec
|
||||
@@ -83,14 +84,16 @@ def list_product_recommendations(
|
||||
max_position_pct: float = 50.0,
|
||||
) -> list[dict]:
|
||||
"""扫描全部品种并排序:推荐 > 可开 > 不足。"""
|
||||
rows = []
|
||||
for p in PRODUCTS:
|
||||
ths = p["ths"]
|
||||
|
||||
def _one(product: dict) -> dict:
|
||||
ths = product["ths"]
|
||||
main_code = price_fn(ths)
|
||||
row = assess_product_for_capital(
|
||||
p, capital, main_code, max_position_pct=max_position_pct
|
||||
return assess_product_for_capital(
|
||||
product, capital, main_code, max_position_pct=max_position_pct
|
||||
)
|
||||
rows.append(row)
|
||||
|
||||
with ThreadPoolExecutor(max_workers=10) as pool:
|
||||
rows = list(pool.map(_one, PRODUCTS))
|
||||
order = {"ok": 0, "margin_ok": 1, "blocked": 2, "no_price": 3}
|
||||
rows.sort(key=lambda r: (order.get(r["status"], 9), r.get("min_capital_one_lot") or 1e18))
|
||||
return rows
|
||||
|
||||
+57
-2
@@ -1,7 +1,16 @@
|
||||
(function () {
|
||||
var list = document.getElementById('position-live-list');
|
||||
var recommendList = document.getElementById('recommend-list');
|
||||
var pollTimer = null;
|
||||
|
||||
function runWhenReady(fn) {
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
} else {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
|
||||
function fmtNum(v, digits) {
|
||||
if (v === null || v === undefined) return '--';
|
||||
return Number(v).toFixed(digits === undefined ? 2 : digits);
|
||||
@@ -110,7 +119,10 @@
|
||||
function pollPositions() {
|
||||
if (!list) return;
|
||||
fetch('/api/trading/live')
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (r) {
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
return r.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
var cap = document.getElementById('cap-display');
|
||||
if (cap && data.capital != null) cap.textContent = Number(data.capital).toFixed(2);
|
||||
@@ -145,6 +157,48 @@
|
||||
});
|
||||
}
|
||||
|
||||
function badgeClass(status) {
|
||||
if (status === 'ok') return 'profit';
|
||||
if (status === 'blocked') return 'loss';
|
||||
return 'planned';
|
||||
}
|
||||
|
||||
function buildRecommendRow(r) {
|
||||
return (
|
||||
'<tr class="rec-' + (r.status || '') + '">' +
|
||||
'<td><strong>' + (r.name || '') + '</strong> <span class="text-muted">' + (r.ths || '') + '</span></td>' +
|
||||
'<td>' + (r.exchange || '') + '</td>' +
|
||||
'<td>' + (r.price != null ? r.price : '—') + '</td>' +
|
||||
'<td>' + (r.margin_one_lot != null ? r.margin_one_lot : '—') + '</td>' +
|
||||
'<td>' + (r.min_capital_one_lot != null ? r.min_capital_one_lot : '—') + '</td>' +
|
||||
'<td><span class="badge ' + badgeClass(r.status) + '">' + (r.status_label || '') + '</span></td>' +
|
||||
'</tr>'
|
||||
);
|
||||
}
|
||||
|
||||
function loadRecommendations() {
|
||||
if (!recommendList) return;
|
||||
fetch('/api/recommend/list')
|
||||
.then(function (r) {
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
return r.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
if (!data.ok) throw new Error(data.error || 'load failed');
|
||||
var recCap = document.getElementById('rec-capital');
|
||||
if (recCap && data.capital != null) recCap.textContent = Number(data.capital).toFixed(2);
|
||||
var rows = data.rows || [];
|
||||
if (!rows.length) {
|
||||
recommendList.innerHTML = '<tr><td colspan="6" class="empty-hint">暂无推荐数据</td></tr>';
|
||||
return;
|
||||
}
|
||||
recommendList.innerHTML = rows.map(buildRecommendRow).join('');
|
||||
})
|
||||
.catch(function () {
|
||||
recommendList.innerHTML = '<tr><td colspan="6" class="empty-hint text-loss">品种推荐加载失败,请刷新页面</td></tr>';
|
||||
});
|
||||
}
|
||||
|
||||
var btnConnect = document.getElementById('btn-ctp-connect');
|
||||
if (btnConnect) {
|
||||
btnConnect.addEventListener('click', function () {
|
||||
@@ -163,8 +217,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
runWhenReady(function () {
|
||||
pollPositions();
|
||||
loadRecommendations();
|
||||
pollTimer = setInterval(pollPositions, 3000);
|
||||
});
|
||||
})();
|
||||
|
||||
+2
-11
@@ -80,17 +80,8 @@
|
||||
<th>品种</th><th>交易所</th><th>参考价</th><th>1手保证金</th><th>建议最低资金</th><th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in recommend_rows %}
|
||||
<tr class="rec-{{ r.status }}">
|
||||
<td><strong>{{ r.name }}</strong> <span class="text-muted">{{ r.ths }}</span></td>
|
||||
<td>{{ r.exchange }}</td>
|
||||
<td>{% if r.price %}{{ r.price }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.margin_one_lot %}{{ r.margin_one_lot }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.min_capital_one_lot %}{{ r.min_capital_one_lot }}{% else %}—{% endif %}</td>
|
||||
<td><span class="badge {% if r.status=='ok' %}profit{% elif r.status=='blocked' %}loss{% else %}planned{% endif %}">{{ r.status_label }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody id="recommend-list">
|
||||
<tr><td colspan="6" class="empty-hint">品种推荐加载中…</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user