4. NumPy 基础:数组创建、数组运算、随机数生成、统计函数
在量化回测中,坏账率哨兵需要高效处理大量时间序列数据与随机模拟。NumPy 作为 Python 科学计算的核心库,提供了高性能的多维数组对象与丰富的数学函数。本章将围绕数组创建、运算、随机数生成与统计函数四个模块,构建回测分析的数据基础。
4.1 数组创建:从列表到结构化数据
NumPy 数组(ndarray)是存储同类型数据的多维容器。创建数组的常用方式包括:
| 创建方式 | 函数/语法 | 示例 | 说明 |
|---|---|---|---|
| 从列表转换 | np.array() |
np.array([1,2,3]) |
最基础方式,自动推断 dtype |
| 全零数组 | np.zeros() |
np.zeros((3,4)) |
常用于初始化权重或占位 |
| 全一数组 | np.ones() |
np.ones((2,3)) |
用于单位矩阵或累加基值 |
| 等差序列 | np.arange() |
np.arange(0,10,2) |
类似 range,返回数组 |
| 等间隔数列 | np.linspace() |
np.linspace(0,1,5) |
指定点数,自动计算步长 |
| 单位矩阵 | np.eye() |
np.eye(3) |
对角线为 1,其余为 0 |
import numpy as np
# 创建坏账率模拟数据(5个时间点,3个资产组合)
bad_rates = np.array([[0.02, 0.03, 0.01],
[0.04, 0.05, 0.02],
[0.03, 0.04, 0.03],
[0.05, 0.06, 0.02],
[0.04, 0.05, 0.04]])
print(bad_rates.shape) # (5, 3)
print(bad_rates.dtype) # float64
回测应用提示: 使用
np.zeros((n_days, n_assets)) 预分配坏账率矩阵,可避免循环中动态扩展数组带来的性能损失。
4.2 数组运算:向量化加速回测计算
NumPy 的向量化运算避免了 Python 原生循环,是回测性能的关键。核心运算分为三类:
- 算术运算: 加、减、乘、除、幂运算,逐元素执行。
- 比较运算: 返回布尔数组,用于条件筛选与阈值判断。
- 广播机制: 不同形状数组运算时自动扩展维度。
# 算术运算:计算坏账率变化
rates = np.array([0.02, 0.04, 0.03, 0.05, 0.04])
threshold = 0.035
# 向量化比较
exceed_mask = rates > threshold
print(exceed_mask) # [False True False True True]
# 广播:每个资产组合的坏账率偏移
offsets = np.array([0.005, -0.002, 0.001])
adjusted_rates = bad_rates + offsets # 形状 (5,3) + (3,) → (5,3)
print(adjusted_rates[:2])
性能对比: 处理 100 万笔贷款数据时,向量化运算比 Python 循环快 50~100 倍,直接决定回测哨兵能否实时响应。
4.3 随机数生成:蒙特卡洛模拟坏账路径
量化回测中,随机数用于生成坏账率的可能演化路径,评估极端风险。NumPy 的 random 模块提供多种分布:
| 函数 | 分布类型 | 回测应用场景 |
|---|---|---|
np.random.rand() |
均匀分布 [0,1) | 随机抽样、违约事件触发 |
np.random.randn() |
标准正态分布 | 坏账率波动建模 |
np.random.binomial() |
二项分布 | 违约次数模拟 |
np.random.poisson() |
泊松分布 | 低频高损事件计数 |
np.random.seed() |
随机种子 | 保证回测结果可复现 |
# 设置随机种子,确保结果可复现
np.random.seed(42)
# 模拟 1000 次蒙特卡洛路径,每次 252 个交易日
n_paths = 1000
n_days = 252
base_rate = 0.03
volatility = 0.01
# 生成正态随机增量
increments = np.random.randn(n_paths, n_days) * volatility
# 累积坏账率路径(简化模型)
bad_rate_paths = base_rate + np.cumsum(increments, axis=1)
print(bad_rate_paths.shape) # (1000, 252)
print("第 1 条路径最后一天坏账率:", bad_rate_paths[0, -1])
种子管理: 在回测框架中,务必在每次模拟前调用
np.random.seed(),否则不同运行批次的结果不可比较,导致策略评估失效。
4.4 统计函数:描述与推断坏账分布
NumPy 提供丰富的统计函数,用于描述坏账率数据的集中趋势、离散程度与分布形态:
- 集中趋势:
np.mean()、np.median()、np.percentile() - 离散程度:
np.std()、np.var()、np.ptp()(极差) - 相关性:
np.corrcoef()、np.cov() - 累积与聚合:
np.cumsum()、np.sum()、np.max()、np.min()
# 对蒙特卡洛模拟结果进行统计
final_rates = bad_rate_paths[:, -1] # 所有路径的最后一天坏账率
mean_rate = np.mean(final_rates)
std_rate = np.std(final_rates)
p95 = np.percentile(final_rates, 95)
p99 = np.percentile(final_rates, 99)
print(f"平均坏账率: {mean_rate:.4f}")
print(f"标准差: {std_rate:.4f}")
print(f"95% 分位点: {p95:.4f}")
print(f"99% 分位点: {p99:.4f}")
# 计算不同资产组合的坏账率相关性
corr_matrix = np.corrcoef(bad_rates.T) # 转置为 (3,5) → (3,3) 相关矩阵
print("资产组合坏账率相关系数矩阵:\n", corr_matrix)
4.5 综合实战:坏账率哨兵统计模块
将数组创建、运算、随机数与统计函数整合为一个哨兵统计模块,用于实时监控坏账率异常:
class BadRateSentinel:
"""坏账率哨兵统计模块"""
def __init__(self, base_rate=0.03, window=60, threshold_percentile=95):
self.base_rate = base_rate
self.window = window
self.threshold_percentile = threshold_percentile
self.history = np.array([])
def update(self, new_rate):
"""更新坏账率观测值"""
self.history = np.append(self.history, new_rate)
if len(self.history) > self.window:
self.history = self.history[-self.window:]
def check_alert(self):
"""检查是否触发预警"""
if len(self.history) < 30:
return False, "数据不足"
current = self.history[-1]
threshold = np.percentile(self.history, self.threshold_percentile)
if current > threshold:
return True, f"坏账率 {current:.4f} 超过 {self.threshold_percentile}% 分位阈值 {threshold:.4f}"
return False, "正常"
# 模拟使用
sentinel = BadRateSentinel()
np.random.seed(2024)
for day in range(100):
rate = 0.03 + np.random.randn() * 0.005
sentinel.update(rate)
if day % 20 == 0:
alert, msg = sentinel.check_alert()
print(f"第 {day+1} 天: {msg}")