02-指数估值查询

指数行情、估值与历史分位查询。趋势定投的核心决策依据。

当前估值快照

获取某指数最新估值与分位:

from db import get_connection
from query_utils import query_to_df, quality_filter

conn = get_connection()
df = query_to_df(conn, f"""
    SELECT d.index_name, v.trade_date, v.pe, v.pe_percentile,
           v.pb, v.pb_percentile, v.dividend_yield
    FROM index_valuation v
    JOIN dim_index d ON v.index_code = d.index_code
    WHERE v.index_code = ?
      AND {quality_filter("v")}
    ORDER BY v.trade_date DESC LIMIT 1
""", ("000300",))
print(df)

对应 SQL:

SELECT d.index_name, v.trade_date, v.pe, v.pe_percentile,
       v.pb, v.pb_percentile, v.dividend_yield
FROM index_valuation v
JOIN dim_index d ON v.index_code = d.index_code
WHERE v.index_code = '000300'
  AND v.data_quality IN ('ok', 'manual_fixed')
ORDER BY v.trade_date DESC LIMIT 1;

分位解读:

pe_percentile估值区域定投建议
< 0.2极低加大定投
0.2 - 0.4正常定投
0.4 - 0.6正常定投
0.6 - 0.8减半定投
> 0.8极高暂停定投或分批止盈

全部指数估值对比

一次性查看所有关注指数的当前估值:

SELECT d.index_code, d.index_name, d.category,
       v.trade_date, v.pe, v.pe_percentile,
       v.pb, v.pb_percentile, v.dividend_yield
FROM index_valuation v
JOIN dim_index d ON v.index_code = d.index_code
WHERE v.trade_date = (SELECT MAX(trade_date) FROM index_valuation)
  AND v.data_quality IN ('ok', 'manual_fixed')
ORDER BY v.pe_percentile ASC;

按 PE 分位升序排列,分位低的指数排在前面,便于发现低估机会。

历史估值序列

获取某指数的历史估值序列,用于绘制估值曲线:

df = query_to_df(conn, f"""
    SELECT trade_date, pe, pe_percentile, pb, pb_percentile
    FROM index_valuation
    WHERE index_code = ?
      AND trade_date >= date('now', '-10 years')
      AND {quality_filter()}
    ORDER BY trade_date
""", ("000300",))

Python 绘图示例:

import matplotlib.pyplot as plt

fig, ax1 = plt.subplots(figsize=(12, 5))
ax1.plot(df["trade_date"], df["pe"], color="steelblue", label="PE")
ax1.set_ylabel("PE", color="steelblue")
ax2 = ax1.twinx()
ax2.plot(df["trade_date"], df["pe_percentile"], color="coral", label="PE 分位")
ax2.axhline(0.2, ls="--", color="green", alpha=0.5)
ax2.axhline(0.8, ls="--", color="red", alpha=0.5)
ax2.set_ylabel("分位", color="coral")
plt.title("沪深300 估值历史")
plt.show()

估值分位历史分布

查看某指数 PE 分位的分布,判断当前处于什么位置:

SELECT
    CASE
        WHEN pe_percentile < 0.1 THEN '0-10%'
        WHEN pe_percentile < 0.2 THEN '10-20%'
        WHEN pe_percentile < 0.3 THEN '20-30%'
        WHEN pe_percentile < 0.4 THEN '30-40%'
        WHEN pe_percentile < 0.5 THEN '40-50%'
        WHEN pe_percentile < 0.6 THEN '50-60%'
        WHEN pe_percentile < 0.7 THEN '60-70%'
        WHEN pe_percentile < 0.8 THEN '70-80%'
        WHEN pe_percentile < 0.9 THEN '80-90%'
        ELSE '90-100%'
    END AS bucket,
    COUNT(*) AS days
FROM index_valuation
WHERE index_code = '000300'
  AND pe_percentile IS NOT NULL
  AND data_quality IN ('ok', 'manual_fixed')
GROUP BY bucket
ORDER BY bucket;

指数日线行情

获取日线数据,用于技术分析或趋势确认:

df = query_to_df(conn, f"""
    SELECT trade_date, open, close, high, low, volume, pct_change
    FROM index_daily
    WHERE index_code = ?
      AND trade_date >= date('now', '-1 year')
      AND {quality_filter()}
    ORDER BY trade_date
""", ("000300",))

均线计算

df["ma20"] = df["close"].rolling(20).mean()
df["ma60"] = df["close"].rolling(60).mean()
df["ma250"] = df["close"].rolling(250).mean()

趋势判断

# 多头排列:MA20 > MA60 > MA250
df["bull_alignment"] = (df["ma20"] > df["ma60"]) & (df["ma60"] > df["ma250"])

多指数相关性

分析指数间的相关性,用于组合配置:

-- 宽基指数收益率相关性
WITH returns AS (
    SELECT
        a.trade_date,
        a.pct_change AS hs300,
        b.pct_change AS zz500,
        c.pct_change AS cybz
    FROM index_daily a
    JOIN index_daily b ON a.trade_date = b.trade_date AND b.index_code = '000905'
    JOIN index_daily c ON a.trade_date = c.trade_date AND c.index_code = '399006'
    WHERE a.index_code = '000300'
      AND a.data_quality IN ('ok', 'manual_fixed')
      AND a.trade_date >= date('now', '-3 years')
)
SELECT
    AVG(hs300 * zz500) - AVG(hs300) * AVG(zz500) AS hs300_zz500_cov,
    AVG(hs300 * cybz) - AVG(hs300) * AVG(cybz) AS hs300_cybz_cov
FROM returns;

更完整的相关性矩阵建议在 Python 中用 df.corr() 计算。

封装函数建议

query_utils.py 中可封装以下函数:

def get_index_valuation_now(conn, index_code: str) -> pd.DataFrame:
    """获取指数最新估值。"""

def get_index_valuation_history(conn, index_code: str, years: int = 10) -> pd.DataFrame:
    """获取指数历史估值序列。"""

def get_all_index_percentiles(conn) -> pd.DataFrame:
    """获取所有指数当前估值分位,按分位升序。"""

def get_index_daily(conn, index_code: str, start_date: str = None,
                    end_date: str = None) -> pd.DataFrame:
    """获取指数日线行情。"""

具体实现见 query_utils.py(可按需创建)。