Qlib 回测结果可视化分析:Analysis 图形化报告与模型评估实战

Qlib 回测结果可视化分析:Analysis 图形化报告与模型评估实战
Qlib 回测结果可视化分析Analysis 图形化报告与模型评估实战【免费下载链接】qlibQlib is an AI-oriented Quant investment platform that aims to use AI tech to empower Quant Research, from exploring ideas to implementing productions. Qlib supports diverse ML modeling paradigms, including supervised learning, market dynamics modeling, and RL, and is now equipped with https://github.com/microsoft/RD-Agent to automate RD process.项目地址: https://gitcode.com/GitHub_Trending/qli/qlib在量化投研流程中回测产出的不只是几条数字指标——累积收益、最大回撤、IC 序列等更需要一套图形化手段来直观检验策略与模型的合理性。Qlib 的Analysis模块qlib.contrib.report正是为此设计它面向 Intraday Trading 场景为投资组合评估和模型打分能力评估提供了一组开箱即用的图形化报告。读完本篇你将掌握 Qlib 全部 6 类图形报告analysis_position下的 5 类与analysis_model下的 1 类的调用方式、输入数据要求、每张图每个子图的业务含义以及其背后的源码实现逻辑从而能够独立完成一次完整的回测结果与模型表现诊断。一、Analysis 模块总览支持的图形报告清单Analysis模块覆盖两大类报告analysis_position组合层面依赖真实回测结果report_graphscore_ic_graphcumulative_return_graphrisk_analysis_graphrank_label_graphanalysis_model模型层面依赖预测分数与 labelmodel_performance_graph所有报告函数都注册在包级常量GRAPH_NAME_LIST中可以直接通过import qlib.contrib.report as qcr后打印确认 import qlib.contrib.report as qcr print(qcr.GRAPH_NAME_LIST) [analysis_position.report_graph, analysis_position.score_ic_graph, analysis_position.cumulative_return_graph, analysis_position.risk_analysis_graph, analysis_position.rank_label_graph, analysis_model.model_performance_graph]该常量定义在 qlib/contrib/report/init.py与上述清单完全一致。官方文档也提示每个函数的完整参数说明可直接通过help(qcr.analysis_position.report_graph)等查看源码中每个函数都附带了可运行的 docstring 示例。一个重要的计算约定累积指标用“加和”而非“复利”Qlib 中所有累积型利润指标return、max drawdown 等都是通过summation加和计算的而不是逐日相乘的几何累积。源码注释明确解释了这一设计动机Qlib tries to cumulate returns by summation instead of product to avoid the cumulated curve being skewed exponentially.这一原则体现在 qlib/contrib/evaluate.py 的risk_analysis函数中modesum默认时annualized_return mean * N、max_drawdown (r.cumsum() - r.cumsum().cummax()).min()即基于简单收益率序列的累加曲线计算回撤modeproduct则提供几何累积口径作为对照。因此阅读报告图形时纵轴的累积收益应当理解为“线性累加收益”而非复利净值曲线。二、analysis_position.report_graph组合绩效总览图report_graph是最核心的组合级报告输入是回测得到的report_normal_df。API 与数据要求函数签名与输入约束源码 docstring 明确要求report_df.index.name必须是datedf.columns必须包含return、turnover、cost、bench四列。return cost bench turnover date 2017-01-04 0.003421 0.000864 0.011693 0.576325 2017-01-05 0.000508 0.000447 0.000721 0.227882 2017-01-06 -0.003321 0.000212 -0.004322 0.102765 2017-01-09 0.006753 0.000212 0.006874 0.105864 2017-01-10 -0.000416 0.000440 -0.003350 0.208396完整调用示例源自函数 docstring典型流程是初始化 qlib → 构造TopkDropoutStrategy与SimulatorExecutor→ 执行backtest→ 取出portfolio_metric_dict中对应频率的report_normal_df→ 绘图import qlib import pandas as pd from qlib.utils.time import Freq from qlib.utils import flatten_dict from qlib.backtest import backtest, executor from qlib.contrib.evaluate import risk_analysis from qlib.contrib.strategy import TopkDropoutStrategy # init qlib qlib.init(provider_uriqlib data dir) CSI300_BENCH SH000300 FREQ day STRATEGY_CONFIG { topk: 50, n_drop: 5, # pred_score, pd.Series signal: pred_score, } EXECUTOR_CONFIG { time_per_step: day, generate_portfolio_metrics: True, } backtest_config { start_time: 2017-01-01, end_time: 2020-08-01, account: 100000000, benchmark: CSI300_BENCH, exchange_kwargs: { freq: FREQ, limit_threshold: 0.095, deal_price: close, open_cost: 0.0005, close_cost: 0.0015, min_cost: 5, }, } # strategy object strategy_obj TopkDropoutStrategy(**STRATEGY_CONFIG) # executor object executor_obj executor.SimulatorExecutor(**EXECUTOR_CONFIG) # backtest portfolio_metric_dict, indicator_dict backtest(executorexecutor_obj, strategystrategy_obj, **backtest_config) analysis_freq {0}{1}.format(*Freq.parse(FREQ)) # backtest info report_normal_df, positions_normal portfolio_metric_dict.get(analysis_freq) qcr.analysis_position.report_graph(report_normal_df)其中exchange_kwargs直接控制图中“含成本”与“不含成本”两条曲线的差异来源open_cost0.0005、close_cost0.0015、min_cost5决定了cost列的取值。图形各子轴含义横轴为交易日Trading day7 个纵排子图依次为子图含义cum bench基准累计收益序列cum return wo cost组合累计收益不含成本cum return w cost组合累计收益含成本return wo mdd不含成本累计收益的最大回撤序列return w cost mdd含成本累计收益的最大回撤序列cum ex return wo cost相对基准的超额累计收益 CAR不含成本cum ex return w cost相对基准的超额累计收益 CAR含成本turnover换手率序列cum ex return wo cost mddCAR不含成本的回撤序列cum ex return w cost mddCAR含成本的回撤序列图中还有两处阴影矩形上半部分阴影标出cum return wo cost对应的最大回撤区间下半部分阴影标出cum ex return wo cost对应的最大回撤区间。源码实现要点qlib/contrib/report/analysis_position/report.py 中_calculate_report_data对输入的 4 列原始数据做cumsum()得到全部累积序列例如cum_return_w_cost (df[return] - df[cost]).cumsum()、cum_ex_return_wo_cost (df[return] - df[bench]).cumsum()与上文“加和口径”完全对应回撤由_calculate_mdd(series)实现即series - series.cummax()因此回撤序列恒为负值或零_calculate_maximum通过idxmin()找到回撤最低点作为区间终点再在其之前找cumsum峰值作为起点生成两处阴影矩形的x0/x1最终通过SubplotsGraph定义于 qlib/contrib/report/graph.py以 7 行 1 列、shared_xaxesTrue、行宽row_width[1, 1, 1, 3, 1, 1, 3]的 plotly 子图布局输出show_notebookTrue时直接在 notebook 内渲染为False时返回plotly.graph_objs.Figure列表便于二次保存或嵌入报告。三、analysis_position.score_ic_graph预测分数与实际收益的每日相关性score_ic_graph用于检验模型预测分数prediction score与真实收益label之间的逐日相关性是判断“打分能力是否有效”的第一道图形化关卡。API 与数据要求输入pred_label要求index 为pd.MultiIndexindex 名为[instrument, datetime]列名为[score, label]。instrument datetime score label SH600004 2017-12-11 -0.013502 -0.013502 2017-12-12 -0.072367 -0.072367 2017-12-13 -0.068605 -0.068605 2017-12-14 0.012440 0.012440 2017-12-15 -0.102778 -0.102778调用示例源自 docstring先从 Qlib 数据层取 label文档示例中的 label 公式为Ref($close, -2)/Ref($close, -1)-1即 T 收盘到 T1 收盘的收益特征公式可参考 docs/component/data.rst 的 Feature 一节再与模型预测拼接后绘图from qlib.data import D from qlib.contrib.report import analysis_position pred_df_dates pred_df.index.get_level_values(leveldatetime) features_df D.features(D.instruments(csi500), [Ref($close, -2)/Ref($close, -1)-1], pred_df_dates.min(), pred_df_dates.max()) features_df.columns [label] pred_label pd.concat([features_df, pred], axis1, sortTrue).reindex(features_df.index) analysis_position.score_ic_graph(pred_label)图形含义与实现图形有两条曲线iclabel与score的逐日Pearson 相关系数序列rank_ic两者的逐日Spearman 秩相关系数序列。源码 qlib/contrib/report/analysis_position/score_ic.py 中_get_score_ic先dropna(howany)剔除缺失再groupby(leveldatetime)逐日计算x[label].corr(x[score])与methodspearman的秩相关最终用ScatterGraphlinesmarkers 模式绘制并通过guess_plotly_rangebreaks隐藏周末与节假日造成的横轴空隙。四、analysis_position.cumulative_return_graph买入/卖出/持有分拆的收益分析cumulative_return_graph从“交易行为”角度拆解组合收益把每天的持仓按持有hold、买入buy、卖出sell三类分拆分别统计加权平均 label 并逐日累加帮助判断收益究竟来自新买入的票、还是继续持有的票卖出回避行为是否有效。API 与数据要求positionqlib.backtest.backtest返回的 positions 字典report_normal含bench列的 report_normal 数据用于逐日对齐基准计算超额label_dataD.features的结果index 为[instrument, datetime]列名为label。T 日的 label 必须是 T 到 T1 的收益变化推荐用收盘价构造如D.features(D.instruments(csi500), [Ref($close, -1)/$close-1])start_date/end_date可选的时间切片show_notebook控制显示或返回 figure 列表。图形含义横轴交易日上方子图 Y 轴(((Ref($close, -1)/$close - 1) * weight).sum() / weight.sum()).cumsum()即按权重加权平均的 label 累加曲线下方子图 Y 轴当日该类操作的权重合计在sell图中y 0表示卖出行为带来了正贡献少赚了被卖掉的跌票 / 规避了下跌其他图中y 0表示正贡献buy_minus_sell图中底部 weight 子图的 Y 值为buy_weight sell_weight每张图右侧直方图中红色竖线表示该序列的均值。源码实现要点qlib/contrib/report/analysis_position/cumulative_return.py 的核心逻辑调用 qlib/contrib/report/analysis_position/parse_position.py 的get_position_data将 position 字典解析为长表含 amount/cash/count/price/status/weight 列并与 label 对齐注意_add_bench_to_position中bench.shift(-1)买卖当日看的是次日的涨跌因为执行上 T 日交易、T1 日才产生收益_calculate_label_rank中status的语义是0-hold、1-buy、-1-sell买入/卖出通过对比 T 日与 T-1 日持仓集合的差集推导T 存在且 T-1 不存在 → buy反之 → sell逐日对三类股票分别计算sum(label * weight) / sum(weight)得到加权平均收益再对 buy/sell/hold/buy_minus_sell 四列分别cumsum()最终以 2×2 子图上方曲线 下方权重 右侧直方图红线为均值输出 4 张图。五、analysis_position.risk_analysis_graph风险指标总览与月度分解risk_analysis_graph把 qlib/contrib/evaluate.py 中risk_analysis计算的统计指标图形化分为“总体柱状图 4 张月度时序图”。API 与数据要求analysis_df分析数据index 为pd.MultiIndex列名为risk典型结构源自 docstringrisk excess_return_without_cost mean 0.000692 std 0.005374 annualized_return 0.174495 information_ratio 2.045576 max_drawdown -0.079103 excess_return_with_cost mean 0.000499 std 0.005372 annualized_return 0.125625 information_ratio 1.473152 max_drawdown -0.088263report_normal_df与report_graph相同的数据结构index.name 为 date列含 return/turnover/cost/benchreport_long_short_df可选列含 long/short/long_short当前实现中该分支已被注释停用实际只使用report_normal_dfshow_notebook默认 True在 notebook 中显示否则返回 figure 列表。调用示例源自 docstring注意risk_analysis需要传入freqanalysis_freqanalysis dict() analysis[excess_return_without_cost] risk_analysis( report_normal_df[return] - report_normal_df[bench], freqanalysis_freq ) analysis[excess_return_with_cost] risk_analysis( report_normal_df[return] - report_normal_df[bench] - report_normal_df[cost], freqanalysis_freq ) analysis_df pd.concat(analysis) # type: pd.DataFrame analysis_position.risk_analysis_graph(analysis_df, report_normal_df)图形含义总体柱状图4 列 × 2 系列excess_return_without_cost与excess_return_with_coststdCAR超额累计收益的标准差annualized_returnCAR 的年化收益率information_ratio信息比率IR衡量单位主动风险换取的超额收益max_drawdownCAR 的最大回撤。月度时序图横轴为按月分组的交易日每月两条曲线分别为含/不含成本annualized_return图月度 CAR 的年化收益序列max_drawdown图月度 CAR 的最大回撤序列information_ratio图月度 IR 序列std图月度 CAR 的标准差序列。源码实现要点qlib/contrib/report/analysis_position/risk_analysis.py 中总体图由_get_risk_analysis_figure生成1 行 4 列的BarGraph子图两个系列无成本/含成本并排比较可直观看到交易成本对年化收益与 IR 的侵蚀幅度月度图由_get_monthly_risk_analysis_figure生成先groupby([year, month])按月聚合当月交易日少于 3 天会被跳过源码注释说明这是为了避免图中出现断点再对annualized_return、max_drawdown、information_ratio、std四个特征逐一用_get_monthly_analysis_with_feature透视出“日期 × 系列”矩阵并绘制折线risk_analysis的年化缩放系数按频率确定日频 238、周频 50、月频 12、分钟频 240×238见 qlib/contrib/evaluate.py 的cal_risk_analysis_scalerN与freq至少提供一个N存在时freq被忽略。六、analysis_position.rank_label_graph买/卖/持股票的 label 排名分析rank_label_graph回答一个问题策略当天买入、卖出、继续持有的股票在全体股票 label 排名中处于什么位置API 与数据要求positionqlib.backtest.backtest返回的 positions 数据label_dataD.features结果index 为[instrument, datetime]列名为labellabel T 为 T 到 T1 的变化推荐D.features(D.instruments(csi500), [Ref($close, -1)/$close-1])start_date/end_date可选时间范围show_notebook显示或返回 figure 列表。调用示例源自 docstringfrom qlib.data import D from qlib.contrib.evaluate import backtest from qlib.contrib.strategy import TopkDropoutStrategy # backtest parameters bparas {} bparas[limit_threshold] 0.095 bparas[account] 1000000000 sparas {} sparas[topk] 50 sparas[n_drop] 5 strategy TopkDropoutStrategy(**sparas) _, positions backtest(pred_df, strategy, **bparas) pred_df_dates pred_df.index.get_level_values(leveldatetime) features_df D.features(D.instruments(csi500), [Ref($close, -1)/$close-1], pred_df_dates.min(), pred_df_dates.max()) features_df.columns [label] qcr.analysis_position.rank_label_graph(positions, features_df, pred_df_dates.min(), pred_df_dates.max())图形含义输出 3 张图Hold / Buy / Sell横轴为交易日纵轴为当日该类操作股票的 label 平均排名比率rank ratio。官方文档给出的公式为ranking ratio Ascending Ranking of label / Number of Stocks in the Portfolio从源码结构看qlib/contrib/report/analysis_position/parse_position.py 中_calculate_label_rank的实际实现是g_df[rank_ratio] g_df[label].rank(ascendingFalse) / len(g_df) * 100即对当日全市场股票按 label 排名后归一化到 0–100 的百分比再对当日 Buystatus1、Holdstatus0、Sellstatus-1三类股票分别取均值rank_label_mean。例如 Buy 曲线长期处于低比值区说明策略持续买入 label 靠前的股票验证了打分信号与真实收益的一致性。七、analysis_model.model_performance_graph模型打分能力全景诊断model_performance_graph面向未执行回测的阶段仅凭pred_label预测分数 真实 label即可诊断模型的排序能力、收益分层效果与预测稳定性是模型迭代中最常用的评估图形。API 与参数pred_labelindex 为[instrument, datetime]的 MultiIndex列名为[score, label]label通常与训练 label 一致如Ref($close, -2)/Ref($close, -1) - 1lag默认 1自相关计算中的滞后天数仅用于 auto-correlationN默认 5分层分组数reverse默认 False为 True 时score * -1用于分数方向与实际效果相反的情形graph_names默认[group_return, pred_ic, pred_autocorr]控制生成哪几组图show_notebookTrue 时在 notebook 渲染否则返回plotly.graph_objs.Figure列表show_nature_day是否展示非交易日的横轴刻度**kwargs透传给 plotly 的样式参数当前支持rangebreaks用于隐藏周末/节假日空隙。图形 1分组累积收益group_return按 score 降序排列后逐日分成 N默认 5组计算各组 label 均值并累加Group1label 排名 ratio ≤ 20% 的股票组累积收益Group220% ratio ≤ 40%Group340% ratio ≤ 60%Group460% ratio ≤ 80%Group5ratio 80%long-shortGroup1 与 Group5 累积收益之差long-averageGroup1 与全市场平均累积收益之差。qlib/contrib/report/analysis_model/analysis_model_performance.py 中_group_return的实现细节先sort_values(score, ascendingFalse)再按len(x) // N切片取每组 label 均值除累积曲线外还会对long-short与long-average的逐日值绘制直方图DistplotGraphbin 宽自动取极差/20观察多空收益的逐日分布。图形 2IC 系列pred_icIC 柱状图逐日label与score的 Pearson 相关系数可用于评估预测分数的有效性Monthly IC 热力图IC 的月度均值源码中对缺失月份做了 reindex 填充保证热力图月轴连续IC 直方图 Q-Q 图IC 的分布形态及与正态分布的 Quantile-Quantile 对比用于判断 IC 是否稳定、是否存在异常尾部。实现上_pred_ic支持methods(IC, Rank IC)分别对应 pearson/spearmanMonthly IC、直方图与 Q-Q 图基于第一种 IC 绘制。图形 3自相关pred_autocorr逐日计算最新预测分数与 lag 天前预测分数的秩相关源码先groupby(levelinstrument)对 score 做shift(lag)再逐日对两组分数的rank(pctTrue)求 Pearson 相关。该序列反映打分信号的稳定性自相关越高意味着每日持仓调整越小可据此估算策略的换手率水平。扩展能力从源码结构看model_performance_graph还内置了_pred_turnover按当日 score 最大/最小len(x)//N集合的重叠程度计算 Top/Bottom 换手率可通过graph_names[group_return, pred_ic, pred_autocorr, pred_turnover]之类的方式组合启用。函数内部通过eval(f_{graph_name})动态分发到对应私有函数因此graph_names中每一项都必须与模块内_xxx函数一一对应。八、实战建议与使用边界输入数据的三个约定是全部报告的通用前提analysis_position类报告依赖backtest(..., generate_portfolio_metricsTrue)产生的portfolio_metric_dict按{count}{freq}组合的 key 取对应频率的(report_normal_df, positions)analysis_model与score_ic依赖[instrument, datetime]双级索引的pred_label矩阵label 一律使用“T 到 T1 收益”口径如Ref($close, -1)/$close - 1与回测成交在次日的设定保持一致。show_notebook参数让所有函数兼具交互展示与程序化产出两种形态返回的plotly.graph_objs.Figure列表可直接fig.write_html(...)落盘适合嵌入投研流水线。口径一致性报告中所有累积曲线、回撤、年化收益均为加和口径risk_analysis默认modesum横向对比外部复利口径指标时需先换算如需几何口径可在调用risk_analysis时显式传modeproduct。相关示例代码可在 examples/nested_decision_execution/workflow.py 中找到其中包含analysis_position.report_graph(report_normal_df)的调用示意配合本文各函数的 docstring 示例即可快速复现整套图形化评估流程。通过上述 6 类报告Qlib 把“组合层面收益/回撤/成本/换手/排名”与“模型层面分层收益/IC/自相关”的诊断完整图形化覆盖从回测结果解读到模型打分能力验证的关键环节是 Qlib 工作流中从backtest/模型预测走向可交付投研结论的重要一环。【免费下载链接】qlibQlib is an AI-oriented Quant investment platform that aims to use AI tech to empower Quant Research, from exploring ideas to implementing productions. Qlib supports diverse ML modeling paradigms, including supervised learning, market dynamics modeling, and RL, and is now equipped with https://github.com/microsoft/RD-Agent to automate RD process.项目地址: https://gitcode.com/GitHub_Trending/qli/qlib创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

最新新闻

日新闻

周新闻

月新闻