3. 使用 HistoryPanel 操作和分析歷史數據

在完成前兩章「入門」和「獲取並管理金融數據」之後,我們已經可以:

  • 在本地配置好數據源並下載所需的歷史數據;

  • 使用 get_history_data()get_history_panel() 獲取價格、成交量等基礎行情。

本章將在此基礎上,通過一個完整的小例子,演示如何使用 HistoryPanel

  • 統一管理多標的、多指標的歷史數據;

  • 計算收益率、波動率等簡單因子;

  • 疊加 K 線技術指標;

  • 識別部分蠟燭形態;

  • 使用 where 生成與數據同形的 bool 掩碼,爲後續帶 mask= 的研究接口做準備;

  • 使用 列屬性(如 hp.close)、比較運算(返回 numpy 布爾數組,可與 where 銜接)以及 hp.loc(僅沿時間軸篩選,等價 hp[:, :, key])。

更完整的 API 說明可參考 HistoryPanel API 參考(含「列屬性訪問、比較與 loc」與 where 專節)。


3.1. 1. 准备数据:获取一个 HistoryPanel

假設我們已經下載了滬深 300 指數(000300.SH)近一年的日線數據,可以直接通過 qt.get_kline()qt.get_history_data() 獲取一個 HistoryPanel

import qteasy as qt

# 获取近一年日线 OHLCV,并直接返回 HistoryPanel
hp = qt.get_kline(
    shares='000300.SH',
    start='20230101',
    end='20231231',
    freq='D',
    as_panel=True,              # 关键:返回 HistoryPanel
)

print('HistoryPanel 结构:', hp.shape)
print('shares:', hp.shares)
print('htypes:', hp.htypes)

此時:

  • hp.shape 是一個 (L, R, C) 的三元組,分別對應 股票數 × 時間長度 × 數據類型數

  • hp.shares 通常是 ['000300.SH']

  • hp.htypes 默認包含 ['open', 'high', 'low', 'close', 'vol'] 等。


3.2. 2. 计算收益率与波动率因子

HistoryPanel 內置了針對價格序列的收益與波動率計算接口:

# 2.1 计算简单收益率(simple return)
ret = hp.returns(
    price_htype='close',
    method='simple',    # 或 'log'
    periods=1,
    as_panel=False,     # 返回 DataFrame: index=时间, columns=shares
)
print('日收益率:\n', ret.tail())

# 2.2 在收益率基础上计算 20 日滚动波动率
vol = hp.volatility(
    window=20,
    price_htype='close',
    method='simple',
    annualize=True,     # 年化波动率
    as_panel=False,     # 同样返回 DataFrame
)
print('20 日年化波动率:\n', vol.tail())

典型用法是:

  • retvol 當作因子值矩陣(時間 × 股票);

  • 直接送入回測引擎,或用於選股、打分等。


3.3. 3. 叠加 K 线技术指标

HistoryPanel 中包含 OHLC 數據時,可以通過 hp.kline 訪問一批常用技術指標:

# 基于 close 价格生成 20 日简单移动平均线
hp_ma = hp.kline.sma(window=20, price_htype='close')
print('新增 htypes:', hp_ma.htypes)

# 再叠加 MACD 指标
hp_ma_macd = hp_ma.kline.macd(price_htype='close')
print('叠加 MACD 后的 htypes:', hp_ma_macd.htypes)

# 将单只股票切成 DataFrame,方便在 pandas / sklearn 中继续处理
df_300 = hp_ma_macd.to_share_frame('000300.SH')
print(df_300.tail())

這裏:

  • hp.kline.sma(...) 會在 htypes 中追加一列,例如 sma_20

  • hp.kline.macd(...) 會追加 macd_xxxmacd_signal_xxxmacd_hist_xxx 三列;

  • 最終通過 to_share_frame(share) 得到標準的二維 DataFrame,可直接與其他數據源聯立分析。

3.1 inplace:原地擴列(減少拷貝)

默認情況下,kline.* 返回 HistoryPanel(不污染原對象)。當你希望在同一個面板上連續追加多列,並減少中間對象時,可以使用 inplace=True

# 在原面板上原地追加两列,并返回原面板(支持链式)
hp2 = hp.kline.sma(window=20, inplace=True).kline.macd(inplace=True)
assert hp2 is hp
print('原地扩列后的 htypes:', hp.htypes)

提示:當新列名與已有列名衝突時,kline.* 會拋出英文 ValueError,避免靜默覆蓋。

3.2 第一入口:research_preset(一行拼列 + 直接出圖)

如果你希望“拿到數據後立刻得到一張像樣的圖”,推薦直接使用 research_preset 生成官方預設的常用列集合(預設只做顯式追加,不在 plot() 內隱藏計算):

# 生成 OHLCV + MACD + SMA20(预设名以 API 文档为准)
hp_preset = hp.research_preset('ohlcv_macd_ma', inplace=False)
print('preset htypes tail:', hp_preset.htypes[-8:])

# 多标的时可分页展示(不再静默只画前 5 只)
fig = hp_preset.plot(shares=hp_preset.shares, max_shares_per_figure=3, page=1)

當輸入面板缺少預設所需的輸入列時,會拋出英文 ValueError,資訊包含缺失列列表與補齊建議。


3.4. 4. 使用 assign 一次派生多列

在實際研究中,經常需要基於已有列派生出多個新因子,例如價差、比例、縮放後的歸一化值等。HistoryPanel.assign() 提供了一次性派生多列的 DSL:

# 假设 hp 已含 'close' 和 'open' 列
hp2 = hp.assign(
    spread=lambda p: p['close'].values[:, :, 0] - p['open'].values[:, :, 0],
    up_ratio=lambda p: p['close'].values[:, :, 0] / np.maximum(p['open'].values[:, :, 0], 1e-6),
)
print('新列:', hp2.htypes[-2:])
  • assign 的關鍵字參數名稱即新列名(htype),值可以是接收當前 HistoryPanel 的可調用對象,也可以是可廣播到 (M,L) 的數組或標量;

  • 同一次調用內,後面的列可以依賴前面剛新增的列,例如:

hp3 = hp.assign(
    a=lambda p: p['close'].values[:, :, 0] + 1.0,
    b=lambda p: p['a'].values[:, :, 0] * 2.0,
)

默認 inplace=False,返回新面板,不修改原對象;當你希望在原面板上原地擴列時,可以使用:

hp.assign(inplace=True, factor=lambda p: p['close'].values[:, :, 0] / 100.0)

提示:與 hp['col'] = ... 一樣,assign 對已有列名採用“覆蓋”語義;空面板上調用 assign 會拋出英文 ValueError


3.5. 5. 横截面 rank 与 zscore(cs/ts)

多標的研究中常見的兩類標準化需求是:

  • 截面(cross-sectional):固定某個交易日,對所有股票的因子值做標準化或排名;

  • 時序(time-series rolling):固定某隻股票,對它自己的歷史序列用滾動窗口做標準化。

HistoryPanel.rank()HistoryPanel.zscore() 提供了這兩類最基礎變換(僅做變換,不做迴歸推斷):

# 5.1 截面排名:逐日对 share 维做排名
hp_rank = hp.rank(by='close')                  # 追加一列 'rank_close'

# 5.2 截面 zscore:逐日横截面标准化(method='cs')
hp_cs = hp.zscore(by='close', method='cs')     # 追加一列 'cs_z_close'

# 5.3 时序 zscore:逐股滚动标准化(method='ts',需要 window)
hp_ts = hp.zscore(by='close', method='ts', window=20)   # 追加一列 'ts_z_close_20'

提示:zscore(method='cs')zscore(method='ts') 語義完全不同,務必顯式指定 method(並在 ts 場景提供 window),避免把“截面標準化”誤當成“時序標準化”。

5.5 對齊與重採樣(align_to / resample):避免 silent 錯行

當你手裏有兩塊 HistoryPanel,想做逐元素運算(比如 hp_factor / hp_price、或兩面板相減)時,需要特別注意:

  • HistoryPanel 不會在運算符裏自動對齊兩塊面板;

  • shares / hdates 不一致,直接拿 .values 做 NumPy 運算很容易出現“看起來能算、但其實錯行”的結果。

因此建議遵循固定流程:先對齊,再計算

# hp_a 与 hp_b 的 shares/hdates 不一定一致
a1, b1 = hp_a.align_to(hp_b, join='inner')   # 交集对齐:只保留共同 shares 与共同日期
ratio = a1.values / b1.values                # 此时逐元素运算不会错行
print('aligned shape:', a1.shape, b1.shape)

=當你需要把日線變成周線/月線等更低頻數據時,可用 resample(),並顯式指定每一列的聚合規則(避免語義不明):

# 典型 OHLCV 周线:open=first, high=max, low=min, close=last, vol=sum
hp_w = hp.resample('W', agg={
    'open': 'first',
    'high': 'max',
    'low': 'min',
    'close': 'last',
    'vol': 'sum',
})
print('weekly dates:', hp_w.hdates[:3], '...')

提示:resample() 要求 agg 覆蓋全部 htypes。對於非 OHLCV 的自定義因子列,你需要自己決定 'last' / 'mean' 等聚合方式並寫進 agg

5.6 用 (M,L) mask 做 plot 高亮(事件日/信號日)

在因子研究裏,你經常會得到一個二維布爾矩陣 mask_ml,表示“每隻股票在哪些日期滿足條件”(例如事件日、信號日、可交易池)。

HistoryPanel.plot()highlight={'condition': mask_ml} 支持直接把它映射到圖形中高亮點:

  • mask_ml.shape == (M_plot, L),其中 M_plot 爲當前 plot(shares=...) 選中的股票數,則按繪圖 share 順序對應;

  • mask_ml.shape == (M_all, L),其中 M_all == len(hp.shares),則按 share 名抽取當前繪圖子集(避免 silent 錯行);

  • layout='overlay' 時默認 primary-only 顯示高亮(與既有 Plotly highlight 語義一致)。

import numpy as np

M, L, _ = hp.shape
mask_ml = np.zeros((M, L), dtype=bool)
mask_ml[:, -1] = True                      # 举例:每只股票的最后一天高亮

fig = hp.plot(
    shares=hp.shares,
    layout='stack',
    highlight={'condition': mask_ml},
)

5.4 Newey–West / Fama–MacBeth(背景:本節不實現)

在計量實證中,你可能會看到 Newey–West(HAC)穩健標準誤Fama–MacBeth 兩步迴歸。它們主要用於迴歸推斷(標準誤修正、風險溢價顯著性檢驗),不屬於 HistoryPanel.zscore() 這類“基礎變換”。 若需要發表級實證,建議將 HistoryPanel 導出爲 DataFrame 後使用 statsmodels 等工具完成推斷流程。


3.6. 6. 使用 apply_ta 统一调用技术指标

如果你已經熟悉 qteasy.tafuncs 或 ta-lib,可以通過 HistoryPanel.apply_ta() 做統一封裝:

# 在所有股票上应用 'sma' 指标,对 close 做 10 日均线
hp_sma10 = hp.apply_ta(
    func_name='sma',
    htype='close',
    timeperiod=10,
    as_panel=True,      # 返回新的 HistoryPanel,追加一列 'sma'
)
print('htypes with sma:', hp_sma10.htypes)

這樣可以避免在循環中手動調用 tafuncs.smaHistoryPanel 會自動對所有選定股票廣播計算。


3.7. 7. 识别蜡烛形态

對於 OHLC 數據,還可以通過 HistoryPanel.candle_pattern() 封裝 ta-lib 的蠟燭形態識別函數,例如:

# 识别锤头线形态(CDLHAMMER),返回时间 × 股票的整数信号矩阵
hammer = hp.candle_pattern(
    name='cdlhammer',
    as_panel=False,          # 返回 DataFrame
)
print('锤头线信号(非 0 代表出现形态):\n', hammer[hammer != 0].dropna(how='all'))

信號含義通常是:

  • 0:未識別到該形態;

  • 正數 / 負數:不同方向或強度的形態信號(取決於具體 ta-lib 函數的定義)。


3.8. 8. 生成研究用 bool 掩码(where

面板 values 的軸順序爲 (標的數 M, 時間長度 L, 指標列數 N),與 hp.shape 一致。hp.where(condition) 返回 同形numpy.ndarraydtype=bool,且不修改 hpcum_returnnormalizeportfolio 已支持 mask=hp.where(...)(或與 where 同廣播規則的 bool 數組)。研究向語義與邊界(路徑斷開、基準日無效、組合與基準關係等)以 API docstring 爲準。

與整表數值比較(與 hp.values > 閾值 等價):

import numpy as np

mask = hp.where(hp.values > 0)   # 或先写 cond = hp.values > 0 再 hp.where(cond)
assert mask.shape == hp.shape
print('True 格点数量:', int(mask.sum()))

按時間維打標(例如某一列索引爲「事件日」,沿所有 htype 複製;以下爲示意,需保證 cond_ml 形狀爲 (M, L)):

M, L, N = hp.shape
cond_ml = np.zeros((M, L), dtype=bool)
cond_ml[:, -1] = True   # 示例:最后一个交易日标记为 True
mask_event = hp.where(cond_ml)
assert mask_event[:, -1, :].all() and not mask_event[:, 0, :].any()

按收盤價列構造條件callable 接收當前 HistoryPanel):

# 假设存在 'close' 列;多列面板可用 p.values[:, :, idx] 或子面板
mask_close = hp.where(lambda p: p['close'].values[:, :, 0] > 0)

複合條件與缺失值

mask_band = hp.where(lambda p: (p.values > -1e9) & (p.values < 1e9))
mask_nan = hp.where(np.isnan(hp.values))   # 标记 NaN 所在格,供排除缺失

說明:形狀恰好爲 (M, L) 的二維條件表示「每個 (標的, 日) 對所有指標列共用同一布爾值」,庫內會先擴展爲 (M, L, 1) 再廣播到 (M, L, N)。一維 (M,)(M, 1) 表示僅隨標的變化,會展開爲 (M, 1, 1) 再廣播。更完整的參數與異常說明見 HistoryPanel API 參考where 一節。

8.1 累計收益與歸一化(cum_return / normalize

返回 HistoryPanel(不修改原面板),默認對解析後的 close 列計算;輸出列名爲 cumret_<列名>norm_<列名>(與復權列 close|b 並存時,仍可傳 htypes=None,列名仍爲 cumret_close)。可與掩碼聯用:

m = hp.where(hp.close > 0)           # 或与 hp.values 比较得到 (M,L,N)
cr = hp.cum_return(mask=m, method='simple')
nm = hp.normalize(base_index=0, mask=m)

8.2 組合聚合(portfolio

研究向:沿 標的維 對指定列做等權或加權平均,返回 HistoryPanelhdates 不變)。mask=where 同廣播規則。無 groups 且指定 benchmark 時,基準標的不參與組合計算,僅用於 benchmark_output='tag_along'(多一行基準曲線)或 'excess_only'(輸出 excess_<列名>)。非賬戶回測、無交易成本。詳見 API docstring。

ew = hp.portfolio(mode='equal', benchmark_output='none')
m = hp.where(hp.close > 0)
ew_m = hp.portfolio(mode='equal', mask=m, benchmark_output='none')
# 含基准指数代码 'IDX' 时:
# tagged = hp.portfolio(mode='equal', benchmark='IDX', benchmark_output='tag_along', new_share_name='EW')

8.3 列屬性、比較與 loc(與 pandas 的差異簡述)

  • 列屬性(只讀):列名爲合法 Python 標識符時可用 hp.close,等價 hp['close'];含 | 的復權列名等須用方括號。賦值請統一 hp['col'] = ...

  • 比較hp.close > hp.openhp > 0 等得到 numpy.ndarray(bool),不是子 HistoryPanel;全形掩碼可寫 hp.where(hp.close > 15)。兩側均爲面板時須 shareshdates 一致;兩列比較時多爲兩個 單列 子面板。

  • lochp.loc[s] 等價 hp[:, :, s],只沿 時間(hdates) 軸篩選;不要where 得到的 (M, L, N) 掩碼傳給 loc,格點條件仍用 where + 後續 mask=

細節與邊界情況以 HistoryPanel API 參考 爲準(含 wherecum_returnnormalizeportfolio)。

# 示意(在已含 open/close 列的 hp 上)
# m = hp.where(hp.close > hp.open)   # 比较 → bool ndarray → where 广播到 (M,L,N)
# tail = hp.loc[-5:]                 # 仅时间轴,等价 hp[:, :, -5:]

3.9. 9. 从 HistoryPanel 研究到 Strategy / Operator:迁移路径

HistoryPanel 上的 cum_returnportfolioplot 等是研究向工具:便於肉眼篩因子、看粗組合線,不包含交割週期、手續費、滑點、信號類型(PT/PS/VS)解析等與正式回測一致的賬戶語義。結論若用於實盤或嚴肅評價,應把同一經濟含義的規則寫進策略的 realize(),交給 Operator + Backtester。

下面用同一思想——20 期簡單動量串起兩階段(需本地已配置數據源並下載相應字段;股票代碼僅爲示意)。

9.1 研究階段:在多標的 HistoryPanel 上算因子並粗看截面

import numpy as np
import qteasy as qt

# 多标的日线收盘价(htype 以你本地数据源为准,可为 close 或复权列如 close|b)
hp = qt.get_history_data(
    htypes='close',
    shares=['000001.SZ', '000002.SZ', '600000.SH'],  # 示例股票池
    start='20200101',
    end='20231231',
    freq='d',
)

# 20 期简单收益 r_t = P_t / P_{t-20} - 1,直接得到带标签的子面板
hp_mom = hp.returns(price_htype='close', periods=20, method='simple', as_panel=True)

# 截面排名:逐日看多标的谁强谁弱(研究向)
hp_rank = hp_mom.rank(by='ret_close')

# 可选:等权「组合」在收益列上的逐日平均,仅用于目视,非账户回测
hp_ew = hp_mom.portfolio(htypes='ret_close', mode='equal', benchmark_output='none')

hp_mom.plot(shares=hp_mom.shares, layout='stack')   # 结合前几节 mask 高亮更佳

這裏你在 三維面板 上完成了因子列生產、截面變換與粗聚合;若結果滿意,進入 9.2 把因子定義搬到策略裏。

9.2 正式回測階段:FactorSorter + Operator

選股類策略常用 FactorSorterrealize() 返回每個標的一個標量因子(長度 = 股票數),框架再按 max_sel_countsort_ascending 等做排序與權重。下面動量與 9.1 中 returns(..., periods=20) 的經濟含義一致,但數據來自策略窗口注入(與回測步對齊、受 use_latest_data_cycle 等約束)。

import numpy as np
import qteasy as qt
from qteasy import StgData


class Mom20Factor(qt.FactorSorter):
    """20 期收盘动量:close[-1]/close[0]-1,与 HP 上 returns(periods=20) 语义对齐。"""

    def __init__(self) -> None:
        super().__init__(
            name='Mom20',
            description='20-bar simple momentum on daily close',
            data_types=[StgData('close', freq='d', asset_type='E')],
            window_length=21,
            max_sel_count=3,
            sort_ascending=False,
            condition='any',
            use_latest_data_cycle=True,
        )

    def realize(self):
        close_w = self.get_data('close_E_d')   # 形状约为 (window_length, 股票数)
        out = close_w[-1] / close_w[0] - 1.0
        out = np.where(np.isfinite(out), out, np.nan)
        return out


# 以下为骨架:具体 run 参数(股票池、起止日、费率、signal_type 等)见「回测」与 Operator 文档
op = qt.Operator()
op.add_strategy(Mom20Factor(), run_freq='d', run_timing='close')
# res = qt.run(op, mode=1, ...)  # 按项目文档填写 shares、起止日期与回测配置

注意

  • Operator 默認組內可使用 PT 類目標倉位信號;FactorSorter 與 PT 的搭配以你項目中的策略文檔爲準。

  • HP 裏用全樣本算的 rank/zscore,與回測中「每步僅見歷史窗口」的因子可能略有數值差異,屬預期現象;應以策略內 get_data 爲準複覈防前視。

完整可運行腳本見項目根目錄 examples/historypanel_research_to_strategy.py


3.10. 10. 多源数据拼成 HistoryPanel(价格 + 基本面示例)

HistoryPanel 可以承載任意 htypes 列;多表/多 DataType 的常見做法是:(1) 一次 get_history_data 拉多列;(2) 或分別取兩塊面板再 align_to

10.1 單次拉取多列(頻率與資產類型需一致)

若本地已有日線 closepb(市淨率)等字段,可直接:

import numpy as np
import qteasy as qt

hp = qt.get_history_data(
    htypes='close, pb',
    shares=['000001.SZ', '000002.SZ'],
    start='20200101',
    end='20231231',
    freq='d',
)

# 估值倒数近似账面市值比(仅作演示,非投资建议)
hp2 = hp.assign(bp=lambda p: 1.0 / np.maximum(p['pb'].values[:, :, 0], 1e-8))

# 横截面 zscore:逐日可比
hp_z = hp2.zscore(by='bp', method='cs')

缺列或表未下載時,get_history_data 會報錯;請先通過數據源教程補齊表數據。

10.2 兩塊面板對齊後再派生(不同來源/不同拉取)

align_to 要求兩側 htypes 名稱與順序完全一致。若你只分別拉取了 closepb 兩列,需先擴成同構列(缺失側用 NaN 佔位),對齊後再用 assign 把有效數值寫回同一套列名:

import numpy as np
import qteasy as qt

hp_px = qt.get_history_data(htypes='close', shares=pool, start='20200101', end='20231231', freq='d')
hp_pb = qt.get_history_data(htypes='pb', shares=pool, start='20200101', end='20231231', freq='d')

c = hp_px['close'].values[:, :, 0]
p = hp_pb['pb'].values[:, :, 0]
nan_c = np.full_like(c, np.nan)
nan_p = np.full_like(p, np.nan)

hp_a = qt.HistoryPanel(
    np.stack([c, nan_p], axis=2),
    levels=hp_px.shares,
    rows=hp_px.hdates,
    columns=['close', 'pb'],
)
hp_b = qt.HistoryPanel(
    np.stack([nan_c, p], axis=2),
    levels=hp_pb.shares,
    rows=hp_pb.hdates,
    columns=['close', 'pb'],
)

a, b = hp_a.align_to(hp_b, join='inner')
hp_joined = a.assign(
    close=lambda x: a['close'].values[:, :, 0],
    pb=lambda x: b['pb'].values[:, :, 0],
)

更多字段(行業、市值等)同理;若可一次 get_history_data(htypes='close, pb', ...),優先用 §10.1,少一步手工同構。核心原則仍是 先對齊再運算,避免 NumPy 靜默錯行。

可運行合成數據演示見項目根目錄 examples/historypanel_multisource_research.py


3.11. 11. 导出到 pandas / statsmodels(宽表、长表与截面回归)

HistoryPanel 不內置 Newey–West、Fama–MacBeth 等推斷流程;需要發表級迴歸時,應導出爲二維表後用 statsmodels 等庫。

11.1 按標的切成多個 DataFrame(寬表)

by_share = hp.to_df_dict(by='share')
df_000001 = by_share['000001.SZ']   # index 为时间,columns 为 htypes

11.2 手工堆成長表(便於按日截面分組)

import pandas as pd

records = []
M, L, N = hp.shape
ci = hp.htypes.index('close')
fi = hp.htypes.index('factor')   # 假设已存在名为 factor 的列
for mi, sh in enumerate(hp.shares):
    for li, dt in enumerate(hp.hdates):
        records.append({
            'date': dt,
            'share': sh,
            'close': hp.values[mi, li, ci],
            'factor': hp.values[mi, li, fi],
        })
df_long = pd.DataFrame(records)

11.3 單日截面 OLS 示例(需已安裝 statsmodels)

# pip install statsmodels
import statsmodels.api as sm

sub = df_long[df_long['date'] == df_long['date'].iloc[-100]].dropna()
y = sub['close'] / sub['close'].mean() - 1.0   # 示例因变量,实际常用前瞻收益
X = sm.add_constant(sub['factor'])
model = sm.OLS(y, X, missing='drop').fit()
print(model.summary())

上述因變量僅爲演示;實務中應將 因子與前瞻收益對齊 並處理停牌、倖存者偏差等——均在計量層完成,而非 HistoryPanel 核心職責。

合成數據示例見項目根目錄 examples/historypanel_statsmodels_export.py


3.12. 12. 小结与下一步

通過本章示例,你可以看到:

  • HistoryPanel 提供了統一的三維歷史數據容器,適合多標的 / 多指標聯合研究;

  • 在其之上,可以直接調用統計方法(describe/mean/std/min/max)、滾動窗口(rolling)、收益/波動率(returns/volatility)、橫截面排名與標準化(rank/zscore累計收益與歸一化(cum_return/normalize組合聚合(portfolio、K 線指標(kline.*)、技術指標橋接(apply_ta)、蠟燭形態識別(candle_pattern)、研究掩碼(where 以及 列屬性 / 比較 / loc 等;

  • 任意一步都可以方便地切回 DataFrame,與 pandas / sklearn / statsmodels 等協同工作;

  • 正式回測請通過 Strategy.realize + Operator 完成;研究向 HP 與回測引擎的邊界與遷移路徑見 §9

下一步建議:

  • 在 Notebook 中基於你自己關注的股票池構造 HistoryPanel,嘗試疊加多個因子(均線、波動率、估值等),並畫出與價格對齊的圖表;

  • 在閱讀 HistoryPanel API 參考 的基礎上,爲你的研究題目設計一套「數據 → 因子(HP)→ 規則固化(Strategy)→ 回測(Operator)」的工作流;

  • 若考慮是否增加獨立「因子評價」API 封裝,可閱讀設計說明 HistoryPanel 與可選 FactorResearch 層