update pay ui

This commit is contained in:
2025-12-15 18:08:06 +08:00
parent 17479a7362
commit 72836fa5d4

189
app.py
View File

@@ -5514,7 +5514,7 @@ def remove_from_watchlist(stock_code):
@app.route('/api/account/watchlist/realtime', methods=['GET']) @app.route('/api/account/watchlist/realtime', methods=['GET'])
def get_watchlist_realtime(): def get_watchlist_realtime():
"""获取自选股实时行情数据(基于分钟线)""" """获取自选股实时行情数据(基于分钟线)- 优化为批量查询"""
try: try:
if 'user_id' not in session: if 'user_id' not in session:
return jsonify({'success': False, 'error': '未登录'}), 401 return jsonify({'success': False, 'error': '未登录'}), 401
@@ -5524,103 +5524,127 @@ def get_watchlist_realtime():
if not watchlist: if not watchlist:
return jsonify({'success': True, 'data': []}) return jsonify({'success': True, 'data': []})
# 获取股票代码列表 # 获取股票代码列表并标准化
stock_codes = [] code_mapping = {} # code6 -> full_code 映射
full_codes = []
for item in watchlist: for item in watchlist:
code6, _ = _normalize_stock_input(item.stock_code) code6, _ = _normalize_stock_input(item.stock_code)
# 统一内部查询代码
normalized = code6 or str(item.stock_code).strip().upper() normalized = code6 or str(item.stock_code).strip().upper()
stock_codes.append(normalized)
# 使用现有的分钟线接口获取最新行情 # 转换为带后缀的完整代码
client = get_clickhouse_client() if '.' in normalized:
quotes_data = {} full_code = normalized
elif normalized.startswith('6'):
# 获取最新交易日 full_code = f"{normalized}.SH"
today = datetime.now().date() elif normalized.startswith(('8', '9', '4')):
full_code = f"{normalized}.BJ"
# 获取每只股票的最新价格
for code in stock_codes:
raw_code = str(code).strip().upper()
if '.' in raw_code:
stock_code_full = raw_code
elif raw_code.startswith('6'):
stock_code_full = f"{raw_code}.SH" # 上海
elif raw_code.startswith(('8', '9', '4')):
stock_code_full = f"{raw_code}.BJ" # 北交所
else: else:
stock_code_full = f"{raw_code}.SZ" # 深圳 full_code = f"{normalized}.SZ"
# 获取最新分钟线数据先查近7天若无数据再兜底倒序取最近一条 code_mapping[normalized] = full_code
query = """ full_codes.append(full_code)
SELECT
close, timestamp, high, low, volume, amt
FROM stock_minute
WHERE code = %(code)s
AND timestamp >= %(start)s
ORDER BY timestamp DESC
LIMIT 1 \
"""
# 获取最近7天的分钟数据 if not full_codes:
start_date = today - timedelta(days=7) return jsonify({'success': True, 'data': []})
result = client.execute(query, { # 使用批量查询获取最新行情(单次查询)
'code': stock_code_full, client = get_clickhouse_client()
'start': datetime.combine(start_date, dt_time(9, 30)) today = datetime.now().date()
}) start_date = today - timedelta(days=7)
# 若近7天无数据兜底直接取最近一条 # 批量查询:获取每只股票的最新一条分钟数据
if not result: batch_query = """
fallback_query = """ WITH latest AS (
SELECT SELECT
close, timestamp, high, low, volume, amt code,
FROM stock_minute close,
WHERE code = %(code)s timestamp,
ORDER BY timestamp DESC high,
LIMIT 1 \ low,
""" volume,
result = client.execute(fallback_query, {'code': stock_code_full}) amt,
ROW_NUMBER() OVER (PARTITION BY code ORDER BY timestamp DESC) as rn
FROM stock_minute
WHERE code IN %(codes)s
AND timestamp >= %(start)s
)
SELECT code, close, timestamp, high, low, volume, amt
FROM latest
WHERE rn = 1
"""
if result: result = client.execute(batch_query, {
latest_data = result[0] 'codes': full_codes,
latest_ts = latest_data[1] 'start': datetime.combine(start_date, dt_time(9, 30))
})
# 获取该bar所属交易日前一个交易日的收盘价 # 构建最新价格映射
prev_close_query = """ latest_data_map = {}
SELECT close for row in result:
FROM stock_minute code, close, ts, high, low, volume, amt = row
WHERE code = %(code)s latest_data_map[code] = {
AND timestamp \ 'close': float(close),
< %(start)s 'timestamp': ts,
ORDER BY timestamp DESC 'high': float(high),
LIMIT 1 \ 'low': float(low),
""" 'volume': int(volume),
'amount': float(amt)
}
prev_result = client.execute(prev_close_query, { # 批量查询前收盘价(使用 ea_trade 表,更准确)
'code': stock_code_full, prev_close_map = {}
'start': datetime.combine(latest_ts.date(), dt_time(9, 30)) if latest_data_map:
}) # 获取前一交易日
prev_trading_day = None
for td in reversed(trading_days):
if td < today:
prev_trading_day = td
break
prev_close = float(prev_result[0][0]) if prev_result else float(latest_data[0]) if prev_trading_day:
base_codes = [code.split('.')[0] for code in full_codes]
prev_day_str = prev_trading_day.strftime('%Y%m%d')
# 计算涨跌幅 with engine.connect() as conn:
change = float(latest_data[0]) - prev_close placeholders = ','.join([f':code{i}' for i in range(len(base_codes))])
change_percent = (change / prev_close * 100) if prev_close > 0 else 0.0 params = {f'code{i}': code for i, code in enumerate(base_codes)}
params['trade_date'] = prev_day_str
quotes_data[code] = { prev_result = conn.execute(text(f"""
'price': float(latest_data[0]), SELECT SECCODE, F007N as close_price
'prev_close': float(prev_close), FROM ea_trade
'change': float(change), WHERE SECCODE IN ({placeholders})
'change_percent': float(change_percent), AND TRADEDATE = :trade_date
'high': float(latest_data[2]), """), params).fetchall()
'low': float(latest_data[3]),
'volume': int(latest_data[4]), for row in prev_result:
'amount': float(latest_data[5]), base_code, close_price = row[0], row[1]
'update_time': latest_ts.strftime('%H:%M:%S') if close_price:
} prev_close_map[base_code] = float(close_price)
# 构建响应数据 # 构建响应数据
quotes_data = {}
for code6, full_code in code_mapping.items():
latest = latest_data_map.get(full_code)
if latest:
base_code = full_code.split('.')[0]
prev_close = prev_close_map.get(base_code, latest['close'])
change = latest['close'] - prev_close
change_percent = (change / prev_close * 100) if prev_close > 0 else 0.0
quotes_data[code6] = {
'price': latest['close'],
'prev_close': prev_close,
'change': change,
'change_percent': change_percent,
'high': latest['high'],
'low': latest['low'],
'volume': latest['volume'],
'amount': latest['amount'],
'update_time': latest['timestamp'].strftime('%H:%M:%S')
}
response_data = [] response_data = []
for item in watchlist: for item in watchlist:
code6, _ = _normalize_stock_input(item.stock_code) code6, _ = _normalize_stock_input(item.stock_code)
@@ -5637,7 +5661,6 @@ def get_watchlist_realtime():
'volume': quote.get('volume', 0), 'volume': quote.get('volume', 0),
'amount': quote.get('amount', 0), 'amount': quote.get('amount', 0),
'update_time': quote.get('update_time', ''), 'update_time': quote.get('update_time', ''),
# industry 字段在 Watchlist 模型中不存在,先不返回该字段
}) })
return jsonify({ return jsonify({
@@ -5647,6 +5670,8 @@ def get_watchlist_realtime():
except Exception as e: except Exception as e:
print(f"获取实时行情失败: {str(e)}") print(f"获取实时行情失败: {str(e)}")
import traceback
traceback.print_exc()
return jsonify({'success': False, 'error': '获取实时行情失败'}), 500 return jsonify({'success': False, 'error': '获取实时行情失败'}), 500