☰
量化笔试题十家公司核心考点:概率、因子与编程边界
2026/10/15 15:17:11 网站建设 项目流程

简介:这是面向量化投资、金融工程及算法交易岗位求职者的笔试题解析资源,汇总诚奇、概率投资、海悦、天演、衍复、佳期、平方和、集微、九坤、凯读投资十家公司的题型方向与配套答案。内容覆盖线性回归、时间序列、蒙特卡洛模拟、GARCH波动率模型、Black-Scholes期权定价、统计套利、协整关系、市场微观结构与高频交易等核心知识点,并深入涉及遗传算法在组合优化中的应用、基于机器学习的预测策略、深度学习与自然语言处理支撑的市场情绪分析,以及DCF、PE、PB等基本面估值模型梳理。除理论知识外,资源还结合Python、Pandas、NumPy、Matlab、R等常用工具讲解实际投资场景中的落地解法与编程思路。zip压缩包约48.43MB,已有752人学习下载,适合备战量化笔试、系统查漏补缺的求职者。

1. 量化笔试题十家公司附答案:LeetCode 刷满也没用,真正卡人的是这三块

量化笔试题十家公司附答案,这类资料很多求职者手里都有一份,但真正吃透的没几个。我见过一位候选人刷了三百多道 LeetCode,却在某头部量化的笔试里被“抛硬币直到连续两次正面,期望要抛多少次”直接卡住。不是题难,是他根本没往这个方向准备。

把十家公司的量化笔试题放在一起看,规律非常明显:数学概率、统计建模、编程与机器学习三块占了八成以上。纯算法题只是其中一小部分,真正拉开差距的是概率题的推导严谨性、因子分析里的数据对齐,以及手撕代码时的边界条件敏感度。这篇文章不罗列公司名单,而是把十家公司共有的考点拆开,每道题给出可复现的答案和验证代码。适合正在准备量化研究或量化开发笔试的人,也适合刚入行想系统补基础的同学。

2. 十家公司笔试的必考底盘:数学与概率题的三类考法

2.1 期望题:状态设对了,答案就出来一半

期望题在十家公司的笔试里出现率接近八成,常见形式不是简单的掷骰子,而是“直到某事件发生”的停止时间问题。比如这道连续抛硬币的题:一枚均匀硬币,直到出现连续两次正面朝上就停止,问平均要抛多少次。

这类题的通用解法是设状态。令 E0 表示“当前连续正面数为 0,或上一次是反面”时期望的剩余次数,E1 表示“当前已经有一次正面”时期望的剩余次数,停止时 E 为 0。从 E0 出发抛一次,一半概率出反面回到 E0,一半概率出正面进入 E1,于是 E0 = 1 + 0.5 × E0 + 0.5 × E1。从 E1 出发抛一次,出正面直接停止,出反面回到 E0,所以 E1 = 1 + 0.5 × E0。联立解得 E0 = 6,E1 = 4。

import numpy as np rng = np.random.default_rng(42) trials = 100_000 counts = [] for _ in range(trials): n, streak = 0, 0 while streak < 2: streak = streak + 1 if rng.integers(0, 2) == 1 else 0 n += 1 counts.append(n) print(f"模拟期望 = {np.mean(counts):.3f},理论值 = 6")

这里随机种子 42 是为了结果可复现;trials 设成 10 万次,均值基本稳定在小数点后两位。streak 记录当前连续正面次数,出反面就清零,这个状态转移正好对应上面方程组里“回到 E0”的分支。把循环次数从 1 万改到 100 万,你会发现均值始终在 6 附近,这就是蒙特卡洛验证的价值——笔试时如果一时推不出解析解,先用模拟摸到答案,再回头列方程。

这道题的变形也很常见:连续 k 次正面才停止,期望次数是 2^(k+1) − 2。比如 k=3 时期望是 14,k=4 是 30。直接背这个结论能省很多时间,但笔试更看重你怎么从状态方程推出它。

2.2 贝叶斯题:先验怎么给,答案能差一个数量级

贝叶斯题是另一类高频考点,典型场景是检测问题。某稀有事件真实发生率是 0.1%,检测灵敏度为 99%,假阳性率为 1%。某人检测结果呈阳性,问真实发生的概率是多少。直觉上很多人会答 99%,但真实答案不足 10%。

def posterior(prior, sensitivity, false_alarm_rate): num = prior * sensitivity den = num + (1 - prior) * false_alarm_rate return num / den print(f"先验 0.1%: {posterior(0.001, 0.99, 0.01):.2%}") print(f"先验 1%: {posterior(0.01, 0.99, 0.01):.2%}")

这个函数就是把贝叶斯公式拆成分子分母两行:分子是“真患病且检测阳性”,分母再加上“没患病但误报”。先验从 0.1% 提到 1%,后验从 9% 跳到 50%,同一个检测器,结论完全不同。笔试里常见的错误是只看灵敏度,忽略基数率;面试追问通常是“如果做了两次独立检测都阳性呢”,这时要用贝叶斯更新,把第一次的后验当第二次的先验,而不是简单把两个 99% 相乘。

这类题在量化里的实际对应物是策略胜率判断。一个信号的历史胜率 55%,但这是在样本内估计的,先验本身就有噪声,直接用后验做仓位决策会翻车。

2.3 大数定律与中心极限定理:收敛速度决定样本量

第三类数学题考极限与收敛,表面问“要多少样本才能让均值稳定”,实际考中心极限定理。样本均值的标准误是 σ/√n,想让标准误减半,样本量要翻 4 倍,不是 2 倍。这是笔试里最容易被直觉带偏的点。

import numpy as np rng = np.random.default_rng(7) rets = rng.normal(0.0005, 0.01, 5000) for n in (50, 500, 5000): x = rets[:n] se = x.std(ddof=1) / np.sqrt(n) half_width = 1.96 * se print(f"n={n:5d} 均值={x.mean():.5f} 95%半宽={half_width:.5f}")

ddof=1 表示用样本标准差,np.sqrt(n) 就是中心极限定理里的收敛速度。n 从 50 涨到 500,半宽只缩到原来的 0.32 左右,这正是 √n 的特性。量化笔试的考法通常是:回测了 5 年日频数据,年化收益均值是 8%,标准误是多少;或者问“要多少年数据才能把年化收益的置信区间压到 ±2%”。答案不是线性外推,而是按平方反推。

这张表可以直接记:样本量变为 4 倍,标准误变为原来的一半;变为 9 倍,标准误变为三分之一。量化里很多“为什么别人家的回测曲线那么平”的问题,本质都是用了更长样本或者更低频的数据,把标准误压下来了,不是策略真的更稳。

样本量倍数标准误变化
1 倍1.00
4 倍0.50
9 倍0.33
16 倍0.25

3. 统计与回归题:从平稳性检验到因子 IC 计算

3.1 ADF 检验:判断序列能不能进入模型

统计题里出现频率最高的是平稳性检验。价格序列和收益序列,哪个能直接进回归模型,这个考点几乎十家必考。答案很明确:价格通常是随机游走,不平稳;收益序列基本平稳。直接用价格做回归,R 平方可能高得吓人,但那是伪回归,是典型的黑匣子陷阱。

import numpy as np from statsmodels.tsa.stattools import adfuller rng = np.random.default_rng(42) rets = rng.normal(0, 0.01, 1000) # 模拟日收益率 price = np.cumsum(rets) # 价格是收益的累加 for name, series in [("returns", rets), ("price", price)]: stat, pval, usedlag, nobs, crit, icbest = adfuller( series, autolag="AIC", regression="c" ) print(f"{name}: ADF={stat:.3f}, p={pval:.3f} => " f"{'平稳' if pval < 0.05 else '不平稳'}")

adfuller 返回的第一个值是 ADF 统计量,第二个是 p 值;p 小于 0.05 就拒绝“存在单位根”的原假设,认为序列平稳。autolag="AIC" 表示自动选择滞后阶数,一般不用手动指定;regression="c" 表示检验回归里带常数项,如果序列有明显趋势,要改成 "ct" 带趋势项,否则会把确定性趋势误判成随机游走。这里说一个血泪经验:遇到价格类序列,先差分或算收益,再丢进 adfuller,不然你只是在验证一个你知道的结论。

笔试追问通常是“不平稳怎么办”。答案不是直接做差分就是取对数收益,但要记住差分后的序列经济含义变了,你建模的对象从价格水平变成了收益率。

3.2 回归题里的隐性考点:多重共线性与过拟合

统计回归题很少直接让你跑 OLS,而是把坑埋在线性回归的假设里。多重共线性是最常考的一个:两个因子高度相关时,OLS 的系数会互相抵消,单个系数方差极大,模型解释全乱。

import numpy as np rng = np.random.default_rng(1) n = 200 X1 = rng.normal(size=n) X2 = X1 + rng.normal(0, 0.05, size=n) # 与 X1 高度相关 y = X1 * 0.5 + rng.normal(0, 0.2, size=n) X = np.column_stack([X1, X2]) beta_ols = np.linalg.lstsq(X, y, rcond=None)[0] beta_ridge = np.linalg.solve(X.T @ X + 1.0 * np.eye(2), X.T @ y) print("OLS 系数:", np.round(beta_ols, 3)) print("岭回归系数:", np.round(beta_ridge, 3))

X2 是 X1 加上一个小噪声,相关系数接近 0.999。OLS 给出的两个系数可能一个很大一个很小,甚至符号相反,但拟合效果一点不差。岭回归在 X.T @ X 的对角线上加了 1.0 的惩罚项,让系数往互相妥协的方向收缩,单看每个系数稳定得多。1.0 就是正则化强度,调大系数更平缓、偏差更大,调小更接近 OLS 但方差更大。

笔试考到这里,通常还会追问“怎么发现共线性”。常见做法是算方差膨胀因子 VIF,VIF 超过 10 说明该变量和其他变量存在严重线性关系;超过 5 就要警惕。处理手段就是刚才的岭回归、Lasso,或者直接删掉相关性最高的因子之一。

3.3 因子 IC / IR 计算:对齐错了,结论全反

因子题是统计部分和量化业务结合最紧的考点。IC 是因子值与下期收益之间的相关系数,信息比率 IR 等于 IC 均值除以 IC 标准差,代表因子预测能力的稳定性。问题是,很多人在笔试里把“下期”两个字丢了,用当期收益算 IC,得到的结果好看得不真实,实盘却全废。

import pandas as pd from scipy.stats import spearmanr df = pd.DataFrame({ "factor": [1.2, -0.5, 0.8, -1.1, 0.3], "next_ret": [0.02, -0.01, 0.015, -0.03, 0.005], }) ic, p = spearmanr(df["factor"], df["next_ret"]) print(f"rank IC = {ic:.3f}, p = {p:.3f}")

这里用 spearmanr 算的是秩相关系数,比 Pearson 相关系数更稳健,不受极端值影响,这也是业界最常用的 rank IC。数据里 factor 是 t 日因子值,next_ret 是 t+1 日收益,两者必须错开一行。如果用同一行的当期收益,等于用今天的因子解释今天的收益,看起来 IC 高,实际是未来函数。计算时手写代码要注意:先给因子排序算秩,再和下一期收益对齐,最后算相关。p 值小于 0.05 才认为 IC 显著不为零。

笔试里“附答案”的部分最容易在这里丢分——不是不会算,是把 shift 方向搞反。记住一句话:t 日收盘后的因子,最早也只能解释 t+1 日开盘到 t+2 日开盘的收益,中间隔着夜和开盘价差。保守的做法是用 t 日因子对齐 t+1 日收益,再按持仓周期决定要不要跳过一天。

4. 编程与机器学习题:边界条件才是分水岭

4.1 手撕代码:最大子数组和及其边界

编程题在量化笔试里占三到四成,但难度通常不及互联网大厂。考的不是复杂算法,而是基础数据结构和边界条件。最大子数组和是出现率最高的一道,Kadane 算法十行内能写完,但能一次写对边界的人不多。

def max_subarray(nums): if not nums: return 0 best = cur = nums[0] for x in nums[1:]: cur = max(x, cur + x) best = max(best, cur) return best print(max_subarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])) print(max_subarray([-3, -1, -2]))

第一个用例输出 6,对应子数组 [4, -1, 2, 1];第二个用例全是负数,输出 -1 而不是 0,这是最容易踩的坑。cur = max(x, cur + x) 表示每个位置面临两个选择:从当前元素重新开始,或者延续之前的子数组。如果初始化 best = 0,全负数时答案会错成 0,所以必须初始化为 nums[0],同时空数组返回 0 或抛异常,看你跟面试官怎么约定。

复杂度是 O(n) 时间、O(1) 空间,笔试答完顺手写上复杂度,能省后面追问。变形题有两个:一是返回最大子数组的下标区间,二是允许最多删除一个元素后的最大子数组和。后者做法是先算前缀和后缀的最大子段,再枚举删除位置,笔试考到的话属于附加题级别。

4.2 Pandas 向量化:循环写起来爽,跑起来痛

量化笔试题里经常给一段几百行的日频行情数据,让算每只股票的滚动波动率。新手第一反应是用 for 循环遍历股票,老手直接写向量化操作。这道题表面上考 Pandas 用法,实际考的是对性能的敏感度。

import pandas as pd def std_loop(close, window=20): out = {} for col in close.columns: out[col] = close[col].pct_change().rolling(window).std() return pd.DataFrame(out) def std_vec(close, window=20): return close.pct_change().rolling(window).std()

close 是一个 DataFrame,列是各只股票的收盘价。循环版先取出一列算完再放回去,逻辑清晰但慢在多次 DataFrame 创建和索引开销;向量化版直接对整个 DataFrame 调用 pct_change 和 rolling,Pandas 底层会按列并行计算。当股票数量从几十只涨到几千只,循环版耗时可能变成向量化的十几倍,笔试机器的配置越好差距越大。

这里的隐性考点是 rolling 的窗口行为和 min_periods 参数。window=20 意味着前 19 行都是 NaN,后续合并因子数据时要用 dropna 或 fillna 处理,不然会把 NaN 带进回归模型。另一个高频追问是“按时间分组后的滚动计算怎么不跨股票”,比如每个行业一个 panel,要先 groupby 再 rolling,直接在整个 DataFrame 上 rolling 会算出跨股票的伪窗口。

4.3 时间序列交叉验证:KFold 在量化题里是错的

机器学习部分最常考的细节是交叉验证方式。很多候选人把 sklearn 的 KFold 直接用在一堆时间序列上,笔试现场就被指出数据泄漏。量化的标签是未来收益,打乱顺序后训练集里混着未来样本,模型等于开卷考试。

import numpy as np from sklearn.model_selection import KFold, TimeSeriesSplit X = np.arange(10) print("KFold(shuffle=True,训练集会混入未来样本):") for train_idx, val_idx in KFold(5, shuffle=True, random_state=0).split(X): print(f"训练 {train_idx} 验证 {val_idx}") print("TimeSeriesSplit(严格按时间切分):") for train_idx, val_idx in TimeSeriesSplit(3).split(X): print(f"训练 {train_idx} 验证 {val_idx}")

输出里 KFold 的验证集索引会出现在训练集之前,比如训练 [1 4 6 2 8] 验证 [0 5 7],这意味着模型在训练时见过“未来”的少量样本,验证集再也不是独立样本。TimeSeriesSplit 保证训练集永远在验证集之前,第一次训练 [0 1 2] 验证 [3],第二次训练 [0 1 2 3] 验证 [4],以此类推。

更细的做法是在训练集末尾与验证集开头之间再留一段 gap,比如训练到 t,验证从 t+5 开始,防止滚动特征把标签附近的信息带过去。量化笔试里追问“如何判断过拟合”时,这就是比 AUC 更切题的回答。

5. 量化笔试高频踩坑的 5 个现场:现象、原因与解决

5.1 因子对齐错误:IC 高得离谱,实盘全废

现象:某同学回测里因子 IC 常年 0.8 以上,换到实盘曲线直接变成直线。原因:因子和收益之间没有错位,用了当期收益,等于用今天的因子解释今天的涨跌,这就是未来函数。解决:写代码时统一用factor.shift(-1)对齐下一期收益,笔试答题时先画一条时间轴,标清因子发布时间和收益计算区间,再写对齐逻辑。

5.2 概率题只写答案不给推导:答案对了拿不到分

现象:连续两次正面的期望题,直接写了 6,答案完全正确,但得分只有一半。原因:笔试评分看的是思路链条,不是最终数字,答案对而过程空白说明你有背题嫌疑。解决:按“设状态 → 列方程 → 解方程 → 验证”四步写,每步一行核心公式。哪怕最后算错,过程完整也能拿大部分分。

5.3 平稳性检验不看趋势项:把确定性趋势当随机游走

现象:序列明显一路爬升,adfuller 的 p 值却小于 0.05,得出“平稳”的错误结论。原因:regression 参数用默认的 "c" 只带常数项,趋势项没有被建模,单位根检验功效降低。解决:先画序列图再决定参数,看到趋势就用 regression="ct";价格类序列先差分再检验,把“平稳”和“趋势平稳”分开表述。

5.4 手撕代码只求过样例:隐藏用例全挂

现象:样例输入全对,提交后隐藏用例全部失败。原因:只验证了正常路径,没处理空数组、全负数、单元素等边界。解决:写代码时先把边界分支写完,再写主逻辑。具体习惯是:函数第一行处理空输入,第二行初始化首元素,然后才进入循环。这道题在 Kadane 代码里体现得最明显,best 初始化为 nums[0] 而不是 0。

5.5 笔试环境不熟悉:本地 IDE 跑得好,线上编译失败

现象:本地 Jupyter 里数据写死在代码里,线上环境要求从标准输入读,读数据花了一半时间。原因:平时写脚本习惯了直接把数据贴在代码里,没按 OJ 模式练习。解决:提前熟悉目标平台的输入输出格式,用sys.stdin.read()一次性读入再解析,业务逻辑封装成函数,主入口只做 IO。

import sys def solve(nums): if not nums: return 0 best = cur = nums[0] for x in nums[1:]: cur = max(x, cur + x) best = max(best, cur) return best data = sys.stdin.read().split() nums = list(map(int, data[1:])) # 第一个数是长度 print(solve(nums))

这段代码展示的是笔试平台常见的输入格式:第一行是数组长度,后面跟着数组元素。用 sys.stdin 而不是 input() 是防止数据量大的时候逐行读入太慢。没写过 OJ 模式的人,第一次遇到这种格式很容易慌,提前跑两遍就有肌肉记忆。

6. 进阶:把十套笔试题改造成你自己的验证型题库

准备量化笔试,最忌讳“背答案”。背下来的期望题答案是 6,面试官换个说法考“连续三次正面”你还是不会。我自己的习惯是给每道题配一个验证脚本和一个变形记录,把静态答案变成能跑的代码,再把跑出来的结果贴回笔记里。

具体做法是做一个四栏的题集:题目、答案、验证脚本、变形题。第一遍刷题只写题目和自己的解法;第二遍把解法里的关键结论用代码验证一遍,比如期望题跑蒙特卡洛,IC 题造一份假数据跑 spearman;第三遍把遇到的变形题和当时的错误原因补进去。这个迭代过程比刷题数量重要得多。量化的笔试考的就是可计算性,能用代码验证的答案,才算是真正掌握。

我当年准备某家自营团队的笔试时,背了一堆因子 IC 的结论,结果被追问“如果因子值和收益不是线性关系怎么办”,当场卡住。后来我把每道题都改成 mini 回测:造数据、算 IC、切训练验证集,答案和边界条件就刻在脑子里了。之后面试官怎么变形,都能绕回“先验证再下结论”这条主线。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询