mirror of
https://ghfast.top/https://github.com/aeroxw/easy-tdx.git
synced 2026-09-12 14:34:15 +08:00
fix: 图表x轴显示完整日期 + 组合回测取数翻页
图表日期格式(4 个组件):KlineChart/EquityChart/CompareChart/ PortfolioCompareChart 的 xAxis 加 axisLabel.formatter,强制显示原始 日期字符串。ECharts 默认把 ISO 日期当时间对象解析,丢失年份只显示 月-日+00:00。 组合回测取数翻页(_fetch_portfolio_bars):固定 count=800 改为翻页循环 (与前端 fetchBars 同逻辑),拼接后 sort_values 排序再过滤。修复组合 回测图表从 800 根最早点(2023-03-14)开始、缺失早期数据的问题。 实测组合回测 2019-01-07 起:净值 1814 点(不再是 800),首日正确。
This commit is contained in:
@@ -322,30 +322,49 @@ async def _fetch_portfolio_bars(
|
||||
) -> list[Any]:
|
||||
"""逐个标的取 K 线并组装 StockData 列表(async,必须在 event loop 内调用)。
|
||||
|
||||
单个标的取数失败时跳过(不中断整个组合),全部失败返回空列表。
|
||||
当 start_date 超出单次 800 根覆盖范围时,自动翻页拉取(与前端 fetchBars
|
||||
同逻辑)。单个标的取数失败时跳过(不中断整个组合),全部失败返回空列表。
|
||||
"""
|
||||
from easy_tdx.backtest.portfolio_engine import StockData
|
||||
from easy_tdx.web.convert import category_from_str, market_from_str
|
||||
|
||||
max_pages = 10 # 翻页上限:10 × 800 = 8000 根
|
||||
stock_data_list: list[StockData] = []
|
||||
for symbol in stocks:
|
||||
market_str, code = symbol.split(":", 1)
|
||||
try:
|
||||
df = await client.get_security_bars(
|
||||
market_from_str(market_str),
|
||||
code,
|
||||
category_from_str(category),
|
||||
0,
|
||||
800, # 固定拉满,前端按日期过滤
|
||||
)
|
||||
except Exception:
|
||||
continue # 单个标的失败不中断
|
||||
if len(df) < 2:
|
||||
frames: list[pd.DataFrame] = []
|
||||
for page in range(max_pages):
|
||||
try:
|
||||
page_df = await client.get_security_bars(
|
||||
market_from_str(market_str),
|
||||
code,
|
||||
category_from_str(category),
|
||||
page * 800,
|
||||
800,
|
||||
)
|
||||
except Exception:
|
||||
break # 单页失败则停止该标的的翻页
|
||||
if len(page_df) == 0:
|
||||
break
|
||||
frames.append(page_df)
|
||||
# 已覆盖到 start_date(本页最早一根 ≤ start_date)则停止
|
||||
if start_date and len(page_df) > 0:
|
||||
dt_col = "datetime" if "datetime" in page_df.columns else "date"
|
||||
oldest = str(page_df[dt_col].iloc[-1])[:10]
|
||||
if oldest <= start_date:
|
||||
break
|
||||
if len(page_df) < 800:
|
||||
break # 数据起点
|
||||
|
||||
if not frames:
|
||||
continue
|
||||
df = pd.concat(frames, ignore_index=True)
|
||||
# 列名归一化:日线返回 date,分钟线返回 datetime
|
||||
if "datetime" not in df.columns and "date" in df.columns:
|
||||
df = df.copy()
|
||||
df["datetime"] = df["date"]
|
||||
# 翻页拼接后按时间正序排序(页间逆序)
|
||||
df = df.sort_values("datetime").reset_index(drop=True)
|
||||
# 日期范围过滤
|
||||
if start_date or end_date:
|
||||
dt_str = df["datetime"].astype(str).str.slice(0, 10)
|
||||
|
||||
@@ -41,7 +41,12 @@ function buildOption(): echarts.EChartsCoreOption {
|
||||
},
|
||||
legend: { top: 0, data: seriesData.map((s) => s.name) },
|
||||
grid: { left: '8%', right: '5%', top: 30, bottom: 50 },
|
||||
xAxis: { type: 'category', data: allDates, boundaryGap: false },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: allDates,
|
||||
boundaryGap: false,
|
||||
axisLabel: { formatter: (v: string) => v },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
scale: true,
|
||||
|
||||
@@ -41,6 +41,7 @@ function buildOption(): echarts.EChartsCoreOption {
|
||||
data: dates,
|
||||
boundaryGap: false,
|
||||
axisLine: { onZero: false },
|
||||
axisLabel: { formatter: (v: string) => v },
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
|
||||
@@ -82,6 +82,11 @@ function buildOption(): echarts.EChartsCoreOption {
|
||||
boundaryGap: true,
|
||||
axisLine: { onZero: false },
|
||||
splitLine: { show: false },
|
||||
// 强制显示原始日期字符串,避免 ECharts 把 "2024-01-15" 当时间对象
|
||||
// 解析后默认按 {MM}-{dd} {HH}:{mm} 显示(丢年份)
|
||||
axisLabel: {
|
||||
formatter: (v: string) => v,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
scale: true,
|
||||
|
||||
@@ -50,7 +50,12 @@ function buildOption(
|
||||
},
|
||||
legend: { top: 0, data: series.map((s) => s.name) },
|
||||
grid: { left: '8%', right: '5%', top: 30, bottom: 50 },
|
||||
xAxis: { type: 'category', data: allDates, boundaryGap: false },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: allDates,
|
||||
boundaryGap: false,
|
||||
axisLabel: { formatter: (v: string) => v },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
scale: true,
|
||||
|
||||
Reference in New Issue
Block a user