跳到内容
当前位置:首页>Excel>AI + 量化开发工程师学习笔记 Day 33:组合构建——等权组合与目标持仓管理

AI + 量化开发工程师学习笔记 Day 33:组合构建——等权组合与目标持仓管理

  • 2026-11-07 18:49:29
AI + 量化开发工程师学习笔记 Day 33:组合构建——等权组合与目标持仓管理

第二阶段:AI Quant Model(Day21~Day35)

预计学习时间:60~90 分钟。

今天严格按照我们原定的课程计划进行:

课程
原定主题
进度
Day31
Feature Importance / SHAP
已完成
Day32
模型选股 Top-K
已完成
Day33
组合构建:等权组合
今天
Day34
策略回测
下一课
Day35
阶段项目:ML 选股系统
阶段总结

Day32,我们已经让 Linear、LightGBM、CatBoost 三个模型分别生成 Top-K 股票信号。

今天要解决的是下一步:

选出了 5 只股票之后,如何把它们变成一个真正有资金约束、有目标权重、有持仓状态的投资组合?

我们将正式区分三个概念:

  • 选股信号:模型想买哪些股票。
  • 目标组合:每只股票应该占总资产的多少。
  • 实际持仓:考虑现金、价格、整手数量等限制后,账户真正持有什么。

今天不引入新的预测模型,也不提前实现 Day34 的完整历史回测。


一、为什么选股不等于组合构建?

假设 Day32 的 LightGBM 选出了五只股票:

股票
模型预测收益
排名
A
8.2%
1
B
6.5%
2
C
5.1%
3
D
3.8%
4
E
2.9%
5

我们有 10 万元资金。

应该怎样分配?

最简单的方案是等权:

当 :

每只股票目标资金:

但问题随之出现:

如果一只股票价格为 37.60 元,分配 20000 元,能买多少股?

A 股普通股票买入通常需要满足 100 股整数倍的规则(具体证券品种及交易规则可能不同)。

因此,实际可以买入:

实际使用资金:

剩余资金:

所以:

目标权重 20%,不代表最终实际持仓权重一定恰好 20%。


二、今天必须掌握的五个概念

2.1 Target Weight:目标权重

模型希望组合达到的权重。

例如:

A 20%
B 20%
C 20%
D 20%
E 20%

2.2 Actual Weight:实际权重

根据当前市场价格和实际持仓计算:

其中:

  • :实际持股数量。
  • :当前估值价格。
  • :账户总资产净值。

2.3 Cash:现金

这里先不考虑负债、融资、费用应计等复杂账户项目。

2.4 Drift:权重偏离

如果:

目标权重:20%
实际权重:16%

则:

意味着需要考虑增持。

2.5 Rebalance:再平衡

再平衡是把当前持仓向目标组合调整。

例如:

当前:
A 30%
B 20%
C 20%
D 10%
现金 20%

目标:
A 20%
B 20%
C 20%
D 20%
E 20%

那么需要减少 A、增加 D 和 E,并使用现金。

注意:真实交易必须根据订单成交结果更新持仓,不能直接把目标权重当作已经成交的持仓。


三、组合构建的数学模型

3.1 等权组合

假设选股集合:

目标权重:

满足:

且:

这是一个不使用杠杆、不做空的满仓股票组合。

但由于交易整手限制,实际持仓通常会留有少量现金。

3.2 组合净值

组合总资产:

例如:

资产
数量
价格
市值
股票 A
500 股
37.60
18,800
股票 B
1,000 股
19.50
19,500
股票 C
600 股
32.00
19,200
股票 D
2,000 股
9.80
19,600
股票 E
800 股
24.20
19,360
现金
—
—
3,540
合计
100,000

这才是我们今天要构建的组合数据结构。


四、Day33 工程设计

今天新增以下模块:

quant_ml/
├── src/
│   └── portfolio/
│       ├── __init__.py
│       ├── topk_selector.py        # Day32已有
│       └── equal_weight.py         # Day33新增
│
├── scripts/
│   └── build_equal_weight.py       # Day33新增
│
├── tests/
│   └── test_equal_weight.py        # Day33新增
│
└── data/
    └── portfolios/
        ├── lightgbm_target.parquet
        ├── lightgbm_orders_demo.csv
        └── lightgbm_account_demo.csv

我们明确区分:

数据对象
作用
TopKSignal
股票选择结果
TargetPortfolio
目标股票和目标权重
Account
当前现金和实际持仓
OrderIntent
计划买入或卖出的数量
Fill
实际成交回报,Day34 再引入

今天实现目标组合、账户快照和订单意图。

不会把订单意图直接视为真实成交。


五、【正式工程代码】src/portfolio/equal_weight.py

这是今天的核心文件。

我们实现三个函数:

  1. build_equal_weight_target():构建目标组合。
  2. calculate_account_snapshot():计算当前净值与实际权重。
  3. generate_order_intents():根据目标权重生成计划交易数量。

完整代码如下:

# ============================================================
# Day33:等权组合构建
# - 目标权重(Target Portfolio)
# - 账户快照(NAV / Actual Weight)
# - 订单意图(Order Intent,不是成交)
# ============================================================

from __future__ import annotations

from dataclasses import dataclass
import math

import numpy as np
import pandas as pd


@dataclass(frozen=True)
classAccount:
"""
    账户当前状态。

    cash:
        可用现金,单位元。

    positions:
        股票代码 -> 当前持股数量。
    """

    cash: float
    positions: dict[str, int]


defbuild_equal_weight_target(
    signals: pd.DataFrame,
)
 -> pd.DataFrame:

"""
    将 Day32 的 Top-K 信号转换为等权目标组合。

    输入:
        signal_date
        symbol
        prediction
        rank

    输出:
        signal_date
        symbol
        target_weight
    """


    required = [
"signal_date",
"symbol",
    ]

    missing = [
        col for col in required
if col notin signals.columns
    ]

if missing:
raise ValueError(
f"缺少字段:{missing}"
        )

    df = signals.copy()

if df.empty:
raise ValueError(
"选股信号不能为空"
        )

    df["signal_date"] = pd.to_datetime(
        df["signal_date"]
    )

if df[
        ["signal_date", "symbol"]
    ].isna().any().any():
raise ValueError(
"信号日期或股票代码不能为空"
        )

    df["symbol"] = df[
"symbol"
    ].astype(str)

if df.duplicated(
        ["signal_date", "symbol"]
    ).any():
raise ValueError(
"存在重复股票信号"
        )

    counts = df.groupby(
"signal_date"
    )["symbol"].transform("size")

    df["target_weight"] = (
1.0 / counts
    )

return df[
        [
"signal_date",
"symbol",
"target_weight",
        ]
    ].sort_values(
        ["signal_date", "symbol"]
    ).reset_index(drop=True)


defvalidate_account(
    account: Account,
)
:

ifnot np.isfinite(account.cash):
raise ValueError(
"现金必须为有限数值"
        )

if account.cash < 0:
raise ValueError(
"现金不能为负"
        )

for symbol, quantity in (
        account.positions.items()
    ):
if (
            isinstance(quantity, bool)
ornot isinstance(quantity, int)
or quantity < 0
        ):
raise ValueError(
f"{symbol}持仓数量不合法"
            )


defcalculate_account_snapshot(
    account: Account,
    prices: dict[str, float],
)
 -> tuple[float, pd.DataFrame]:

"""
    按给定估值价格计算账户净值和持仓权重。
    """


    validate_account(account)

    rows = []
    market_value = 0.0

for symbol, quantity in (
        account.positions.items()
    ):

if quantity == 0:
continue

if symbol notin prices:
raise ValueError(
f"缺少{symbol}的估值价格"
            )

        price = float(prices[symbol])

ifnot np.isfinite(price) or price <= 0:
raise ValueError(
f"{symbol}价格不合法"
            )

        value = quantity * price

        market_value += value

        rows.append({
"symbol": symbol,
"quantity": quantity,
"price": price,
"market_value": value,
        })

    nav = account.cash + market_value

if nav <= 0:
raise ValueError(
"账户净值必须大于0"
        )

    snapshot = pd.DataFrame(
        rows,
        columns=[
"symbol",
"quantity",
"price",
"market_value",
        ],
    )

    snapshot["actual_weight"] = (
        snapshot["market_value"] / nav
    )

return nav, snapshot


defgenerate_order_intents(
    target: pd.DataFrame,
    account: Account,
    prices: dict[str, float],
    lot_size: int = 100,
)
 -> pd.DataFrame:

"""
    生成计划交易数量。

    注意:
    - 这是订单意图,不是成交回报。
    - 卖出可以按实际持仓数量计算,
      不强制要求100股整数倍。
    - 买入按lot_size向下取整。
    - 不处理手续费、滑点、T+1可卖数量、
      涨跌停、停牌或订单成交。
    """


if lot_size <= 0:
raise ValueError(
"lot_size必须大于0"
        )

if target.empty:
raise ValueError(
"目标组合不能为空"
        )

if target["symbol"].duplicated().any():
raise ValueError(
"单期目标组合存在重复股票"
        )

    weights = pd.to_numeric(
        target["target_weight"],
        errors="raise",
    )

if (
not np.isfinite(weights).all()
or (weights < 0).any()
ornot np.isclose(weights.sum(), 1.0)
    ):
raise ValueError(
"目标权重必须非负且合计为1"
        )

    nav, _ = calculate_account_snapshot(
        account,
        prices,
    )

    target_weights = dict(
        zip(
            target["symbol"].astype(str),
            weights,
        )
    )

    all_symbols = sorted(
        set(target_weights)
        | set(account.positions)
    )

    rows = []

for symbol in all_symbols:

if symbol notin prices:
raise ValueError(
f"缺少{symbol}的价格"
            )

        price = float(prices[symbol])

ifnot np.isfinite(price) or price <= 0:
raise ValueError(
f"{symbol}价格不合法"
            )

        current_qty = (
            account.positions.get(
                symbol, 0
            )
        )

        weight = target_weights.get(
            symbol, 0.0
        )

        target_amount = nav * weight

# 目标持仓按整手向下取整
        target_qty = (
            math.floor(
                target_amount
                / (price * lot_size)
            ) * lot_size
        )

        delta = target_qty - current_qty

if delta > 0:
            side = "BUY"
elif delta < 0:
            side = "SELL"
else:
            side = "HOLD"

        rows.append({
"symbol": symbol,
"price": price,
"current_quantity": current_qty,
"target_weight": weight,
"target_quantity": target_qty,
"order_quantity": abs(delta),
"side": side,
        })

return pd.DataFrame(rows)

5.1 为什么要使用 dataclass?

我们之前学习过 C++,可以把它理解成一个简单的账户状态结构体。

structAccount {
double cash;
std::map<std::string, int> positions;
};

Python 中:

@dataclass(frozen=True)
classAccount:
    cash: float
    positions: dict[str, int]

frozen=True可以阻止重新给 cash或 positions字段赋值,但不能阻止修改字典内部内容。

例如:

account.positions["A"] = 100

仍可能修改字典。

因此,今天把 Account作为不主动修改的输入快照使用。后续实现正式账户引擎时,可以采用不可变映射或明确的状态更新接口。

5.2 这段代码为什么还不是交易执行引擎?

假设:

当前现金:0元
持有A股票:100000元

目标:
A 0%
B 100%

程序会生成:

SELL A
BUY B

但在真实交易中,必须先确认 A 的卖出成交,再根据实际到账可用资金安排 B 的买入。

当前函数只负责生成目标交易意图。

它没有模拟成交顺序和资金约束,因此不能直接把生成的买入指令全部执行。

这是我们 Day34 回测引擎需要解决的问题。


六、【工程入口代码】scripts/build_equal_weight.py

今天我们先从 Day32 的 LightGBM Top-K 选股信号中选择一个日期,构建示例账户。

这里使用教学模拟价格,不把它们当成真实历史行情。

创建:

scripts/build_equal_weight.py
# ============================================================
# Day33:等权目标组合构建入口
# 正式输出:lightgbm_target.parquet
# 演示输出:orders_demo / account_demo(不可用于历史回测)
# ============================================================

from pathlib import Path

import pandas as pd

from src.portfolio.equal_weight import (
    Account,
    build_equal_weight_target,
    calculate_account_snapshot,
    generate_order_intents,
)


SIGNAL_PATH = Path(
"data/signals/lightgbm_topk_test.parquet"
)

OUTPUT_DIR = Path(
"data/portfolios"
)


defmain():

    OUTPUT_DIR.mkdir(
        parents=True,
        exist_ok=True,
    )

ifnot SIGNAL_PATH.exists():
raise FileNotFoundError(
            SIGNAL_PATH
        )

    signals = pd.read_parquet(
        SIGNAL_PATH
    )

    target = build_equal_weight_target(
        signals
    )

    target.to_parquet(
        OUTPUT_DIR
        / "lightgbm_target.parquet",
        index=False,
    )

    latest_date = target[
"signal_date"
    ].max()

    latest_target = target.loc[
        target["signal_date"]
        == latest_date
    ].copy()

    print("\n最新一期目标组合:")

    print(
        latest_target.to_string(
            index=False
        )
    )

# 仅供组合构建功能验证。
# 不代表真实A股历史价格。
    demo_prices = {
        symbol: float(
10 + i * 5
        )
for i, symbol in enumerate(
            latest_target["symbol"]
        )
    }

    account = Account(
        cash=100000.0,
        positions={},
    )

    nav, snapshot = (
        calculate_account_snapshot(
            account,
            demo_prices,
        )
    )

    print("\n初始账户净值:")
    print(nav)

    orders = generate_order_intents(
        latest_target,
        account,
        demo_prices,
        lot_size=100,
    )

    orders.to_csv(
        OUTPUT_DIR
        / "lightgbm_orders_demo.csv",
        index=False,
        encoding="utf-8-sig",
    )

    snapshot.to_csv(
        OUTPUT_DIR
        / "lightgbm_account_demo.csv",
        index=False,
        encoding="utf-8-sig",
    )

    print("\n计划订单:")
    print(
        orders.to_string(index=False)
    )

    planned_buy_amount = (
        orders.loc[
            orders["side"] == "BUY",
"order_quantity",
        ]
        * orders.loc[
            orders["side"] == "BUY",
"price",
        ]
    ).sum()

    print(
"\n计划买入金额:",
        planned_buy_amount,
    )

    print(
"未考虑费用的剩余现金:",
        account.cash - planned_buy_amount,
    )

    print(
"\n注意:订单尚未成交,"
"账户实际持仓仍为空。"
    )


if __name__ == "__main__":
    main()

运行:

python -m scripts.build_equal_weight

成功后,检查:

ls data/portfolios

今天生成的 lightgbm_target.parquet是正式的研究目标组合数据;orders_demo.csv和 account_demo.csv只是功能演示,不能用于历史回测收益计算。


七、从目标组合到实际持仓:还缺少什么?

这是 Day33 必须明确的边界。

假设目标权重已经确定,但我们还没有完成:

  • 根据历史真实价格确定可买数量。
  • 检查股票是否停牌。
  • 检查涨跌停限制。
  • 检查买卖手续费。
  • 检查印花税。
  • 检查卖出股票是否满足 T+1 规则。
  • 检查订单是否成交。
  • 更新现金和持仓。

因此,我们的系统目前是:

模型预测
    ↓
Top-K选股
    ↓
目标权重
    ↓
计划订单
    ↓
【尚未实现】
真实成交模拟
    ↓
账户状态更新
    ↓
净值曲线

其中,今天已经完成前三个环节和基础计划订单。

Day34 才会正式研究回测。


八、为什么要区分目标权重和实际权重?

举个例子。

假设初始组合:

股票
初始权重
A
20%
B
20%
C
20%
D
20%
E
20%

经过一段时间,A 上涨 20%,其他股票价格不变。

初始总资产 100 万元,A 初始市值 20 万元。

上涨后:

组合总资产:

此时 A 的实际权重:

其余股票:

这意味着:

即使选股集合完全不变,价格变化也会导致组合权重偏离目标。

因此,后续再平衡策略需要决定:

  • 每天恢复等权?
  • 每周恢复等权?
  • 每月恢复等权?
  • 还是只有权重偏离超过阈值才交易?

再平衡越频繁,通常意味着更高的交易成本。

但也可能更及时地控制风险。

这需要回测验证,不能仅凭直觉选择。


九、【自动化测试】tests/test_equal_weight.py

创建:

tests/test_equal_weight.py

完整代码:

# ============================================================
# Day33:等权组合构建单元测试
# ============================================================

import pandas as pd
import pytest

from src.portfolio.equal_weight import (
    Account,
    build_equal_weight_target,
    calculate_account_snapshot,
    generate_order_intents,
)


deftest_equal_weight_target():

    signals = pd.DataFrame({
"signal_date": [
"2025-01-02",
"2025-01-02",
"2025-01-02",
"2025-01-03",
"2025-01-03",
        ],
"symbol": [
"A", "B", "C",
"A", "D",
        ],
    })

    result = build_equal_weight_target(
        signals
    )

    first = result.loc[
        result["signal_date"]
        == pd.Timestamp("2025-01-02")
    ]

    second = result.loc[
        result["signal_date"]
        == pd.Timestamp("2025-01-03")
    ]

assert len(first) == 3
assert len(second) == 2

assert first[
"target_weight"
    ].iloc[0] == pytest.approx(1 / 3)

assert second[
"target_weight"
    ].iloc[0] == pytest.approx(0.5)


deftest_account_snapshot():

    account = Account(
        cash=10000.0,
        positions={
"A": 100,
"B": 200,
        },
    )

    prices = {
"A": 20.0,
"B": 30.0,
    }

    nav, snapshot = (
        calculate_account_snapshot(
            account,
            prices,
        )
    )

assert nav == pytest.approx(
18000.0
    )

assert snapshot[
"actual_weight"
    ].sum() == pytest.approx(
8000 / 18000
    )


deftest_generate_buy_orders():

    target = pd.DataFrame({
"symbol": ["A", "B"],
"target_weight": [0.5, 0.5],
    })

    account = Account(
        cash=100000.0,
        positions={},
    )

    prices = {
"A": 20.0,
"B": 25.0,
    }

    orders = generate_order_intents(
        target,
        account,
        prices,
    )

    a = orders.loc[
        orders["symbol"] == "A"
    ].iloc[0]

    b = orders.loc[
        orders["symbol"] == "B"
    ].iloc[0]

assert a["side"] == "BUY"
assert b["side"] == "BUY"

assert a["order_quantity"] == 2500
assert b["order_quantity"] == 2000


deftest_generate_sell_order():

    target = pd.DataFrame({
"symbol": ["B"],
"target_weight": [1.0],
    })

    account = Account(
        cash=0.0,
        positions={
"A": 100,
        },
    )

    prices = {
"A": 20.0,
"B": 10.0,
    }

    orders = generate_order_intents(
        target,
        account,
        prices,
    )

    a = orders.loc[
        orders["symbol"] == "A"
    ].iloc[0]

assert a["side"] == "SELL"
assert a["order_quantity"] == 100


deftest_invalid_weights():

    target = pd.DataFrame({
"symbol": ["A", "B"],
"target_weight": [0.4, 0.4],
    })

    account = Account(
        cash=100000.0,
        positions={},
    )

with pytest.raises(ValueError):

        generate_order_intents(
            target,
            account,
            {
"A": 10.0,
"B": 20.0,
            },
        )

运行:

pytest -q tests/test_equal_weight.py

再运行:

python -m scripts.build_equal_weight

十、今天的高质量练习题

理论题

题1:目标权重与实际权重有什么区别?为什么二者可能不相等?

题2:一个 5 只股票的等权组合,初始资金 100 万元,每只股票分配多少?

题3:如果一只股票价格为 36.80 元,目标分配资金为 20000 元,按照 100 股整手买入,最多可以买多少股?剩余多少现金?

题4:为什么组合净值上涨之后,原来的等权组合可能变成非等权?

题5:为什么订单意图不能直接当作实际成交?

编程题

题6:修改 build_equal_weight_target(),支持 10% 的现金预留。

例如:

A 18%
B 18%
C 18%
D 18%
E 18%
现金 10%

题7:编写一个 calculate_weight_drift()函数,比较目标权重与实际权重。

题8:设计一个再平衡阈值,例如:

rebalance_threshold = 0.03

只有目标权重与实际权重的偏差超过 3 个百分点,才产生调整意图。

题9:在不考虑手续费的前提下,计算整手买入造成的剩余现金,并计算实际股票权重。

题10:思考:如果账户已经持有某只股票,但当天因为 T+1 限制不能卖出,应该如何处理目标组合?


十一、部分练习参考答案

题3:整手买入

买入金额:

剩余现金:

题6:保留 10% 现金

可以把 build_equal_weight_target()扩展为:

defbuild_equal_weight_target(
    signals: pd.DataFrame,
    cash_reserve: float = 0.0,
)
 -> pd.DataFrame:


ifnot0 <= cash_reserve < 1:
raise ValueError(
"cash_reserve必须在[0,1)之间"
        )

# 省略原有输入验证代码

    df = signals.copy()

    counts = df.groupby(
"signal_date"
    )["symbol"].transform("size")

    df["target_weight"] = (
        (1.0 - cash_reserve) / counts
    )

return df

这里有一个配套修改:今天 generate_order_intents()要求目标股票权重合计为 1。如果支持现金预留,也必须将该验证条件修改为允许权重合计小于等于 1,并明确剩余部分代表目标现金权重。

这两个函数需要一起调整,不能只改其中一个。

题7:计算权重偏离

defcalculate_weight_drift(
    target: pd.DataFrame,
    snapshot: pd.DataFrame,
)
 -> pd.DataFrame:


    result = target.merge(
        snapshot[
            ["symbol", "actual_weight"]
        ],
        on="symbol",
        how="outer",
    )

    result["target_weight"] = (
        result["target_weight"].fillna(0)
    )

    result["actual_weight"] = (
        result["actual_weight"].fillna(0)
    )

    result["weight_drift"] = (
        result["target_weight"]
        - result["actual_weight"]
    )

return result

注意,目标组合中不存在、但账户仍持有的股票,也必须纳入比较。


十二、Day33 工程验收


十三、Day33 总结

今天,我们完成了从 Top-K 选股到等权组合构建的工程衔接。

目前 quant_ml已经具备:

真实A股数据
     │
     ▼
特征工程与收益标签
     │
     ▼
Linear / LightGBM / CatBoost
     │
     ▼
IC / RankIC / ICIR
     │
     ▼
Feature Importance / SHAP
     │
     ▼
Top-K模型选股
     │
     ▼
等权目标组合
     │
     ▼
当前账户净值与权重
     │
     ▼
计划订单
     │
     ▼
Day34:历史回测引擎

今天最重要的一句话:

模型决定想买什么,组合构建决定资金如何分配,而交易执行决定最终真正持有什么。三者必须严格分离。


下一课:Day34——策略回测

按照原定计划,Day34 将进入完整的策略回测:

  • 历史行情与选股信号按时间对齐。
  • T 日盘后生成信号,T+1 执行。
  • 现金、持仓和组合净值更新。
  • 手续费、印花税、滑点。
  • 调仓与交易日志。
  • 策略收益、最大回撤、夏普比率。
  • 对照基准与样本外评价。

Day35 则会把这一阶段的模块整合为完整的 ML 选股系统。

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-11-08 01:50:11 HTTP/1.1 GET : http://h.sjds.net/a/578305.html
  2. 运行时间 : 0.078483s [ 吞吐率:12.74req/s ] 内存消耗:4,530.18kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=8adaa07f375161d9451df4af05ab3d68
  1. /www/wwwroot/h.sjds.net/public/index.php ( 0.79 KB )
  2. /www/wwwroot/h.sjds.net/vendor/autoload.php ( 0.17 KB )
  3. /www/wwwroot/h.sjds.net/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /www/wwwroot/h.sjds.net/vendor/composer/platform_check.php ( 0.90 KB )
  5. /www/wwwroot/h.sjds.net/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /www/wwwroot/h.sjds.net/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /www/wwwroot/h.sjds.net/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /www/wwwroot/h.sjds.net/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /www/wwwroot/h.sjds.net/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /www/wwwroot/h.sjds.net/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /www/wwwroot/h.sjds.net/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /www/wwwroot/h.sjds.net/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /www/wwwroot/h.sjds.net/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /www/wwwroot/h.sjds.net/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /www/wwwroot/h.sjds.net/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /www/wwwroot/h.sjds.net/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /www/wwwroot/h.sjds.net/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /www/wwwroot/h.sjds.net/app/provider.php ( 0.19 KB )
  23. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /www/wwwroot/h.sjds.net/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /www/wwwroot/h.sjds.net/app/common.php ( 0.03 KB )
  27. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /www/wwwroot/h.sjds.net/config/app.php ( 0.95 KB )
  30. /www/wwwroot/h.sjds.net/config/cache.php ( 0.78 KB )
  31. /www/wwwroot/h.sjds.net/config/console.php ( 0.23 KB )
  32. /www/wwwroot/h.sjds.net/config/cookie.php ( 0.56 KB )
  33. /www/wwwroot/h.sjds.net/config/database.php ( 2.48 KB )
  34. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /www/wwwroot/h.sjds.net/config/filesystem.php ( 0.61 KB )
  36. /www/wwwroot/h.sjds.net/config/lang.php ( 0.91 KB )
  37. /www/wwwroot/h.sjds.net/config/log.php ( 1.35 KB )
  38. /www/wwwroot/h.sjds.net/config/middleware.php ( 0.19 KB )
  39. /www/wwwroot/h.sjds.net/config/route.php ( 1.89 KB )
  40. /www/wwwroot/h.sjds.net/config/session.php ( 0.57 KB )
  41. /www/wwwroot/h.sjds.net/config/trace.php ( 0.34 KB )
  42. /www/wwwroot/h.sjds.net/config/view.php ( 0.82 KB )
  43. /www/wwwroot/h.sjds.net/app/event.php ( 0.25 KB )
  44. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /www/wwwroot/h.sjds.net/app/service.php ( 0.13 KB )
  46. /www/wwwroot/h.sjds.net/app/AppService.php ( 0.26 KB )
  47. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /www/wwwroot/h.sjds.net/vendor/services.php ( 0.14 KB )
  53. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /www/wwwroot/h.sjds.net/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /www/wwwroot/h.sjds.net/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /www/wwwroot/h.sjds.net/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /www/wwwroot/h.sjds.net/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /www/wwwroot/h.sjds.net/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /www/wwwroot/h.sjds.net/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /www/wwwroot/h.sjds.net/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /www/wwwroot/h.sjds.net/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /www/wwwroot/h.sjds.net/app/Request.php ( 0.09 KB )
  84. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /www/wwwroot/h.sjds.net/app/middleware.php ( 0.25 KB )
  86. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /www/wwwroot/h.sjds.net/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /www/wwwroot/h.sjds.net/route/app.php ( 1.72 KB )
  100. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /www/wwwroot/h.sjds.net/app/controller/Index.php ( 4.81 KB )
  104. /www/wwwroot/h.sjds.net/app/BaseController.php ( 2.05 KB )
  105. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /www/wwwroot/h.sjds.net/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /www/wwwroot/h.sjds.net/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /www/wwwroot/h.sjds.net/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /www/wwwroot/h.sjds.net/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /www/wwwroot/h.sjds.net/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /www/wwwroot/h.sjds.net/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /www/wwwroot/h.sjds.net/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /www/wwwroot/h.sjds.net/runtime/temp/c1d6e8c94b2c2fa10e259903674c804d.php ( 8.11 KB )
  140. /www/wwwroot/h.sjds.net/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000537s ] mysql:host=172.18.0.4;port=3306;dbname=h_sjds;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000797s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000383s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000359s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000512s ]
  6. SELECT * FROM `set` [ RunTime:0.000280s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000558s ]
  8. SELECT * FROM `article` WHERE `id` = 578305 LIMIT 1 [ RunTime:0.000543s ]
  9. UPDATE `article` SET `lasttime` = 1794073812 WHERE `id` = 578305 [ RunTime:0.002079s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.000305s ]
  11. SELECT * FROM `article` WHERE `id` < 578305 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000399s ]
  12. SELECT * FROM `article` WHERE `id` > 578305 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000353s ]
  13. SELECT * FROM `article` WHERE `id` < 578305 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000617s ]
  14. SELECT * FROM `article` WHERE `id` < 578305 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.000657s ]
  15. SELECT * FROM `article` WHERE `id` < 578305 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000596s ]
0.090137s