diff --git a/install_trading.py b/install_trading.py index 1e48da1..bb74e11 100644 --- a/install_trading.py +++ b/install_trading.py @@ -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, diff --git a/product_recommend.py b/product_recommend.py index 9a7a3bf..2c5eff4 100644 --- a/product_recommend.py +++ b/product_recommend.py @@ -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 diff --git a/static/js/trade.js b/static/js/trade.js index af6c584..45fbe6c 100644 --- a/static/js/trade.js +++ b/static/js/trade.js @@ -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 ( + '