feat(web): 增量自动刷新、结构区修复与默认指标/周期
自动刷新常态只拉 recent 尾部 K,每 1 分钟全量重算缠论;修复结构区缓存导入;默认指标/4h·1h·15m/近30天;同步 ECR-009 screener 相关改动。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
from flask import Blueprint, jsonify, request
|
||||
from services.runtime import * # noqa: F403
|
||||
from services import runtime as R
|
||||
# import * 不会带出下划线私有名;结构区缓存需显式导入
|
||||
from services.runtime.state import _zone_cache
|
||||
from services.runtime.timeframes import _zone_cache_ttl
|
||||
|
||||
bp = Blueprint("analyze", __name__)
|
||||
|
||||
@@ -785,3 +788,77 @@ def analyze():
|
||||
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
def _serialize_kl_tail(df, limit: int):
|
||||
"""只序列化最近 limit 根,供自动刷新增量合并。"""
|
||||
if df is None or getattr(df, "empty", True):
|
||||
return []
|
||||
tail = df.tail(limit)
|
||||
clean = clean_dataframe_for_json(tail)
|
||||
records = clean.to_dict("records")
|
||||
for row in records:
|
||||
d = row.get("date")
|
||||
if hasattr(d, "isoformat"):
|
||||
try:
|
||||
row["date"] = d.isoformat()
|
||||
except Exception:
|
||||
row["date"] = str(d)
|
||||
# timestamp 统一成 int ms,便于前端按 key 合并
|
||||
ts = row.get("timestamp")
|
||||
if ts is not None:
|
||||
try:
|
||||
row["timestamp"] = int(ts)
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
elif hasattr(d, "timestamp"):
|
||||
try:
|
||||
row["timestamp"] = int(d.timestamp() * 1000)
|
||||
except Exception:
|
||||
pass
|
||||
return records
|
||||
|
||||
|
||||
@bp.route("/api/klines/recent")
|
||||
def klines_recent():
|
||||
"""轻量拉取最近 N 根 K 线(不做缠论/威科夫),供主站自动刷新增量。"""
|
||||
symbol = (request.args.get("symbol") or "").strip()
|
||||
if not symbol:
|
||||
return jsonify({"error": "交易对不能为空"}), 400
|
||||
|
||||
timeframe = request.args.get("timeframe", "5m")
|
||||
try:
|
||||
limit = int(request.args.get("limit", 2))
|
||||
except (TypeError, ValueError):
|
||||
limit = 2
|
||||
limit = max(1, min(limit, 20))
|
||||
|
||||
element_timeframe = request.args.get("element_timeframe") or None
|
||||
sub_sub_timeframe = request.args.get("sub_sub_timeframe") or None
|
||||
|
||||
# 只取尾部:不传 start/end,避免全量窗口回拉
|
||||
df = get_kl_data(symbol, timeframe, limit=limit)
|
||||
if df is None:
|
||||
return jsonify({"error": "获取数据失败"}), 502
|
||||
if len(df) == 0:
|
||||
return jsonify({"error": "没有数据"}), 404
|
||||
|
||||
result = {
|
||||
"partial": True,
|
||||
"symbol": symbol,
|
||||
"timeframe": timeframe,
|
||||
"limit": limit,
|
||||
"kline_data": _serialize_kl_tail(df, limit),
|
||||
}
|
||||
|
||||
if element_timeframe:
|
||||
edf = get_kl_data(symbol, element_timeframe, limit=limit)
|
||||
result["element_timeframe"] = element_timeframe
|
||||
result["element_kline_data"] = _serialize_kl_tail(edf, limit) if edf is not None else []
|
||||
|
||||
if sub_sub_timeframe:
|
||||
sdf = get_kl_data(symbol, sub_sub_timeframe, limit=limit)
|
||||
result["sub_sub_timeframe"] = sub_sub_timeframe
|
||||
result["sub_sub_kline_data"] = _serialize_kl_tail(sdf, limit) if sdf is not None else []
|
||||
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user