feat: 品种下拉统一展示推荐列表,与下方品种推荐表一致。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -24,7 +24,13 @@ from werkzeug.security import check_password_hash, generate_password_hash
|
|||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from symbols import search_symbols, ths_to_codes, list_main_contracts_grouped, refresh_main_index
|
from symbols import (
|
||||||
|
search_symbols,
|
||||||
|
ths_to_codes,
|
||||||
|
list_main_contracts_grouped,
|
||||||
|
list_recommended_symbols_grouped,
|
||||||
|
refresh_main_index,
|
||||||
|
)
|
||||||
from contract_specs import calc_position_metrics
|
from contract_specs import calc_position_metrics
|
||||||
from fee_specs import (
|
from fee_specs import (
|
||||||
calc_fee_breakdown,
|
calc_fee_breakdown,
|
||||||
@@ -742,6 +748,26 @@ def api_symbols_mains():
|
|||||||
return jsonify(list_main_contracts_grouped())
|
return jsonify(list_main_contracts_grouped())
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/symbols/recommended")
|
||||||
|
@login_required
|
||||||
|
def api_symbols_recommended():
|
||||||
|
"""品种下拉:仅展示当前资金下推荐的品种(与下方品种推荐表一致)。"""
|
||||||
|
from recommend_store import recommend_payload
|
||||||
|
from trading_context import get_account_capital, get_max_margin_pct
|
||||||
|
|
||||||
|
conn = get_db()
|
||||||
|
try:
|
||||||
|
capital = get_account_capital(conn, get_setting)
|
||||||
|
payload = recommend_payload(
|
||||||
|
conn,
|
||||||
|
live_capital=capital,
|
||||||
|
max_margin_pct=get_max_margin_pct(get_setting),
|
||||||
|
)
|
||||||
|
return jsonify(list_recommended_symbols_grouped(payload.get("rows") or []))
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/key_prices")
|
@app.route("/api/key_prices")
|
||||||
@login_required
|
@login_required
|
||||||
def api_key_prices():
|
def api_key_prices():
|
||||||
|
|||||||
+14
-4
@@ -1,8 +1,12 @@
|
|||||||
(function () {
|
(function () {
|
||||||
function formatSub(item) {
|
function formatSub(item) {
|
||||||
return '同花顺 ' + item.ths_code +
|
var sub = '同花顺 ' + item.ths_code +
|
||||||
(item.market_code ? ' · ' + item.market_code : '') +
|
(item.market_code ? ' · ' + item.market_code : '') +
|
||||||
' · ' + (item.exchange || '');
|
' · ' + (item.exchange || '');
|
||||||
|
if (item.max_lots != null && item.max_lots > 0) {
|
||||||
|
sub += ' · 最大 ' + item.max_lots + ' 手';
|
||||||
|
}
|
||||||
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatInputLabel(item) {
|
function formatInputLabel(item) {
|
||||||
@@ -132,17 +136,23 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mainsLoading) {
|
if (mainsLoading) {
|
||||||
dropdown.innerHTML = '<div class="symbol-option">正在识别主力合约…</div>';
|
dropdown.innerHTML = '<div class="symbol-option">正在加载推荐品种…</div>';
|
||||||
dropdown.classList.add('show');
|
dropdown.classList.add('show');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mainsLoading = true;
|
mainsLoading = true;
|
||||||
dropdown.innerHTML = '<div class="symbol-option">正在识别主力合约…</div>';
|
dropdown.innerHTML = '<div class="symbol-option">正在加载推荐品种…</div>';
|
||||||
dropdown.classList.add('show');
|
dropdown.classList.add('show');
|
||||||
fetch('/api/symbols/mains')
|
fetch('/api/symbols/recommended')
|
||||||
.then(function (r) { return r.json(); })
|
.then(function (r) { return r.json(); })
|
||||||
.then(function (groups) {
|
.then(function (groups) {
|
||||||
mainsCache = groups;
|
mainsCache = groups;
|
||||||
|
if (!groups.length) {
|
||||||
|
dropdown.innerHTML =
|
||||||
|
'<div class="symbol-option">当前资金下暂无推荐品种,可输入合约代码搜索</div>';
|
||||||
|
dropdown.classList.add('show');
|
||||||
|
return;
|
||||||
|
}
|
||||||
showMarketMains(filterQ, onEmpty);
|
showMarketMains(filterQ, onEmpty);
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
|
|||||||
+57
@@ -419,6 +419,63 @@ def search_symbols(query: str) -> list:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
_THS_TO_PRODUCT = {p["ths"]: p for p in PRODUCTS}
|
||||||
|
for _p in PRODUCTS:
|
||||||
|
_THS_TO_PRODUCT.setdefault(_p["ths"].lower(), _p)
|
||||||
|
|
||||||
|
|
||||||
|
def _product_for_ths(ths: str) -> Optional[dict]:
|
||||||
|
key = (ths or "").strip()
|
||||||
|
if not key:
|
||||||
|
return None
|
||||||
|
return _THS_TO_PRODUCT.get(key) or _THS_TO_PRODUCT.get(key.lower())
|
||||||
|
|
||||||
|
|
||||||
|
def _main_for_product(product: dict) -> Optional[dict]:
|
||||||
|
with _main_index_lock:
|
||||||
|
index = dict(_main_index)
|
||||||
|
main = index.get(product["sina"])
|
||||||
|
if not main:
|
||||||
|
resolved = resolve_main_contract(product)
|
||||||
|
if resolved:
|
||||||
|
main = _enrich_item(resolved)
|
||||||
|
return main
|
||||||
|
|
||||||
|
|
||||||
|
def list_recommended_symbols_grouped(recommend_rows: list[dict]) -> list[dict]:
|
||||||
|
"""按交易所分类返回推荐品种对应的主力合约(品种选择下拉用)。"""
|
||||||
|
if not recommend_rows:
|
||||||
|
return []
|
||||||
|
|
||||||
|
buckets: dict[str, list] = defaultdict(list)
|
||||||
|
seen: set[str] = set()
|
||||||
|
for row in recommend_rows:
|
||||||
|
if row.get("status") not in ("ok", "margin_ok"):
|
||||||
|
continue
|
||||||
|
ths_key = (row.get("ths") or "").strip()
|
||||||
|
if not ths_key or ths_key in seen:
|
||||||
|
continue
|
||||||
|
product = _product_for_ths(ths_key)
|
||||||
|
if not product:
|
||||||
|
continue
|
||||||
|
seen.add(ths_key)
|
||||||
|
main = _main_for_product(product)
|
||||||
|
if not main:
|
||||||
|
continue
|
||||||
|
item = dict(main)
|
||||||
|
max_lots = row.get("max_lots")
|
||||||
|
if max_lots is not None:
|
||||||
|
item["max_lots"] = max_lots
|
||||||
|
buckets[product["exchange"]].append(_enrich_item(item))
|
||||||
|
|
||||||
|
groups: list[dict] = []
|
||||||
|
for cat in EXCHANGE_ORDER:
|
||||||
|
items = buckets.get(cat)
|
||||||
|
if items:
|
||||||
|
groups.append({"category": cat, "items": items})
|
||||||
|
return groups
|
||||||
|
|
||||||
|
|
||||||
def list_main_contracts_grouped() -> list[dict]:
|
def list_main_contracts_grouped() -> list[dict]:
|
||||||
"""按交易所分类返回全部品种主力合约(行情页下拉用)。"""
|
"""按交易所分类返回全部品种主力合约(行情页下拉用)。"""
|
||||||
with _main_index_lock:
|
with _main_index_lock:
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form action="{{ url_for('add_key') }}" method="post" class="form-compact">
|
<form action="{{ url_for('add_key') }}" method="post" class="form-compact">
|
||||||
<div class="form-line line-3">
|
<div class="form-line line-3">
|
||||||
<div class="symbol-wrap">
|
<div class="symbol-wrap symbol-mains">
|
||||||
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required>
|
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required>
|
||||||
<input type="hidden" name="symbol" required>
|
<input type="hidden" name="symbol" required>
|
||||||
<input type="hidden" name="symbol_name">
|
<input type="hidden" name="symbol_name">
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<p class="hint" style="margin-bottom:.75rem">开盘前制定,当日有效;请先选择<strong>主力合约</strong>,下方为进行中计划。</p>
|
<p class="hint" style="margin-bottom:.75rem">开盘前制定,当日有效;请先选择<strong>主力合约</strong>,下方为进行中计划。</p>
|
||||||
<form action="{{ url_for('add_plan') }}" method="post" class="form-compact">
|
<form action="{{ url_for('add_plan') }}" method="post" class="form-compact">
|
||||||
<div class="form-line line-plan-1">
|
<div class="form-line line-plan-1">
|
||||||
<div class="symbol-wrap">
|
<div class="symbol-wrap symbol-mains">
|
||||||
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required>
|
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required>
|
||||||
<input type="hidden" name="symbol" required>
|
<input type="hidden" name="symbol" required>
|
||||||
<input type="hidden" name="symbol_name">
|
<input type="hidden" name="symbol_name">
|
||||||
|
|||||||
Reference in New Issue
Block a user