Betalens新手系列 · 14 BacktestBase 全参数拆解
阅读提示
这篇是 BacktestBase 的参数手册,详细讲解每个参数的作用、默认值和调优思路。如果你想深入理解回测引擎的行为,这篇是必读的。
导言
BacktestBase 是 Betalens 回测引擎的核心类。它把一个权重矩阵(行=调仓日,列=股票+cash)转换成一组时间序列:净值、实际持仓、每日盈亏、调仓日志。理解它的参数,是调出”真实”回测结果的第一步。
完整函数签名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| from betalens.backtest import BacktestBase
engine = BacktestBase( weight, symbol, amount,
ftc=0.0, ptc=0.0,
metric="收盘价(元)", time_tolerance=24, table_name="daily_market",
check_trade_status=True, trade_status_mode="to_cash", trade_status_table="trade_status", lot_size=100,
verbose=True, )
|
参数逐个拆解
weight:目标权重矩阵
最常见的错误:忘记补 cash 列:
1 2 3 4 5 6
| engine = BacktestBase(weight=weights, ...)
weights["cash"] = 0 weights["cash"] = 1 - weights.iloc[:, :-1].sum(axis=1)
|
amount:初始资金
初始资金决定了你能否买够整数手。如果金额太小,会出现”有钱但买不了任何股票”的困境。
1 2 3 4 5 6
| engine = BacktestBase(weight=weights, amount=1_000_000, ...)
print(engine.actual_weight.iloc[:, :-1].abs().sum(axis=1).mean())
|
经验法则:初始资金 ≥ 组内股票数量 × 单手最低金额(≈ 股价 × 100)。如果每个调仓日有 20 只股票,平均股价 20 元,最小资金 ≈ 20 × 100 × 20 = 40,000 元。实际建议用 100 万以上。
ftc / ptc:交易成本
1 2 3 4 5 6 7 8 9 10 11 12 13
| engine = BacktestBase(weight=weights, ftc=0.0, ptc=0.0, ...)
engine = BacktestBase( weight=weights, ftc=5.0, ptc=0.0013, ... )
|
注意:ptc 是买卖双向收取的。如果你想模拟”只收印花税”(卖出时才收),目前的 ptc 参数是双向的,需要在分析阶段单独处理印花税。框架未来可能支持单向 ptc_sell 参数。
metric:成交价格字段
1 2 3 4 5 6 7 8
| engine = BacktestBase(weight=weights, metric="收盘价(元)", ...)
engine = BacktestBase(weight=weights, metric="复权收盘价(元)", ...)
engine = BacktestBase(weight=weights, metric="开盘价(元)", ...)
|
time_tolerance:价格容差
1
| engine = BacktestBase(weight=weights, time_tolerance=24, ...)
|
含义:如果某天某股票的价格数据缺失(如数据源断了一天),最多往前找多少小时的数据来填充。默认 24 小时,即最多往前找一天。
check_trade_status:停牌检查
1 2 3 4 5
| engine = BacktestBase(weight=weights, check_trade_status=True, ...)
engine = BacktestBase(weight=weights, check_trade_status=False, ...)
|
关闭后,停牌股票会按正常价格成交——这在研究中有时有用(如估算”如果不考虑停牌,收益会高多少”),但不适合作为实际策略的参考。
trade_status_mode:停牌处理策略
1 2
| engine = BacktestBase(weight=weights, trade_status_mode="redistribute", ...)
|
lot_size:每手股数
1 2 3 4 5
| engine = BacktestBase(weight=weights, lot_size=100, ...)
|
回测结果:所有产出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| engine = BacktestBase(...)
nav = engine.nav print(nav.tail())
actual_weight = engine.actual_weight
position = engine.position
daily_pnl = engine.daily_pnl daily_pnl_total = engine.daily_pnl_total
rebalance_log = engine.rebalance_log
trade_status_matrix = engine.trade_status_matrix
daily_position_value = engine.daily_position_value
|
开发者侧:整数手约束的实现
1 2 3 4 5 6 7 8 9
| for each rebalance_date: for each stock in target_weights: target_shares = (target_weight * portfolio_value) / price actual_shares = trunc(target_shares / lot_size) * lot_size actual_value = actual_shares * price cash += target_value - actual_value
actual_weights = actual_values / portfolio_value
|
注意 trunc(向零取整)的含义:-3.7 股 → -3 股,3.7 股 → 3 股。这和 floor(向下取整)有区别——空头会多买 1 手(因为向零取整比向下取整更接近 0)。这是 A 股整数手撮合的一个细微但重要的特性。
常见错误
1. 权重矩阵的索引不是 DatetimeIndex
1 2 3 4 5 6
| engine = BacktestBase(weight=weights, ...)
weights.index = pd.to_datetime(weights.index) engine = BacktestBase(weight=weights, ...)
|
2. ptc 双向收费导致成本高估
当前 ptc 是买卖双向的。如果只想模拟印花税(卖出时才收),需要手动在结果上调整,或者把 ptc 设为 0,用 ftc 来单独处理卖出印花税。
3. 资金太小导致 actual_weight 全 0
这是整数手约束导致的必然结果,不是 bug。增加 amount 或减少持仓数量(减少分组数)。
延伸阅读