
Top Quant Interview Questions Asked by Tower Research Capital
In this article, we will cover some of the most representative and challenging quant interview questions from these firms. We’ll walk through detailed solutions, explanations of all key concepts, and provide example code and formulas where appropriate.
Quant Interview Questions from Tower Research Capital and Other Companies
1. Self-Attention Explained and C++ Implementation (Tower Research Capital)
What is Self-Attention?
Self-attention is a core mechanism in modern machine learning, especially in natural language processing models like Transformers. At its essence, self-attention allows a model to weigh the importance of different elements within a sequence when encoding a particular element of that sequence. It dynamically computes a weighted sum of all elements, where the weights (attention scores) are determined by learned similarity functions.
Key Concepts in Self-Attention
- Queries, Keys, and Values: Each input element is projected into three vectors: a query (Q), a key (K), and a value (V).
- Attention Scores: The attention score between two elements is typically computed as the dot product of their query and key vectors, often scaled and passed through a softmax to obtain a distribution.
- Weighted Sum: Each output is a weighted sum of all value vectors, where weights are the attention scores.
Mathematical Formulation
Given an input sequence of \( n \) elements represented as vectors:
- Let \( X = [x_1, x_2, ..., x_n] \), with \( x_i \in \mathbb{R}^d \).
- Define three projection matrices: \( W^Q, W^K, W^V \in \mathbb{R}^{d \times d_k} \).
The steps are:
- Compute queries, keys, and values:
\( Q = X W^Q \), \( K = X W^K \), \( V = X W^V \) - Compute attention scores:
\( \text{Attention}(Q, K, V) = \text{softmax}\left( \frac{QK^T}{\sqrt{d_k}} \right) V \)
Here, the softmax is applied row-wise, and the scaling factor \( \sqrt{d_k} \) stabilizes gradients.
C++ Implementation of Self-Attention
Below is a simple, self-contained C++ implementation of the self-attention mechanism for a single input sequence. For clarity, the code avoids external libraries beyond standard C++. In practice, you would use libraries like Eigen, Armadillo, or dedicated ML frameworks for efficiency and clarity.
// Simple self-attention implementation in C++
#include <iostream>
#include <vector>
#include <cmath>
#include <random>
using namespace std;
// Helper function: Dot product of two vectors
double dot(const vector<double>& a, const vector<double>& b) {
double result = 0.0;
for (size_t i = 0; i < a.size(); ++i)
result += a[i] * b[i];
return result;
}
// Helper function: Softmax
vector<double> softmax(const vector<double>& scores) {
vector<double> exp_scores(scores.size());
double max_score = *max_element(scores.begin(), scores.end());
double sum = 0.0;
for (size_t i = 0; i < scores.size(); ++i) {
exp_scores[i] = exp(scores[i] - max_score); // for numerical stability
sum += exp_scores[i];
}
for (size_t i = 0; i < exp_scores.size(); ++i)
exp_scores[i] /= sum;
return exp_scores;
}
// Multiply matrix (N x D) with matrix (D x Dk)
vector<vector<double>> matmul(const vector<vector<double>>& X, const vector<vector<double>>& W) {
size_t N = X.size(), D = X[0].size(), Dk = W[0].size();
vector<vector<double>> result(N, vector<double>(Dk, 0.0));
for (size_t i = 0; i < N; ++i)
for (size_t j = 0; j < Dk; ++j)
for (size_t k = 0; k < D; ++k)
result[i][j] += X[i][k] * W[k][j];
return result;
}
// Self-Attention function
vector<vector<double>> self_attention(
const vector<vector<double>>& X, // N x D
const vector<vector<double>>& WQ, // D x Dk
const vector<vector<double>>& WK, // D x Dk
const vector<vector<double>>& WV // D x Dk
) {
size_t N = X.size();
size_t Dk = WQ[0].size();
// Step 1: Linear projections
auto Q = matmul(X, WQ); // N x Dk
auto K = matmul(X, WK); // N x Dk
auto V = matmul(X, WV); // N x Dk
// Step 2: Compute attention scores and output
vector<vector<double>> output(N, vector<double>(Dk, 0.0));
for (size_t i = 0; i < N; ++i) {
// Compute attention scores for position i with all positions
vector<double> scores(N);
for (size_t j = 0; j < N; ++j)
scores[j] = dot(Q[i], K[j]) / sqrt((double)Dk);
vector<double> attn_weights = softmax(scores);
// Weighted sum of values
for (size_t j = 0; j < N; ++j)
for (size_t d = 0; d < Dk; ++d)
output[i][d] += attn_weights[j] * V[j][d];
}
return output;
}
int main() {
// Example: 3 tokens, embedding size 4, d_k = 4
vector<vector<double>> X = {{1, 0, 1, 0}, {0, 1, 1, 0}, {1, 1, 0, 1}};
vector<vector<double>> WQ = {{0.1, 0.2, 0.0, 0.1}, {0.0, 0.2, 0.1, 0.0}, {0.1, 0.0, 0.2, 0.1}, {0.1, 0.1, 0.0, 0.2}};
vector<vector<double>> WK = {{0.2, 0.0, 0.1, 0.1}, {0.1, 0.1, 0.0, 0.2}, {0.0, 0.2, 0.1, 0.0}, {0.1, 0.1, 0.2, 0.0}};
vector<vector<double>> WV = {{0.1, 0.0, 0.2, 0.1}, {0.0, 0.2, 0.1, 0.1}, {0.1, 0.1, 0.0, 0.2}, {0.2, 0.1, 0.1, 0.0}};
auto output = self_attention(X, WQ, WK, WV);
cout << "Self-attention output:" << endl;
for (const auto& vec : output) {
for (double v : vec)
cout << v << " ";
cout << endl;
}
return 0;
}
This implementation demonstrates the core calculations of self-attention. In real-world usage, you'll use optimized libraries and often work with batches, multi-head attention, and masking.
2. Classic Clock Angle and Overlap Problems (Virtu Financial)
Question 1: At 5:15, What’s the Angle Between the Clock Hands?
This is a popular brainteaser in quant interviews. Let’s break it down step by step.
Step 1: Compute the Position of Each Hand
- Minute hand:
- The minute hand moves 360 degrees in 60 minutes, so each minute corresponds to 6 degrees.
- At 15 minutes, the minute hand is at \( 15 \times 6 = 90 \) degrees from the 12 o'clock position.
- Hour hand:
- The hour hand moves 360 degrees in 12 hours, so each hour corresponds to 30 degrees.
- At 5 hours, it is at \( 5 \times 30 = 150 \) degrees.
- It also moves as the minutes pass. In 15 minutes, it moves an extra \( \frac{15}{60} \times 30 = 7.5 \) degrees.
- So, at 5:15, the hour hand is at \( 150 + 7.5 = 157.5 \) degrees.
Step 2: Compute the Angle Between the Hands
The angle between the hands is the absolute difference:
\( |157.5 - 90| = 67.5 \) degrees.
Final Answer
The angle between the clock hands at 5:15 is 67.5 degrees.
Question 2: When Will the Hour and Minute Hands Overlap Next?
To find when the hour and minute hands overlap, let’s generalize the problem:
General Formula for Overlap
Let’s denote:
- \( t \): minutes past 12:00
- The minute hand moves at 6 degrees per minute (\( 360^\circ / 60 \)), so after \( t \) minutes, it's at \( 6t \) degrees.
- The hour hand moves at 0.5 degrees per minute (\( 360^\circ / 12 / 60 \)), so after \( t \) minutes, it's at \( 0.5t \) degrees. Starting from \( H \) hours, the hour hand is at \( 30H + 0.5t \) degrees.
They overlap when their positions are equal (modulo 360):
\( 30H + 0.5t = 6t \)
\( 30H = 5.5t \)
\( t = \frac{30H}{5.5} \)
Apply to 5:15
At 5:15, \( H = 5 \), \( t = 75 \) minutes past 12:00. We want the next overlap after 5:15.
- The first overlap after 12:00 is at \( t_1 = \frac{30}{5.5} \approx 5.4545 \) minutes (i.e., 12:05:27).
- The nth overlap occurs at \( t_n = \frac{12 \times 60}{11} \times n \) minutes, for \( n = 1, 2, ..., 11 \).
To find the overlap after 5:15 (i.e., after 315 minutes from 12:00), we solve for \( n \):
\( t_n = \frac{12 \times 60}{11} \times n = \frac{720}{11} n \)
Set \( t_n > 315 \):
\( n > \frac{315 \times 11}{720} \approx 4.819 \)
So the next overlap is at \( n = 5 \).
\( t_5 = \frac{720}{11} \times 5 = \frac{3600}{11} \approx 327.2727 \) minutes
\( 327.2727 \) minutes past 12:00 is \( 5 \) hours, \( 27.2727 \) minutes, or 5:27:16.
Final Answer
The next time after 5:15 when the hour and minute hands overlap is at approximately 5:27:16.
3. Design a Local Stochastic Volatility Model (Bank of America)
Overview of Stochastic Volatility Models
In quantitative finance, stochastic volatility models are used to describe the random evolution of an asset's volatility, capturing phenomena like volatility clustering and the volatility smile. Local volatility models, on the other hand, make volatility a deterministic function of the asset’s price and time.
A local stochastic volatility model (LSV) combines both approaches: volatility is driven by both a stochastic process and a deterministic function of price and time. LSV models are widely used in structuring and pricing derivatives, especially in equity and FX markets.
Local Volatility Model: Dupire Framework
In the local volatility model (Dupire, 1994), the asset price \( S_t \) follows the SDE:
\[ dS_t = r S_t dt + \sigma_{loc}(S_t, t) S_t dW_t \]
where \( r \) is the risk-free rate, \( \sigma_{loc}(S_t, t) \) is the local volatility as a deterministic function of price and time, and \( W_t \) is a standard Brownian motion. The local volatility function \( \sigma_{loc}(S_t, t) \) can be extracted from market option prices using the Dupire formula.
Stochastic Volatility Model: Heston Framework
A classic stochastic volatility model is the Heston model, which assumes that the variance itself follows a mean-reverting stochastic process. The dynamics are:
\[ \begin{aligned} dS_t &= r S_t dt + \sqrt{v_t} S_t dW_t^S \\ dv_t &= \kappa (\theta - v_t) dt + \xi \sqrt{v_t} dW_t^v \end{aligned} \]
- \( v_t \) is the instantaneous variance.
- \( \kappa \) is the mean reversion speed.
- \( \theta \) is the long-term variance level.
- \( \xi \) is the volatility of volatility.
- \( W_t^S \) and \( W_t^v \) are Brownian motions with correlation \( \rho \).
Local Stochastic Volatility (LSV) Model: Combining Both
The LSV model merges these two approaches by introducing a local volatility multiplier to the stochastic volatility process. The SDE becomes:
\[ \begin{aligned} dS_t &= r S_t dt + \sigma_{loc}(S_t, t)\sqrt{v_t} S_t dW_t^S \\ dv_t &= \kappa (\theta - v_t) dt + \xi \sqrt{v_t} dW_t^v \end{aligned} \]
Here, \( \sigma_{loc}(S_t, t) \) is calibrated to market data (ensuring the model fits the observed implied volatility surface), and \( v_t \) introduces stochastic features like volatility clustering and fat tails.
Key Features and Implementation Steps
- Calibration of \( \sigma_{loc}(S_t, t) \):
- First, calibrate a local volatility surface using market option prices via the Dupire formula.
- This ensures the LSV model fits vanilla option prices exactly.
- Stochastic Volatility Dynamics:
- Choose parameters \( \kappa, \theta, \xi, \rho \) for the stochastic variance process \( v_t \), typically using time series estimation or advanced calibration techniques (e.g., particle filtering, maximum likelihood, or calibration to exotic options).
- Simulating the Model:
- Simulate correlated Brownian motions \( W_t^S \), \( W_t^v \).
- At each time step, compute \( \sigma_{loc}(S_t, t) \) from the local volatility surface and multiply by \( \sqrt{v_t} \).
- Advance \( S_t \) and \( v_t \) using Euler-Maruyama or more sophisticated schemes (e.g., Milstein, QE scheme for variance).
- Pricing and Risk Management:
- Use Monte Carlo simulation to price exotic derivatives, compute Greeks, and assess risk.
Mathematical Intuition
The LSV model gives the best of both worlds:
- Perfect fit to vanilla option prices (thanks to the local volatility component).
- Stochastic volatility features for realistic modeling of path-dependent and exotic payoffs.
- Capture of both implied volatility surface and real-world volatility clustering.
Sample Python Pseudocode for LSV Simulation
Although the original request is for a design rather than code, it’s helpful to see a pseudocode outline for simulation. For real implementations, use C++ for performance, but here’s a Python-like pseudocode for clarity:
import numpy as np
def simulate_lsv(S0, v0, r, T, N, dt, kappa, theta, xi, rho, sigma_loc):
# S0: initial price, v0: initial variance
# sigma_loc: function S, t -> local volatility
S = np.zeros(N+1)
v = np.zeros(N+1)
S[0], v[0] = S0, v0
for i in range(N):
t = i * dt
z1, z2 = np.random.normal(size=2)
dW1 = np.sqrt(dt)*z1
dW2 = rho*dW1 + np.sqrt(1 - rho**2)*np.sqrt(dt)*z2
# Stochastic variance - CIR process (ensure positivity)
v[i+1] = np.abs(v[i] + kappa*(theta - v[i])*dt + xi*np.sqrt(v[i])*dW2)
# Local stochastic volatility for price
S[i+1] = S[i] * np.exp((r - 0.5*sigma_loc(S[i], t)**2*v[i])*dt +
sigma_loc(S[i], t)*np.sqrt(v[i])*dW1)
return S, v
In production, use robust schemes and handle boundary conditions carefully.
Applications of the LSV Model in Quantitative Finance
- Exotic Option Pricing: Captures market smiles/skews and path-dependent volatility effects.
- Risk Management: More accurate VaR, CVaR, and stress scenarios for portfolios with options.
- Hedging: Better dynamic hedging ratios for both vanilla and exotic books.
Calibration Example: The Gyöngy Lemma
The Gyöngy Lemma ensures that a local volatility model can be matched to any marginal distribution of a stochastic volatility model. In practice, calibrating the LSV model means iteratively adjusting \( \sigma_{loc}(S, t) \) so that model prices match market prices for a wide range of strikes and maturities.
Summary Table: LSV Model vs. Alternatives
| Model | Volatility Type | Fits Implied Vol Surface? | Captures Stochastic Features? | Use Case |
|---|---|---|---|---|
| Black-Scholes | Constant | No | No | Benchmark, simple instruments |
| Local Volatility (Dupire) | Deterministic, function of (S, t) | Yes (for vanillas) | No | Vanilla options, smile fitting |
| Stochastic Volatility (Heston) | Random, mean-reverting | Partial | Yes | Exotics, volatility clustering |
| Local Stochastic Volatility (LSV) | Both deterministic & stochastic | Yes | Yes | Exotics, hybrids, real-world hedging |
Conclusion
Quantitative interviews at firms like Tower Research Capital, Virtu Financial, and Bank of America challenge candidates with a blend of mathematics, programming, and financial modeling. From implementing modern deep learning components like self-attention in C++, to solving classic brainteasers involving clock hands, and designing sophisticated local stochastic volatility models, the scope is broad and rigorous.
By understanding the mathematical concepts behind these questions, practicing their implementation, and being able to clearly communicate your reasoning, you can stand out in the quant interview process. Whether you are preparing for technical interviews or deepening your knowledge as a finance professional, mastery of these topics is essential in today’s quantitative landscape.
Further Reading and Resources
- Self-Attention and Transformers: "Attention Is All You Need" by Vaswani et al.
- Clock Problems: Numberphile’s video on clock hands.
- Local and Stochastic Volatility: "Stochastic Calculus for Finance II" by Steven Shreve; "The Volatility Surface" by Jim Gatheral.
- Monte Carlo Methods: "Monte Carlo Methods in Financial Engineering" by Paul Glasserman.
Preparing for quant interviews is a journey—deepen your understanding, practice implementation, and you will improve your chances of landing a role at top quantitative finance firms.