numpy_demo · 向量化演示 2026-09-27

内容:
从 quant_data 导入"拉数据"函数,拿到茅台真实日线
收盘价转成 numpy 数组
日收益率:for 循环 vs 向量化,性能对比
numpy 常用操作:切片 / 统计 / np.where 生成信号

导入与主入口

import time # time:Python 自带,用来给代码计时 import numpy as np # numpy:数值计算库(发动机) import pandas as pd # pandas:表格处理库(工作台) # from ... import ...:从别的文件里"拿"一个函数来用 # quant_data 是咱们自己写的数据工具箱(quant_data.py),里面有 get_kline from quant_data import get_kline # get_kline:拉 A 股日线(自动兜底数据源) if __name__ == "__main__": # 只有直接运行本文件时才执行下面的代码

1. 拉取真实日线

# ========== 1. 拉取真实日线 ========== df = get_kline() # 调用函数,一行拿到 近1月日线表格 print("\n----- 最近 5 个交易日 -----") print(df.tail(5).to_string()) # .tail(5):取最后 5 行;.to_string():转成文字打印 print("\n共", len(df), "条数据,每一条都是从服务器实时拉下来的真实行情") # len(df):表格有多少行(多少天)

2. 收盘价 → numpy 数组

# ========== 2. 收盘价 -> numpy 数组 ========== close = df["close"].to_numpy() # df["close"]:取出"收盘价"这一列(Series) # .to_numpy():把这一列转成 numpy 数组(发动机的"专用油") print("\n收盘价数组(前 5 个):", close[:5]) # \n:换行 # close[:5]:切片,取前 5 个元素(开头到第 5 个) print("数组形状:", close.shape) # .shape:通常就是和数组一起使用,它用于返回数组的维度信息;这里说明下这是属性不是函数; # 就是说数组长什么样,比如 (1360,) 表示一维、1360 个元素;再比如(2, 3) 表示一个2行3列的二维数组矩阵,以此类推。

3. 日收益率:for 循环 vs 向量化

先用真实数据验证两种写法结果一致,再放大到 200 万条感受性能差距。

for 循环版本

# ========== 3. 日收益率:for 循环 vs 向量化 ========== # 先用真实数据验证两种写法结果一致 ret_loop = [] # 建一个空列表,准备装每天的收益率 for i in range(1, len(close)): # 循环:从第 2 天到最后一天 # 日收益率 = (今天价格 - 昨天价格) / 昨天价格 today_ret = (close[i] - close[i - 1]) / close[i - 1] ret_loop.append(today_ret) # .append():把结果装进列表末尾 ret_loop = np.array(ret_loop) # 列表转成 numpy 数组,方便和下面比较

向量化版本

ret_vec = np.diff(close) / close[:-1] # np.diff(close):相邻两天的差值(今天-昨天),自动得到 len-1 个 # close[:-1]:切片,去掉最后一个元素([:-1] 表示"开头到倒数第 2 个") # 一行就算完了整段历史的收益率 —— 这就是向量化 print("\n逻辑验证:两种写法结果一致:", np.allclose(ret_loop, ret_vec, equal_nan=True)) # np.allclose(数组1, 数组2):判断两个数组里的数是不是基本一样

放大到 200 万条 · 性能对比

# 再把真实序列复制放大到约 200 万条,感受真实量化场景的差距 big = np.tile(close, int(2_000_000 / len(close)) + 1) # np.tile(数组, n):把数组整体重复 n 遍 # 2_000_000 就是 2000000,下划线只是方便人看 print(f"放大后的数组大小: {len(big):,} 条") # f-string:{变量} 把值塞进字符串 # {len(big):,} 里的 , 表示千分位分隔符,显示成 2,000,560 t0 = time.perf_counter() # 记下"现在"的时间(计时开始) ret_loop_big = [] # 空列表 for i in range(1, len(big)): # 循环 200 万次,一格一格算 ret_loop_big.append((big[i] - big[i - 1]) / big[i - 1]) ret_loop_big = np.array(ret_loop_big) # 转数组 t_loop = time.perf_counter() - t0 # 计时结束,算出 for 循环用了多少秒 t0 = time.perf_counter() # 计时开始 ret_vec_big = np.diff(big) / big[:-1] # 向量化,一行算完 t_vec = time.perf_counter() - t0 # 计时结束,算出向量化用了多少秒 print("\n----- 性能对比(同样的数学,不同的写法)-----") print(f"for 循环 : {t_loop * 1000:8.1f} ms") # 秒乘 1000 变成毫秒(ms);{:8.1f} 表示宽度 8、保留 1 位小数 print(f"向量化 : {t_vec * 1000:8.1f} ms") # 防止除数为 0,用 max() 兜底一下 print(f"快约 {t_loop / max(t_vec, 1e-9):.0f} 倍") # 大数字除以小数字 = 快了多少倍;{:0f} 表示不留小数

4. numpy 常用操作

# ========== 4. 常用操作 ========== print("\n----- numpy 常用操作 -----") print("最近 5 天收盘价 close[-5:]:", close[-5:]) # close[-5:]:负数切片,从倒数第 5 个一直到最后(最近的 5 天) print("全部均值 close.mean() :", round(close.mean(), 2)) # .mean():求平均值;round(数, 2):保留 2 位小数 print("波动 close.std() :", round(close.std(), 2)) # .std():标准差,衡量价格波动大不大 print("最高 close.max() :", close.max()) # 最大值 print("最低 close.min() :", close.min()) # 最小值 # np.where(条件, 满足填什么, 不满足填什么):生成 0/1 信号 signal = np.where(close > close.mean(), 1, 0) print("\n信号序列(前 20 个):", signal[:20]) print("含义:1 = 收盘价高于五年均值,0 = 低于均值 —— 这就是策略信号的雏形")

核心要点速览

数组转换:df["close"].to_numpy()
向量化:np.diff(close) / close[:-1]
for 循环:一格一格算,慢但直观
性能对比:200 万条数据实测
切片:close[-5:] 取最近 5 天
统计:mean / std / max / min
信号:np.where(close > mean, 1, 0)
计时:time.perf_counter()
金句: np.diff 一口气算出全部差值, np.where 一行生成 0/1 信号 —— numpy 把"数学"变成"一行代码"。

运行输出 · python .\numpy_demo.py

PS> python .\numpy_demo.py [数据源] akshare 不可用(ProxyError),尝试 baostock... login success! logout success! [数据源] baostock 获取成功,共 178 条 ----- 最近 5 个交易日 ----- open high low close volume date 2026-09-18 1262.99 1265.88 1256.10 1257.12 2489087 2026-09-21 1259.00 1259.95 1250.80 1252.57 2501689 2026-09-22 1252.15 1265.88 1248.10 1253.80 2457294 2026-09-23 1255.03 1271.50 1250.89 1251.24 3098122 2026-09-24 1250.01 1256.13 1231.05 1237.00 3123935 共 178 条数据,每一条都是从服务器实时拉下来的真实行情 收盘价数组(前 5 个): [1393.035158 1394.99869283 1390.45618688 1379.6518609 1386.2946653 ] 数组形状: (178,) 逻辑验证:两种写法结果一致: True 放大后的数组大小: 2,000,008 条 ----- 性能对比(同样的数学,不同的写法)----- for 循环 : 5130.7 ms 向量化 : 26.4 ms 快约 194 倍 ----- numpy 常用操作 ----- 最近 5 天收盘价 close[-5:]: [1257.12 1252.57 1253.8 1251.24 1237. ] 全部均值 close.mean() : 1326.43 波动 close.std() : 76.99 最高 close.max() : 1519.053065 最低 close.min() : 1168.63 信号序列(前 20 个): [1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1] 含义:1 = 收盘价高于五年均值,0 = 低于均值 —— 这就是策略信号的雏形
关键结论:同样的数学,向量化比 for 循环快约 194 倍(5130.7 ms → 26.4 ms)。