blog-cover-image

Top Quant Interview Questions on Probability, Volatility & Python

In this article, we'll solve and discuss in-depth some real interview questions covering probability distributions, implied volatility, Python programming, and portfolio risk analysis. Whether you're preparing for quant interviews or seeking to deepen your understanding, this guide will help you master key concepts and problem-solving techniques.

Quant Interview Questions from QRT, Five Rings & Citadel - Solutions and Explanations


1. What is Implied Volatility?

Understanding Volatility in Financial Markets

In financial markets, volatility refers to the degree of variation of a trading price series over time, typically measured by the standard deviation of returns. Volatility is a critical parameter in options pricing models, risk management, and quantitative trading strategies.

Definition of Implied Volatility

Implied volatility (IV) is the market's forecast of a likely movement in a security's price. It is derived from the market price of an option, using an option pricing model such as the Black-Scholes model. Unlike historical volatility, which is calculated from past price movements, implied volatility reflects the market's expectations of future volatility.

Mathematically, implied volatility is the value of volatility \( \sigma \) that, when plugged into the Black-Scholes pricing formula, yields the observed market price of the option. It is obtained by inverting the pricing formula:

$$ C_{mkt} = BS(S, K, T, r, \sigma_{IV}) $$

  • \( C_{mkt} \): Observed market price of the option
  • \( BS(\cdot) \): Black-Scholes formula
  • \( S \): Current price of the underlying asset
  • \( K \): Strike price
  • \( T \): Time to maturity
  • \( r \): Risk-free interest rate
  • \( \sigma_{IV} \): Implied volatility (unknown, to be solved for)

Why is Implied Volatility Important?

  • IV indicates how much the market expects the price of an asset to move in the future.
  • Traders use IV to gauge market sentiment, identify mispriced options, and construct volatility-based trading strategies.
  • Implied volatility is a key input in risk management and portfolio optimization.

How is Implied Volatility Computed?

There is no closed-form solution for IV in the Black-Scholes formula, so numerical methods such as the Newton-Raphson method or bisection search are typically used.


from scipy.stats import norm
import numpy as np

def black_scholes_call(S, K, T, r, sigma):
    d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)
    return S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)

def implied_volatility(C_mkt, S, K, T, r, tol=1e-6, max_iter=100):
    # Use Newton-Raphson method
    sigma = 0.2 # initial guess
    for i in range(max_iter):
        price = black_scholes_call(S, K, T, r, sigma)
        vega = S * norm.pdf((np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))) * np.sqrt(T)
        price_diff = price - C_mkt
        if abs(price_diff) < tol:
            return sigma
        sigma -= price_diff / vega
    return sigma

Summary: Implied volatility is a forward-looking measure of expected price fluctuation, obtained from observed option prices via models like Black-Scholes. It is a cornerstone concept in quantitative finance.


2. Probability Distribution of X + Y Where X and Y are IID Uniformly Distributed Random Variables (Five Rings)

Restating the Problem

Let \( X \) and \( Y \) be independent and identically distributed (iid) random variables, each uniformly distributed on the interval [0, 1]. What is the probability distribution of \( Z = X + Y \)?

Step 1: Understanding the Uniform Distribution

If a random variable \( X \sim U[0, 1] \), its probability density function (PDF) is:

$$ f_X(x) = \begin{cases} 1 & \text{if } 0 \leq x \leq 1 \\ 0 & \text{otherwise} \end{cases} $$

Step 2: The Sum of Two Independent Uniform Random Variables

The PDF of \( Z = X + Y \) is given by the convolution of the PDFs of \( X \) and \( Y \):

$$ f_Z(z) = \int_{-\infty}^{\infty} f_X(x) f_Y(z - x) dx $$

Since \( X \) and \( Y \) are both uniform on [0, 1], \( Z \) ranges from 0 to 2.

Step 3: Computing the Convolution

For \( 0 \leq z \leq 1 \):

- \( x \) can range from 0 to \( z \), and \( z-x \) is between 0 and 1. - So,

$$ f_Z(z) = \int_{0}^{z} 1 \cdot 1 \; dx = z $$

For \( 1 < z \leq 2 \):

- \( x \) can range from \( z-1 \) to 1, and \( z-x \) is between 0 and 1. - So,

$$ f_Z(z) = \int_{z-1}^{1} 1 \cdot 1 \; dx = 1 - (z-1) = 2 - z $$

Step 4: Piecewise PDF of Z

The PDF of \( Z = X + Y \), where both are \( U[0, 1] \), is:

$$ f_Z(z) = \begin{cases} z & 0 \leq z \leq 1 \\ 2 - z & 1 < z \leq 2 \\ 0 & \text{otherwise} \end{cases} $$

Cumulative Distribution Function (CDF)

For completeness, the CDF \( F_Z(z) \) is:

  • For \( 0 \leq z \leq 1 \):
    \( F_Z(z) = \int_0^z t dt = \frac{1}{2} z^2 \)
  • For \( 1 < z \leq 2 \):
    \( F_Z(z) = \frac{1}{2} (1)^2 + \int_1^z (2 - t) dt = \frac{1}{2} + [2t - \frac{1}{2} t^2]_{1}^{z} \)
    \( = \frac{1}{2} + (2z - \frac{1}{2}z^2 - (2 \times 1 - \frac{1}{2}\times 1^2)) \)
    \( = \frac{1}{2} + (2z - \frac{1}{2}z^2 - \frac{3}{2}) \)
    \( = 2z - \frac{1}{2}z^2 - 1 \)

 

Python Simulation


import numpy as np
import matplotlib.pyplot as plt

N = 100000
x = np.random.uniform(0, 1, N)
y = np.random.uniform(0, 1, N)
z = x + y

plt.hist(z, bins=100, density=True, alpha=0.6, color='g')
plt.title('PDF of Z = X + Y, X,Y ~ U[0,1]')
plt.xlabel('z')
plt.ylabel('Density')
plt.show()

Key Takeaways

  • The sum of two independent uniform [0,1] random variables follows a triangular distribution on [0,2].
  • The peak of the PDF is at z=1.
  • This result generalizes: the sum of n independent uniform [0,1] variables follows the Irwin-Hall distribution.

3. How to Make a Custom Python Class Hashable (QRT)

Hashability in Python

In Python, objects are hashable if they have a hash value that does not change over their lifetime, and they can be compared to other objects. Hashable objects can be used as keys in dictionaries and as elements in sets.

  • Primitive immutable types (ints, floats, strings, tuples of hashable types) are hashable by default.
  • Mutable objects or custom classes are NOT hashable unless you implement specific methods.

To make a custom class hashable, you must implement the __hash__ and __eq__ methods.

Steps to Make a Custom Class Hashable

  • Implement __eq__: Defines equality between instances.
  • Implement __hash__: Returns an integer hash value for the instance.
  • Ensure Immutability: Ideally, hashable objects should be immutable, as mutating them after insertion into a set/dictionary will break hash-based lookups.

Example: Hashable Class


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return isinstance(other, Point) and self.x == other.x and self.y == other.y

    def __hash__(self):
        return hash((self.x, self.y))

# Usage:
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(2, 3)

point_set = {p1, p2, p3} # p1 and p2 are considered equal, so only two unique points
print(len(point_set)) # Output: 2

Best Practices

  • Do not change the attributes used in __hash__ after object creation.
  • Use __slots__ or @dataclass(frozen=True) for immutability if using Python 3.7+.

Example with @dataclass(frozen=True)


from dataclasses import dataclass

@dataclass(frozen=True)
class Point:
    x: int
    y: int

# Now Point is immutable and hashable by default!

Summary

  • Implement __eq__ and __hash__ for custom classes.
  • Ensure that instances are immutable for consistent hash values.
  • Hashable classes are essential for using your objects as dictionary keys or set elements.

4. How to Estimate the Sharpe Ratio of a Portfolio Combining Different Signals (Citadel)

What is the Sharpe Ratio?

The Sharpe ratio is a measure of risk-adjusted return. It quantifies the excess return per unit of risk (volatility) of an investment or portfolio.

The Sharpe ratio is defined as:

$$ SR = \frac{E[R_p - R_f]}{\sigma_p} $$

  • \( R_p \): Portfolio return
  • \( R_f \): Risk-free rate
  • \( \sigma_p \): Standard deviation of portfolio returns

Usually, for short-term or signal evaluation, \( R_f \) is taken as zero or negligible.

Combining Multiple Signals in a Portfolio

Suppose you have \( n \) signals, each generating a return series. You want to combine them into a portfolio and estimate the Sharpe ratio.

Step 1: Signal Returns and Portfolio Weights

  • Let \( \mathbf{r} = [r_1, r_2, ..., r_n]^T \) be the vector of average returns from each signal.
  • Let \( \mathbf{w} = [w_1, w_2, ..., w_n]^T \) be the vector of portfolio weights assigned to each signal.
  • Let \( \Sigma \) be the \( n \times n \) sample covariance matrix of signal returns.

The portfolio return is:

$$ R_{portfolio} = \mathbf{w}^T \mathbf{r} $$

The portfolio variance is:

$$ \sigma_{portfolio}^2 = \mathbf{w}^T \Sigma \mathbf{w} $$

The Sharpe ratio is then:

$$ SR = \frac{\mathbf{w}^T \mathbf{r}}{\sqrt{\mathbf{w}^T \Sigma \mathbf{w}}} $$

Step 2: Estimating the Sharpe Ratio from Data

Step 3: Practical Python Implementation

Below is a Python example showing how to compute the Sharpe ratio for a portfolio combining multiple signals:


import numpy as np

# Suppose returns is a T x n numpy array: rows = time, columns = signals
returns = np.random.normal(size=(1000, 5))  # Example: 1000 days, 5 signals

# Estimate mean returns and covariance matrix
mean_returns = np.mean(returns, axis=0)      # Shape: (n,)
cov_matrix = np.cov(returns, rowvar=False)   # Shape: (n, n)

# Example: Use equal weights for each signal
n_signals = returns.shape[1]
weights = np.ones(n_signals) / n_signals     # Shape: (n,)

# Portfolio mean and standard deviation
portfolio_mean = np.dot(weights, mean_returns)
portfolio_std = np.sqrt(np.dot(weights, np.dot(cov_matrix, weights)))

# Sharpe ratio (risk-free rate assumed zero)
sharpe_ratio = portfolio_mean / portfolio_std

print(f"Portfolio Sharpe Ratio: {sharpe_ratio:.4f}")

Step 4: Optimizing Portfolio Weights for Best Sharpe Ratio

If you wish to maximize the Sharpe ratio, the optimal weight vector (without constraints) is proportional to the inverse covariance matrix times the mean returns:

$$ \mathbf{w}_{opt} \propto \Sigma^{-1} \mathbf{r} $$

In practice, you may want to normalize w_opt such that the sum of absolute weights equals 1, or restrict the weights to be non-negative (long-only portfolio).


# Unconstrained optimal weights (risk parity)
inv_cov = np.linalg.inv(cov_matrix)
opt_weights = np.dot(inv_cov, mean_returns)
opt_weights /= np.sum(np.abs(opt_weights))  # Normalize for comparability

# Optimal portfolio Sharpe ratio
opt_port_mean = np.dot(opt_weights, mean_returns)
opt_port_std = np.sqrt(np.dot(opt_weights, np.dot(cov_matrix, opt_weights)))
opt_sharpe = opt_port_mean / opt_port_std

print(f"Optimized Portfolio Sharpe Ratio: {opt_sharpe:.4f}")

Step 5: Interpretation and Cautions

Step 6: Sharpe Ratio for Online/Streaming Signals

When combining signals in an online or streaming setting, you can estimate the portfolio mean and variance incrementally using recursive formulas, or by maintaining rolling windows.

Sharpe Ratio Formula Summary

The general formula for the Sharpe ratio of a weighted portfolio combining n signals is:

$$ SR_{portfolio} = \frac{\sum_{i=1}^{n} w_i \mu_i}{\sqrt{\sum_{i=1}^{n}\sum_{j=1}^{n} w_i w_j \sigma_{ij}}} $$

Sharpe Ratio with Signal Correlation

Signals are rarely independent. The denominator (portfolio volatility) must account for the covariance (correlation) between signals. If signals are highly correlated, combining them adds less incremental Sharpe than combining uncorrelated signals.

If all signals have the same volatility and pairwise correlation \( \rho \):

$$ \sigma_{portfolio}^2 = \frac{1}{n^2} \left[ n \sigma^2 + n(n-1) \rho \sigma^2 \right] $$

This highlights the importance of diversifying across uncorrelated signals to maximize the Sharpe ratio.

Summary Table: Steps to Estimate Portfolio Sharpe Ratio

Step Description Python/Math
1 Collect signal return time series returns = np.array([...])
2 Compute mean returns np.mean(returns, axis=0)
3 Compute covariance matrix np.cov(returns, rowvar=False)
4 Select weights (equal or optimized) weights = ...
5 Plug into Sharpe ratio formula $$ SR = \frac{w^T r}{\sqrt{w^T \Sigma w}} $$

Key Points for Interviews


Conclusion

Quant interview questions from top firms like QRT, Five Rings, and Citadel demand a deep understanding of probability, statistics, programming, and portfolio theory, as well as the ability to apply these concepts under pressure. In this article, we have:

Mastering these types of questions will not only help you succeed in interviews at elite quant firms, but also prepare you for rigorous real-world quantitative research and trading roles.

Further Reading

    • Suppose you have historical returns for each signal as a time series (matrix of shape T × n, where T is the number of time periods and n is the number of signals): returns[t][i] is the return of signal i at time t.
    • Estimate the mean return vector r as the average return of each signal over time.
    • Estimate the covariance matrix Σ using the sample covariance of the signals’ return series.
    • Choose portfolio weights w (e.g., equal weights, or optimized weights).
    • Plug into the Sharpe ratio formula above.
    • The Sharpe ratio is only as reliable as the accuracy and stability of your mean return and covariance estimates.
    • Using too many signals or too little data can lead to estimation noise, overfitting, and unstable weights.
    • Consider using shrinkage estimators (e.g., Ledoit-Wolf) for the covariance matrix in high-dimensional problems.
    • Always backtest and stress-test your portfolio with out-of-sample data.
    • \( w_i \): Weight of signal \( i \)
    • \( \mu_i \): Mean return of signal \( i \)
    • \( \sigma_{ij} \): Covariance between signal \( i \) and \( j \)
    • Understand the difference between arithmetic and geometric mean returns.
    • Know how to handle signal correlation and portfolio covariance.
    • Be prepared to discuss overfitting, estimation error, and practical considerations in real trading environments.
    • Show awareness of advanced techniques (shrinkage covariance, Bayesian approaches, etc.) for robustness.
    • Explained implied volatility and its critical role in options pricing, providing both conceptual and practical perspectives.
    • Derived the distribution for the sum of two independent uniform random variables, demonstrating convolution and resulting in a triangular distribution.
    • Shown how to make a custom Python class hashable—crucial for using custom objects in sets and dictionaries.
    • Detailed the estimation of the Sharpe ratio for a portfolio combining multiple signals, including practical implementation tips and considerations for robust quant research.

Related Articles