DRW 是全球最顶尖的自营交易公司之一,其招聘以数学密度高、淘汰率高著称。不同于一般投行的行为题筛选,DRW 的 OA 直接把概率论、Markov Chain、期权定价和图论算法砸到你面前,限时作答,没有退路。本文整合 Programhelp 平台上多位真实候选人的一手反馈,深度拆解 DRW Online Assessment 的考察逻辑与完整面试流程,帮你在这条竞争最激烈的量化赛道上,提前建立压倒性的优势。

DRW 招募时间线
Step 01 网申
在 DRW 官网提交申请,约 1 周内收到 OA 邀请。部分岗位支持内推(Employee Referral),可加快流程。建议简历突出数学竞赛、编程项目或量化研究经历。
Step 02 OA
- Quant 方向:6 道数学/统计题,约 45 分钟,限时完成,题目含线性代数、概率论、Markov Chain 等。据候选人反馈,有 1 道题目故意设计成无解,考察候选人面对不确定性的心态。
- Dev 方向:Codility 平台,3 道编程题(Easy + Medium + Hard),覆盖字符串处理、贪心算法、图论匹配,约 2 小时。
完成窗口通常为 72 小时,建议选择在安静、网络稳定的环境下完整作答。
Step 03 Phone Screen
约 30 分钟,主要聊背景动机(为什么 DRW?为什么 Quant?)及简单的心算测试。节奏相对轻松,但要准备好清晰的 motivation statement。
Step 04 Technical Interview
由 Junior Quant Trader 或研究员主持,约 45 分钟。考察概率统计、期望值计算、Market Making 逻辑,以及正态分布、置信区间等基础题。Quant Researcher 岗还会涉及 ML 模型问题。
Step 05 Superday
现场赴芝加哥(或 Zoom)进行,Day 1 下午报到 + 交流,Day 2 全天面试。涵盖数据分析任务、Trading Game(扑克/骰子博弈)、背靠背技术面与行为面试。DRW 强调 Day 1 的社交活动对最终决策同样重要,不建议缺席。
DRW / Codility 6 道真题分享
题目 1:游戏生命值计算
题目描述
想象一个电子游戏,玩家操控角色闯过多个关卡。角色的初始生命值为 initialHealth,生命值会随着关卡进程发生变化。
给定一个整数数组 deltas,表示每一关的生命值变化。第 i 关(从 0 开始计数)会让角色当前的生命值变化 deltas[i]。
规则:
- 当角色的生命值变为小于 0 时,会立即被设置为 0。
- 当角色的生命值变为大于 100 时,会立即被设置为 100。
你的任务是:计算并返回角色闯过所有关卡后的最终生命值。
解题思路
直接模拟即可:
- 初始化
currentHealth = initialHealth - 遍历
deltas数组,每次更新currentHealth += deltas[i] - 每次更新后,将
currentHealth限制在[0, 100]之间 - 遍历结束后返回
currentHealth
Java 实现
public class Solution {
public int solution(int initialHealth, int[] deltas) {
int current = initialHealth;
for (int delta : deltas) {
current += delta;
if (current < 0) current = 0;
if (current > 100) current = 100;
}
return current;
}
}
题目 2:匹配子数组模式
题目描述
给定一个整数数组 numbers 和一个表示比较模式的数组 pattern,找出 numbers 中有多少个子数组与给定的 pattern 匹配。
pattern 数组中只包含以下整数:
pattern[i] = 1:对应位置的数字比前一个数字大pattern[i] = 0:对应位置的数字与前一个数字相等pattern[i] = -1:对应位置的数字比前一个数字小
题目保证 numbers.length > pattern.length。
解题思路
暴力匹配即可(满足题目时间复杂度要求):
- 子数组的长度必须为
pattern.length + 1 - 遍历所有可能的起始位置
i,检查从i开始的子数组是否匹配pattern - 统计匹配的子数组数量
Java 实现
public class Solution {
public int solution(int[] numbers, int[] pattern) {
int count = 0;
int m = pattern.length;
int n = numbers.length;
for (int i = 0; i <= n - m; i++) {
boolean match = true;
for (int j = 0; j < m; j++) {
int curr = numbers[i + j + 1];
int prev = numbers[i + j];
if (pattern[j] == 1 && curr <= prev) match = false;
else if (pattern[j] == 0 && curr != prev) match = false;
else if (pattern[j] == -1 && curr >= prev) match = false;
}
if (match) count++;
}
return count;
}
}
题目 3:矩阵绘制字母 Y
题目描述
给定一个 n × n 的正方形矩阵(n 为奇数),矩阵中只包含数字 0、1、2。你可以将任意格子的数字改为 0、1 或 2。
目标是计算出在矩阵中画出字母 Y 所需的最少修改次数。
字母 Y 的定义:
- 构成
Y的所有数字都相等:左上到中心的对角线、右上到中心的对角线、从中心垂直向下的所有格子。 - 所有不构成
Y的格子数字都相等,且与构成Y的数字不同。
解题思路
枚举所有 6 种可能的颜色组合,计算每种组合需要修改的格子数,取最小值:
Y=0, 背景=1/Y=0, 背景=2Y=1, 背景=0/Y=1, 背景=2Y=2, 背景=0/Y=2, 背景=1
Java 实现
public class Solution {
public int solution(int[][] matrix) {
int n = matrix.length;
int center = n / 2;
boolean[][] isY = new boolean[n][n];
// 标记Y的位置
for (int i = 0; i < center; i++) {
isY[i][i] = true;
isY[i][n - 1 - i] = true;
}
for (int i = center; i < n; i++) {
isY[i][center] = true;
}
int[][] pairs = {{0,1}, {0,2}, {1,0}, {1,2}, {2,0}, {2,1}};
int minChanges = Integer.MAX_VALUE;
for (int[] pair : pairs) {
int yColor = pair[0];
int bgColor = pair[1];
int changes = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (isY[i][j]) {
if (matrix[i][j] != yColor) changes++;
} else {
if (matrix[i][j] != bgColor) changes++;
}
}
}
minChanges = Math.min(minChanges, changes);
}
return minChanges;
}
}
题目 4:数字翻转配对
题目描述
定义 flipDigits 函数:将整数的数字顺序反转,并去掉结果中的所有前导零。
例如:flipDigits(5070) = 705,flipDigits(800) = 8。
给定一个非负整数数组 arr,计算满足以下条件的数对 (i, j) 的数量:
i ≤ jarr[i] + flipDigits(arr[j]) = arr[j] + flipDigits(arr[i])
解题思路
对等式变形:arr[i] - flipDigits(arr[i]) = arr[j] - flipDigits(arr[j])
因此,我们只需要统计每个 (x - flipDigits(x)) 值出现的次数,再用组合数计算即可。
Java 实现
import java.util.*;
public class Solution {
public long solution(int[] arr) {
Map<Long, Long> countMap = new HashMap<>();
for (int x : arr) {
long key = x - flipDigits(x);
countMap.put(key, countMap.getOrDefault(key, 0L) + 1);
}
long total = 0;
for (long cnt : countMap.values()) {
total += cnt * (cnt + 1) / 2;
}
return total;
}
private long flipDigits(int x) {
long res = 0;
while (x > 0) {
res = res * 10 + (x % 10);
x /= 10;
}
return res;
}
}
题目 5:构造每个字母出现奇数次的字符串
题目描述
编写一个函数,给定整数 N,返回一个由 N 个小写字母(a-z)组成的字符串,要求每个出现过的字母的出现次数都是奇数。
解题思路
- 当
N是奇数时:直接用N个'a'即可('a'出现奇数次)。 - 当
N是偶数时:用(N-1)个'a'和 1 个'b'即可(两个字母都出现奇数次)。
Python 实现
def solution(N):
if N % 2 == 1:
return 'a' * N
else:
return 'a' * (N - 1) + 'b'
题目 6:最小交换次数使两个数字差最小
题目描述
给定两个数字字符串 S 和 T,可以交换对应位置上的数字,目标是让两个数的差的绝对值尽可能小,求最小交换次数。
解题思路
动态规划:逐位处理,维护三种状态的最小交换次数:
dp[0]:当前两个数前缀完全相等dp[1]:当前S前缀比T前缀大dp[2]:当前S前缀比T前缀小
Java 实现
public class Solution {
public int solution(String S, String T) {
int n = S.length();
int[] dp = new int[]{0, n + 1, n + 1};
for (int i = 0; i < n; i++) {
char s = S.charAt(i);
char t = T.charAt(i);
int[] next = new int[]{n + 1, n + 1, n + 1};
// 不交换
updateNext(next, dp, s, t, 0);
// 交换
updateNext(next, dp, t, s, 1);
dp = next;
}
return Math.min(dp[0], Math.min(dp[1], dp[2]));
}
private void updateNext(int[] next, int[] dp, char a, char b, int cost) {
int aVal = a - '0';
int bVal = b - '0';
if (dp[0] != Integer.MAX_VALUE) {
if (aVal > bVal) next[1] = Math.min(next[1], dp[0] + cost);
else if (aVal < bVal) next[2] = Math.min(next[2], dp[0] + cost);
else next[0] = Math.min(next[0], dp[0] + cost);
}
if (dp[1] != Integer.MAX_VALUE) next[1] = Math.min(next[1], dp[1] + cost);
if (dp[2] != Integer.MAX_VALUE) next[2] = Math.min(next[2], dp[2] + cost);
}
}
推荐备考资源
- Tradermath.org:心算与概率题库
- A Practical Guide to Quant Finance Interviews(绿皮书)
- LeetCode / Codility:Dev Track 必备
- Kaggle:Python 数据分析练习
- DRW 官方备考指南
额外推荐: 如果你想系统提升 OA 通过率和面试表现,强烈建议了解 Programhelp。他们的学长提供专业的 OA 实战辅助 、真题预测、代码优化以及高强度模拟面试,能帮你快速补齐弱点,显著提高备考效率。
有需要的同学可以直接联系 Programhelp 详谈,他们会根据你的情况给出针对性方案。